From 6b9c9b91e324ffe867971b7b665f05108b240457 Mon Sep 17 00:00:00 2001 From: Leonid Bugaev Date: Sat, 11 Jul 2015 09:47:26 +0500 Subject: [PATCH] Improve performance of input-raw and improve handing of large payloads --- Makefile | 2 +- byteutils/byteutils.go | 46 ++--- byteutils/byteutils_test.go | 36 ++-- emitter.go | 2 +- http_client.go | 5 +- input_raw_test.go | 59 ++++++ proto/proto.go | 186 +++++++++---------- proto/proto_test.go | 178 +++++++++--------- raw_socket_listener/listener.go | 282 ++++++++++++++++++----------- raw_socket_listener/tcp_message.go | 119 ++++++------ raw_socket_listener/tcp_packet.go | 124 ++++++------- settings.go | 2 +- test_input.go | 6 +- 13 files changed, 586 insertions(+), 461 deletions(-) diff --git a/Makefile b/Makefile index d8bc799..40fff87 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ dcover: go tool cover -html=coverage.out dfmt: - docker run -v `pwd`:$(SOURCE_PATH) -t -i gor go fmt + docker run -v `pwd`:$(SOURCE_PATH) -t -i gor go fmt ./... dvet: docker run -v `pwd`:$(SOURCE_PATH) -t -i gor go vet diff --git a/byteutils/byteutils.go b/byteutils/byteutils.go index 33ea49e..c65bbdb 100644 --- a/byteutils/byteutils.go +++ b/byteutils/byteutils.go @@ -1,37 +1,37 @@ package byteutils func Cut(a []byte, from, to int) []byte { - copy(a[from:], a[to:]) - a = a[:len(a)-to+from] + copy(a[from:], a[to:]) + a = a[:len(a)-to+from] - return a + return a } func Insert(a []byte, i int, b []byte) []byte { - a = append(a, make([]byte, len(b))...) - copy(a[i+len(b):], a[i:]) - copy(a[i:i+len(b)], b) + a = append(a, make([]byte, len(b))...) + copy(a[i+len(b):], a[i:]) + copy(a[i:i+len(b)], b) - return a + return a } // Unlike bytes.Replace it allows you to specify range func Replace(a []byte, from, to int, new []byte) []byte { - lenDiff := len(new) - (to - from) + lenDiff := len(new) - (to - from) - if lenDiff > 0 { - // Extend if new segment bigger - a = append(a, make([]byte, lenDiff)...) - copy(a[to+lenDiff:], a[to:]) - copy(a[from:from+len(new)], new) + if lenDiff > 0 { + // Extend if new segment bigger + a = append(a, make([]byte, lenDiff)...) + copy(a[to+lenDiff:], a[to:]) + copy(a[from:from+len(new)], new) - return a - } else if lenDiff < 0 { - copy(a[from:], new) - copy(a[from+len(new):],a[to:]) - return a[:len(a) + lenDiff] - } else { // same size - copy(a[from:], new) - return a - } -} \ No newline at end of file + return a + } else if lenDiff < 0 { + copy(a[from:], new) + copy(a[from+len(new):], a[to:]) + return a[:len(a)+lenDiff] + } else { // same size + copy(a[from:], new) + return a + } +} diff --git a/byteutils/byteutils_test.go b/byteutils/byteutils_test.go index 5b7bb04..8d242f0 100644 --- a/byteutils/byteutils_test.go +++ b/byteutils/byteutils_test.go @@ -1,32 +1,32 @@ package byteutils import ( - "testing" - "bytes" + "bytes" + "testing" ) func TestCut(t *testing.T) { - if !bytes.Equal(Cut([]byte("123456"), 2, 4), []byte("1256")) { - t.Error("Should properly cut") - } + if !bytes.Equal(Cut([]byte("123456"), 2, 4), []byte("1256")) { + t.Error("Should properly cut") + } } func TestInsert(t *testing.T) { - if !bytes.Equal(Insert([]byte("123456"), 2, []byte("abcd")), []byte("12abcd3456")) { - t.Error("Should insert into middle of slice") - } + if !bytes.Equal(Insert([]byte("123456"), 2, []byte("abcd")), []byte("12abcd3456")) { + t.Error("Should insert into middle of slice") + } } func TestReplace(t *testing.T) { - if !bytes.Equal(Replace([]byte("123456"), 2, 4, []byte("ab")), []byte("12ab56")) { - t.Error("Should replace when same length") - } + if !bytes.Equal(Replace([]byte("123456"), 2, 4, []byte("ab")), []byte("12ab56")) { + t.Error("Should replace when same length") + } - if !bytes.Equal(Replace([]byte("123456"), 2, 4, []byte("abcd")), []byte("12abcd56")) { - t.Error("Should replace when replacement length bigger") - } + if !bytes.Equal(Replace([]byte("123456"), 2, 4, []byte("abcd")), []byte("12abcd56")) { + t.Error("Should replace when replacement length bigger") + } - if !bytes.Equal(Replace([]byte("123456"), 2, 5, []byte("ab")), []byte("12ab6")) { - t.Error("Should replace when replacement length bigger") - } -} \ No newline at end of file + if !bytes.Equal(Replace([]byte("123456"), 2, 5, []byte("ab")), []byte("12ab6")) { + t.Error("Should replace when replacement length bigger") + } +} diff --git a/emitter.go b/emitter.go index a2373f5..2181331 100644 --- a/emitter.go +++ b/emitter.go @@ -40,7 +40,7 @@ func CopyMulty(src io.Reader, writers ...io.Writer) (err error) { } if Settings.debug { - Debug("[EMITTER] Sending paylod:", string(payload)) + Debug("[EMITTER] Sending payload, size:", len(payload), "First 500 bytes:", string(payload[0:500])) } if Settings.splitOutput { diff --git a/http_client.go b/http_client.go index fb0ec74..0743f04 100644 --- a/http_client.go +++ b/http_client.go @@ -4,12 +4,12 @@ import ( "crypto/tls" "github.com/buger/gor/proto" "io" + "log" "net" "net/url" + "runtime/debug" "strings" "time" - "runtime/debug" - "log" ) var defaultPorts = map[string]string{ @@ -103,7 +103,6 @@ func (c *HTTPClient) Send(data []byte) (response []byte, err error) { } }() - if c.conn == nil || !c.isAlive() { Debug("[HTTPClient] Connecting:", c.baseURL) if err = c.Connect(); err != nil { diff --git a/input_raw_test.go b/input_raw_test.go index 477eff8..9b45b95 100644 --- a/input_raw_test.go +++ b/input_raw_test.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "log" "net/http" + "net/http/httptest" "net/http/httputil" "os/exec" "strings" @@ -150,3 +151,61 @@ func TestInputRAWChunkedEncoding(t *testing.T) { close(quit) } + +func TestInputRAWLargePayload(t *testing.T) { + wg := new(sync.WaitGroup) + quit := make(chan int) + + // Generate 100kb file + dd := exec.Command("dd", "if=/dev/urandom", "of=/tmp/large", "bs=1KB", "count=100") + err := dd.Run() + if err != nil { + log.Fatal("dd error:", err) + } + + // Origing and Replay server initialization + origin := startHTTP(func(req *http.Request) { + defer req.Body.Close() + body, _ := ioutil.ReadAll(req.Body) + + if len(body) != 100*1000 { + t.Error("File size should be 1mb:", len(body)) + } + + wg.Done() + }) + origin_address := strings.Replace(origin.Addr().String(), "[::]", "127.0.0.1", -1) + + input := NewRAWInput(origin_address) + + replay := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + req.Body = http.MaxBytesReader(w, req.Body, 1*1024*1024) + buf := make([]byte, 1*1024*1024) + n, _ := req.Body.Read(buf) + body := buf[0:n] + + if len(body) != 100*1000 { + t.Error("File size should be 100000 bytes:", len(body)) + } + + wg.Done() + })) + defer replay.Close() + + http_output := NewHTTPOutput(replay.URL, &HTTPOutputConfig{Debug: false}) + + Plugins.Inputs = []io.Reader{input} + Plugins.Outputs = []io.Writer{http_output} + + go Start(quit) + + wg.Add(2) + curl := exec.Command("curl", "http://"+origin_address, "--header", "Transfer-Encoding: chunked", "--data-binary", "@/tmp/large") + err = curl.Run() + if err != nil { + log.Fatal("curl error:", err) + } + + wg.Wait() + close(quit) +} diff --git a/proto/proto.go b/proto/proto.go index 626d289..ee4a1a0 100644 --- a/proto/proto.go +++ b/proto/proto.go @@ -2,9 +2,9 @@ package proto import ( - "bytes" - "github.com/buger/gor/byteutils" - _ "log" + "bytes" + "github.com/buger/gor/byteutils" + _ "log" ) var CLRF = []byte("\r\n") @@ -13,154 +13,154 @@ var HEADER_DELIM = []byte(": ") // Headers should end with empty line func MIMEHeadersEndPos(payload []byte) int { - return bytes.Index(payload, EMPTY_LINE) + return bytes.Index(payload, EMPTY_LINE) } func MIMEHeadersStartPos(payload []byte) int { - return bytes.Index(payload, CLRF) + 2 // Find first line end + return bytes.Index(payload, CLRF) + 2 // Find first line end } // Find header value or return error // Do not support multi-line headers func Header(payload []byte, name []byte) (value []byte, headerStart, valueStart, headerEnd int) { - headerStart = bytes.Index(payload, name) + headerStart = bytes.Index(payload, name) - if headerStart == -1 { - return - } + if headerStart == -1 { + return + } - valueStart = headerStart + len(name) + 1 // Skip ":" after header name - if payload[valueStart] == ' ' { // Ignore empty space after ':' - valueStart += 1 - } - headerEnd = valueStart + bytes.IndexByte(payload[valueStart:], '\r') - value = payload[valueStart:headerEnd] + valueStart = headerStart + len(name) + 1 // Skip ":" after header name + if payload[valueStart] == ' ' { // Ignore empty space after ':' + valueStart += 1 + } + headerEnd = valueStart + bytes.IndexByte(payload[valueStart:], '\r') + value = payload[valueStart:headerEnd] - return + return } func GetHeader(payload []byte, name string) []byte { - val, _, _, _ := Header(payload, []byte(name)) + val, _, _, _ := Header(payload, []byte(name)) - return val + return val } func SetHeader(payload, name, value []byte) []byte { - _, hs, vs, he := Header(payload, name) + _, hs, vs, he := Header(payload, name) - // If header found - if hs != -1 { - return byteutils.Replace(payload, vs, he, value) - } else { - return AddHeader(payload, name, value) - } + // If header found + if hs != -1 { + return byteutils.Replace(payload, vs, he, value) + } else { + return AddHeader(payload, name, value) + } } func AddHeader(payload, name, value []byte) []byte { - header := make([]byte, len(name) + 2 + len(value) + 2) - copy(header[0:], name) - copy(header[len(name):], HEADER_DELIM) - copy(header[len(name)+2:], value) - copy(header[len(header)-2:], CLRF) + header := make([]byte, len(name)+2+len(value)+2) + copy(header[0:], name) + copy(header[len(name):], HEADER_DELIM) + copy(header[len(name)+2:], value) + copy(header[len(header)-2:], CLRF) - mimeStart := MIMEHeadersStartPos(payload) + mimeStart := MIMEHeadersStartPos(payload) - return byteutils.Insert(payload, mimeStart, header) + return byteutils.Insert(payload, mimeStart, header) } func Path(payload []byte) []byte { - start := bytes.IndexByte(payload, ' ') - start += 1 + start := bytes.IndexByte(payload, ' ') + start += 1 - end := bytes.IndexByte(payload[start:], ' ') + end := bytes.IndexByte(payload[start:], ' ') - return payload[start:start+end] + return payload[start : start+end] } func SetPath(payload, path []byte) []byte { - start := bytes.IndexByte(payload, ' ') - start += 1 + start := bytes.IndexByte(payload, ' ') + start += 1 - end := bytes.IndexByte(payload[start:], ' ') + end := bytes.IndexByte(payload[start:], ' ') - return byteutils.Replace(payload, start, start+end, path) + return byteutils.Replace(payload, start, start+end, path) } func PathParam(payload, name []byte) (value []byte, valueStart, valueEnd int) { - path := Path(payload) + path := Path(payload) - if paramStart := bytes.Index(path, append(name, '=')); paramStart != -1 { - valueStart := paramStart + len(name) + 1 - paramEnd := bytes.IndexByte(path[valueStart:], '&') - if paramEnd == -1 { // It is final param - paramEnd = len(path) - } else { - paramEnd += valueStart - } + if paramStart := bytes.Index(path, append(name, '=')); paramStart != -1 { + valueStart := paramStart + len(name) + 1 + paramEnd := bytes.IndexByte(path[valueStart:], '&') + if paramEnd == -1 { // It is final param + paramEnd = len(path) + } else { + paramEnd += valueStart + } - return path[valueStart:paramEnd], valueStart, paramEnd - } else { - return []byte(""), -1, -1 - } + return path[valueStart:paramEnd], valueStart, paramEnd + } else { + return []byte(""), -1, -1 + } } func SetPathParam(payload, name, value []byte) []byte { - path := Path(payload) - _, vs, ve := PathParam(payload, name) + path := Path(payload) + _, vs, ve := PathParam(payload, name) - if vs != -1 { - newPath := make([]byte, len(path)) - copy(newPath, path) - newPath = byteutils.Replace(newPath, vs, ve, value) + if vs != -1 { + newPath := make([]byte, len(path)) + copy(newPath, path) + newPath = byteutils.Replace(newPath, vs, ve, value) - return SetPath(payload, newPath) - } else { // if param not found append to end of url - // Adding 2 because of '?' or '&' at start, and '=' in middle - newParam := make([]byte, len(name) + len(value) + 2) + return SetPath(payload, newPath) + } else { // if param not found append to end of url + // Adding 2 because of '?' or '&' at start, and '=' in middle + newParam := make([]byte, len(name)+len(value)+2) - if bytes.IndexByte(path, '?') == -1 { - newParam[0] = '?' - } else { - newParam[0] = '&' - } + if bytes.IndexByte(path, '?') == -1 { + newParam[0] = '?' + } else { + newParam[0] = '&' + } - copy(newParam[1:], name) - newParam[1+len(name)] = '=' - copy(newParam[2+len(name):], value) + copy(newParam[1:], name) + newParam[1+len(name)] = '=' + copy(newParam[2+len(name):], value) - newPath := make([]byte, len(path) + len(newParam)) - copy(newPath, path) - copy(newPath[len(path):], newParam) + newPath := make([]byte, len(path)+len(newParam)) + copy(newPath, path) + copy(newPath[len(path):], newParam) - return SetPath(payload, newPath) - } + return SetPath(payload, newPath) + } } func SetHost(payload, url, host []byte) []byte { - // If this is HTTP 1.0 traffic or proxy traffic it may include host right into path variable, so instead of setting Host header we rewrite Path - // Fix for https://github.com/buger/gor/issues/156 - if path := Path(payload); bytes.HasPrefix(path, []byte("http")) { - hostStart := bytes.IndexByte(path, ':') // : position "https?:" - hostStart += 3 // Skip 1 ':' and 2 '\' - hostEnd := hostStart + bytes.IndexByte(path[hostStart:], '/') + // If this is HTTP 1.0 traffic or proxy traffic it may include host right into path variable, so instead of setting Host header we rewrite Path + // Fix for https://github.com/buger/gor/issues/156 + if path := Path(payload); bytes.HasPrefix(path, []byte("http")) { + hostStart := bytes.IndexByte(path, ':') // : position "https?:" + hostStart += 3 // Skip 1 ':' and 2 '\' + hostEnd := hostStart + bytes.IndexByte(path[hostStart:], '/') - newPath := make([]byte, len(path)) - copy(newPath, path) - newPath = byteutils.Replace(newPath, 0, hostEnd, url) + newPath := make([]byte, len(path)) + copy(newPath, path) + newPath = byteutils.Replace(newPath, 0, hostEnd, url) - return SetPath(payload, newPath) - } else { - return SetHeader(payload, []byte("Host"), host) - } + return SetPath(payload, newPath) + } else { + return SetHeader(payload, []byte("Host"), host) + } } func Method(payload []byte) []byte { - end := bytes.IndexByte(payload, ' ') + end := bytes.IndexByte(payload, ' ') - return payload[:end] + return payload[:end] } // Status in response have same position as Path in request func Status(payload []byte) []byte { - return Path(payload) -} \ No newline at end of file + return Path(payload) +} diff --git a/proto/proto_test.go b/proto/proto_test.go index 080f023..f232ee3 100644 --- a/proto/proto_test.go +++ b/proto/proto_test.go @@ -1,152 +1,146 @@ package proto import ( - "testing" - "bytes" + "bytes" + "testing" ) func TestHeader(t *testing.T) { - var payload, val []byte - var headerStart int + var payload, val []byte + var headerStart int - payload = []byte("POST /post HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") + payload = []byte("POST /post HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") - if val, _, _, _ = Header(payload, []byte("Content-Length")); !bytes.Equal(val, []byte("7")) { - t.Error("Should find header value") - } + if val, _, _, _ = Header(payload, []byte("Content-Length")); !bytes.Equal(val, []byte("7")) { + t.Error("Should find header value") + } - payload = []byte("POST /post HTTP/1.1\r\nContent-Length:7\r\nHost: www.w3.org\r\n\r\na=1&b=2") + payload = []byte("POST /post HTTP/1.1\r\nContent-Length:7\r\nHost: www.w3.org\r\n\r\na=1&b=2") - if val, _, _, _ = Header(payload, []byte("Content-Length")); !bytes.Equal(val, []byte("7")) { - t.Error("Should find header value without space after :") - } + if val, _, _, _ = Header(payload, []byte("Content-Length")); !bytes.Equal(val, []byte("7")) { + t.Error("Should find header value without space after :") + } - if _, headerStart, _, _ = Header(payload, []byte("Not-Found")); headerStart != -1 { - t.Error("Should not found header") - } + if _, headerStart, _, _ = Header(payload, []byte("Not-Found")); headerStart != -1 { + t.Error("Should not found header") + } } func TestMIMEHeadersEndPos(t *testing.T) { - head := []byte("POST /post HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org") - payload := []byte("POST /post HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") + head := []byte("POST /post HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org") + payload := []byte("POST /post HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") - end := MIMEHeadersEndPos(payload) + end := MIMEHeadersEndPos(payload) - if !bytes.Equal(payload[:end], head) { - t.Error("Wrong headers end position:", end) - } + if !bytes.Equal(payload[:end], head) { + t.Error("Wrong headers end position:", end) + } } func TestMIMEHeadersStartPos(t *testing.T) { - headers := []byte("Content-Length: 7\r\nHost: www.w3.org") - payload := []byte("POST /post HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") + headers := []byte("Content-Length: 7\r\nHost: www.w3.org") + payload := []byte("POST /post HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") - start := MIMEHeadersStartPos(payload) - end := MIMEHeadersEndPos(payload) + start := MIMEHeadersStartPos(payload) + end := MIMEHeadersEndPos(payload) - if !bytes.Equal(payload[start:end], headers) { - t.Error("Wrong headers end position:", start, end) - } + if !bytes.Equal(payload[start:end], headers) { + t.Error("Wrong headers end position:", start, end) + } } func TestSetHeader(t *testing.T) { - var payload, payload_after []byte + var payload, payload_after []byte - payload = []byte("POST /post HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") - payload_after = []byte("POST /post HTTP/1.1\r\nContent-Length: 14\r\nHost: www.w3.org\r\n\r\na=1&b=2") + payload = []byte("POST /post HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") + payload_after = []byte("POST /post HTTP/1.1\r\nContent-Length: 14\r\nHost: www.w3.org\r\n\r\na=1&b=2") - if payload = SetHeader(payload, []byte("Content-Length"), []byte("14")); !bytes.Equal(payload, payload_after) { - t.Error("Should update header if it exists", string(payload)) - } + if payload = SetHeader(payload, []byte("Content-Length"), []byte("14")); !bytes.Equal(payload, payload_after) { + t.Error("Should update header if it exists", string(payload)) + } + payload = []byte("POST /post HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") + payload_after = []byte("POST /post HTTP/1.1\r\nUser-Agent: Gor\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") - payload = []byte("POST /post HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") - payload_after = []byte("POST /post HTTP/1.1\r\nUser-Agent: Gor\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") - - if payload = SetHeader(payload, []byte("User-Agent"), []byte("Gor")); !bytes.Equal(payload, payload_after) { - t.Error("Should add header if not found", string(payload)) - } + if payload = SetHeader(payload, []byte("User-Agent"), []byte("Gor")); !bytes.Equal(payload, payload_after) { + t.Error("Should add header if not found", string(payload)) + } } func TestPath(t *testing.T) { - var path, payload []byte + var path, payload []byte - payload = []byte("POST /post HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") + payload = []byte("POST /post HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") - if path = Path(payload); !bytes.Equal(path, []byte("/post")) { - t.Error("Should find path", string(path)) - } + if path = Path(payload); !bytes.Equal(path, []byte("/post")) { + t.Error("Should find path", string(path)) + } } func TestSetPath(t *testing.T) { - var payload, payload_after []byte + var payload, payload_after []byte - payload = []byte("POST /post HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") - payload_after = []byte("POST /new_path HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") + payload = []byte("POST /post HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") + payload_after = []byte("POST /new_path HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") - if payload = SetPath(payload, []byte("/new_path")); !bytes.Equal(payload, payload_after) { - t.Error("Should replace path", string(payload)) - } + if payload = SetPath(payload, []byte("/new_path")); !bytes.Equal(payload, payload_after) { + t.Error("Should replace path", string(payload)) + } } func TestPathParam(t *testing.T) { - var payload []byte + var payload []byte - payload = []byte("POST /post?param=test&user_id=1 HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") + payload = []byte("POST /post?param=test&user_id=1 HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") - if val, _, _ := PathParam(payload, []byte("param")); !bytes.Equal(val, []byte("test")) { - t.Error("Should detect attribute", string(val)) - } + if val, _, _ := PathParam(payload, []byte("param")); !bytes.Equal(val, []byte("test")) { + t.Error("Should detect attribute", string(val)) + } - if val, _, _ := PathParam(payload, []byte("user_id")); !bytes.Equal(val, []byte("1")) { - t.Error("Should detect attribute", string(val)) - } + if val, _, _ := PathParam(payload, []byte("user_id")); !bytes.Equal(val, []byte("1")) { + t.Error("Should detect attribute", string(val)) + } } - func TestSetPathParam(t *testing.T) { - var payload, payload_after []byte + var payload, payload_after []byte - payload = []byte("POST /post?param=test&user_id=1 HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") - payload_after = []byte("POST /post?param=new&user_id=1 HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") + payload = []byte("POST /post?param=test&user_id=1 HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") + payload_after = []byte("POST /post?param=new&user_id=1 HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") - if payload = SetPathParam(payload, []byte("param"), []byte("new")); !bytes.Equal(payload, payload_after) { - t.Error("Should replace existing value", string(payload)) - } + if payload = SetPathParam(payload, []byte("param"), []byte("new")); !bytes.Equal(payload, payload_after) { + t.Error("Should replace existing value", string(payload)) + } - payload = []byte("POST /post?param=test&user_id=1 HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") - payload_after = []byte("POST /post?param=test&user_id=2 HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") + payload = []byte("POST /post?param=test&user_id=1 HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") + payload_after = []byte("POST /post?param=test&user_id=2 HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") - if payload = SetPathParam(payload, []byte("user_id"), []byte("2")); !bytes.Equal(payload, payload_after) { - t.Error("Should replace existing value", string(payload)) - } + if payload = SetPathParam(payload, []byte("user_id"), []byte("2")); !bytes.Equal(payload, payload_after) { + t.Error("Should replace existing value", string(payload)) + } + payload = []byte("POST /post HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") + payload_after = []byte("POST /post?param=test HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") - payload = []byte("POST /post HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") - payload_after = []byte("POST /post?param=test HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") + if payload = SetPathParam(payload, []byte("param"), []byte("test")); !bytes.Equal(payload, payload_after) { + t.Error("Should set param if url have no params", string(payload)) + } - if payload = SetPathParam(payload, []byte("param"), []byte("test")); !bytes.Equal(payload, payload_after) { - t.Error("Should set param if url have no params", string(payload)) - } + payload = []byte("POST /post?param=test HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") + payload_after = []byte("POST /post?param=test&user_id=1 HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") - - payload = []byte("POST /post?param=test HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") - payload_after = []byte("POST /post?param=test&user_id=1 HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") - - if payload = SetPathParam(payload, []byte("user_id"), []byte("1")); !bytes.Equal(payload, payload_after) { - t.Error("Should set param at the end if url params", string(payload)) - } + if payload = SetPathParam(payload, []byte("user_id"), []byte("1")); !bytes.Equal(payload, payload_after) { + t.Error("Should set param at the end if url params", string(payload)) + } } - - func TestSetHostHTTP10(t *testing.T) { - var payload, payload_after []byte + var payload, payload_after []byte - payload = []byte("POST http://example.com/post HTTP/1.0\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") - payload_after = []byte("POST http://new.com/post HTTP/1.0\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") + payload = []byte("POST http://example.com/post HTTP/1.0\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") + payload_after = []byte("POST http://new.com/post HTTP/1.0\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") - if payload = SetHost(payload, []byte("http://new.com"), []byte("new.com")); !bytes.Equal(payload, payload_after) { - t.Error("Should replace host", string(payload)) - } -} \ No newline at end of file + if payload = SetHost(payload, []byte("http://new.com"), []byte("new.com")); !bytes.Equal(payload, payload_after) { + t.Error("Should replace host", string(payload)) + } +} diff --git a/raw_socket_listener/listener.go b/raw_socket_listener/listener.go index b5f7150..ef8f950 100644 --- a/raw_socket_listener/listener.go +++ b/raw_socket_listener/listener.go @@ -1,11 +1,13 @@ package raw_socket import ( - "encoding/binary" - "log" - "net" - "strconv" - "bytes" + "bytes" + "encoding/binary" + "log" + "net" + "os" + "strconv" + "syscall" ) // Capture traffic from socket using RAW_SOCKET's @@ -16,113 +18,183 @@ import ( // Since we can't use default TCP libraries RAWTCPLitener implements own TCP layer // TCP packets is parsed using tcp_packet.go, and flow control is managed by tcp_message.go type Listener struct { - messages map[string]*TCPMessage // buffer of TCPMessages waiting to be send + messages map[string]*TCPMessage // buffer of TCPMessages waiting to be send - // Expect: 100-continue request is send in 2 tcp messages - // We store ACK aliases to merge this packets together - ack_aliases map[uint32]uint32 - seq_with_data map[uint32]uint32 + // Expect: 100-continue request is send in 2 tcp messages + // We store ACK aliases to merge this packets together + ack_aliases map[uint32]uint32 + seq_with_data map[uint32]uint32 - c_packets chan *TCPPacket - c_messages chan *TCPMessage // Messages ready to be send to client + c_packets chan *TCPPacket + c_messages chan *TCPMessage // Messages ready to be send to client - c_del_message chan *TCPMessage // Used for notifications about completed or expired messages + c_del_message chan *TCPMessage // Used for notifications about completed or expired messages - addr string // IP to listen - port int // Port to listen + addr string // IP to listen + port int // Port to listen } // RAWTCPListen creates a listener to capture traffic from RAW_SOCKET func NewListener(addr string, port string) (rawListener *Listener) { - rawListener = &Listener{} + rawListener = &Listener{} - rawListener.c_packets = make(chan *TCPPacket, 10000) - rawListener.c_messages = make(chan *TCPMessage, 10000) - rawListener.c_del_message = make(chan *TCPMessage, 10000) + rawListener.c_packets = make(chan *TCPPacket, 10000) + rawListener.c_messages = make(chan *TCPMessage, 10000) + rawListener.c_del_message = make(chan *TCPMessage, 10000) - rawListener.messages = make(map[string]*TCPMessage) - rawListener.ack_aliases = make(map[uint32]uint32) - rawListener.seq_with_data = make(map[uint32]uint32) + rawListener.messages = make(map[string]*TCPMessage) + rawListener.ack_aliases = make(map[uint32]uint32) + rawListener.seq_with_data = make(map[uint32]uint32) - rawListener.addr = addr - rawListener.port, _ = strconv.Atoi(port) + rawListener.addr = addr + rawListener.port, _ = strconv.Atoi(port) - go rawListener.listen() - go rawListener.readRAWSocket() + go rawListener.listen() + go rawListener.readRAWSocket() - return + return } func (t *Listener) listen() { - for { - select { - // If message ready for deletion it means that its also complete or expired by timeout - case message := <-t.c_del_message: - t.c_messages <- message - delete(t.ack_aliases, message.packets[0].Ack) - delete(t.messages, message.ID) + for { + select { + // If message ready for deletion it means that its also complete or expired by timeout + case message := <-t.c_del_message: + t.c_messages <- message + delete(t.ack_aliases, message.Ack) + delete(t.messages, message.ID) - // We need to use channels to process each packet to avoid data races - case packet := <-t.c_packets: - t.processTCPPacket(packet) - } - } + // We need to use channels to process each packet to avoid data races + case packet := <-t.c_packets: + t.processTCPPacket(packet) + } + } +} + +// Taken from http://golang.org/src/net/sock_cloexec.go?h=sysSocket#L16 +func sysSocket(family, sotype, proto int) (int, error) { + s, err := syscall.Socket(family, sotype|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, proto) + // On Linux the SOCK_NONBLOCK and SOCK_CLOEXEC flags were + // introduced in 2.6.27 kernel and on FreeBSD both flags were + // introduced in 10 kernel. If we get an EINVAL error on Linux + // or EPROTONOSUPPORT error on FreeBSD, fall back to using + // socket without them. + if err == nil || (err != syscall.EPROTONOSUPPORT && err != syscall.EINVAL) { + return s, err + } + + // See ../syscall/exec_unix.go for description of ForkLock. + syscall.ForkLock.RLock() + s, err = syscall.Socket(family, sotype, proto) + if err == nil { + syscall.CloseOnExec(s) + } + syscall.ForkLock.RUnlock() + if err != nil { + return -1, err + } + if err = syscall.SetNonblock(s, true); err != nil { + syscall.Close(s) + return -1, err + } + return s, nil +} + +func ipToSockaddr(ip net.IP) (syscall.Sockaddr, error) { + if len(ip) == 0 { + ip = net.IPv4zero + } + if ip = ip.To4(); ip == nil { + return nil, net.InvalidAddrError("non-IPv4 address") + } + + sa := new(syscall.SockaddrInet4) + for i := 0; i < net.IPv4len; i++ { + sa.Addr[i] = ip[i] + } + sa.Port = 0 + return sa, nil } func (t *Listener) readRAWSocket() { - conn, e := net.ListenPacket("ip4:tcp", t.addr) + var n int + var sa syscall.Sockaddr + var err error - if e != nil { - log.Fatal(e) - } + addr, _ := net.ResolveIPAddr("ip4", t.addr) + sa, _ = ipToSockaddr(addr.IP) + s, e := sysSocket(syscall.AF_INET, syscall.SOCK_RAW|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, syscall.IPPROTO_TCP) - defer conn.Close() + if e != nil { + log.Fatal(e) + } - buf := make([]byte, 4096*10) + if err := syscall.Bind(s, sa); err != nil { + log.Fatal(os.NewSyscallError("bind", err)) + } - for { - // Note: ReadFrom receive messages without IP header - n, addr, err := conn.ReadFrom(buf) + defer syscall.Close(s) - if err != nil { - log.Println("Error:", err) - continue - } + for { + buf := make([]byte, 64*1024) // 64kb - if n > 0 { - t.parsePacket(addr, buf[:n]) - } - } + for { + n, sa, err = syscall.Recvfrom(s, buf, 0) + + if err != nil { + n = 0 + if err == syscall.EAGAIN { + continue + } + } + + break + } + + if err != nil { + log.Println("Error:", err) + continue + } + + if n > 0 { + // Ip header size + hsize := (int(buf[0]) & 0xf) * 4 + + if n > hsize { + go t.parsePacket(sa, buf[hsize:n]) + } + } + + } } -func (t *Listener) parsePacket(addr net.Addr, buf []byte) { - if t.isIncomingDataPacket(buf) { - new_buf := make([]byte, len(buf)) - copy(new_buf, buf) +func (t *Listener) parsePacket(sa syscall.Sockaddr, buf []byte) { + addr := &net.IPAddr{IP: sa.(*syscall.SockaddrInet4).Addr[0:]} - t.c_packets <- ParseTCPPacket(addr, new_buf) - } + if t.isIncomingDataPacket(buf) { + t.c_packets <- ParseTCPPacket(addr, buf) + } } func (t *Listener) isIncomingDataPacket(buf []byte) bool { - // To avoid full packet parsing every time, we manually parsing values needed for packet filtering - // http://en.wikipedia.org/wiki/Transmission_Control_Protocol - dest_port := binary.BigEndian.Uint16(buf[2:4]) + // To avoid full packet parsing every time, we manually parsing values needed for packet filtering + // http://en.wikipedia.org/wiki/Transmission_Control_Protocol + dest_port := binary.BigEndian.Uint16(buf[2:4]) - // Because RAW_SOCKET can't be bound to port, we have to control it by ourself - if int(dest_port) == t.port { - // Get the 'data offset' (size of the TCP header in 32-bit words) - dataOffset := (buf[12] & 0xF0) >> 4 + // Because RAW_SOCKET can't be bound to port, we have to control it by ourself + if int(dest_port) == t.port { + // Get the 'data offset' (size of the TCP header in 32-bit words) + dataOffset := (buf[12] & 0xF0) >> 4 - // We need only packets with data inside - // Check that the buffer is larger than the size of the TCP header - if len(buf) > int(dataOffset*4) { - // We should create new buffer because go slices is pointers. So buffer data shoud be immutable. - return true - } - } + // We need only packets with data inside + // Check that the buffer is larger than the size of the TCP header + if len(buf) > int(dataOffset*4) { + // We should create new buffer because go slices is pointers. So buffer data shoud be immutable. + return true + } + } - return false + return false } var bExpect100ContinueCheck = []byte("Expect: 100-continue") @@ -132,44 +204,44 @@ var bPOST = []byte("POST") // // For TCP message unique id is Acknowledgment number (see tcp_packet.go) func (t *Listener) processTCPPacket(packet *TCPPacket) { - defer func() { recover() }() + defer func() { recover() }() - var message *TCPMessage + var message *TCPMessage - parent_message_ack, parent_ok := t.seq_with_data[packet.Seq] - if parent_ok { - t.ack_aliases[packet.Ack] = parent_message_ack - delete(t.seq_with_data, packet.Seq) - } + parent_message_ack, parent_ok := t.seq_with_data[packet.Seq] + if parent_ok { + t.ack_aliases[packet.Ack] = parent_message_ack + delete(t.seq_with_data, packet.Seq) + } - ack_alias, alias_ok := t.ack_aliases[packet.Ack] - if alias_ok { - packet.Ack = ack_alias - } + ack_alias, alias_ok := t.ack_aliases[packet.Ack] + if alias_ok { + packet.Ack = ack_alias + } - m_id := packet.Addr.String() + strconv.Itoa(int(packet.Ack)) - message, ok := t.messages[m_id] + m_id := packet.Addr.String() + strconv.Itoa(int(packet.Ack)) + message, ok := t.messages[m_id] - if !ok { - // We sending c_del_message channel, so message object can communicate with Listener and notify it if message completed - message = NewTCPMessage(m_id, t.c_del_message) - t.messages[m_id] = message - } + if !ok { + // We sending c_del_message channel, so message object can communicate with Listener and notify it if message completed + message = NewTCPMessage(m_id, t.c_del_message, packet.Ack) + t.messages[m_id] = message + } - if bytes.Equal(packet.Data[0:4], bPOST) { - if bytes.Equal(packet.Data[len(packet.Data)-24:len(packet.Data)-4], bExpect100ContinueCheck) { - t.seq_with_data[packet.Seq + uint32(len(packet.Data))] = packet.Ack + if bytes.Equal(packet.Data[0:4], bPOST) { + if bytes.Equal(packet.Data[len(packet.Data)-24:len(packet.Data)-4], bExpect100ContinueCheck) { + t.seq_with_data[packet.Seq+uint32(len(packet.Data))] = packet.Ack - // Removing `Expect: 100-continue` header - packet.Data = append(packet.Data[:len(packet.Data)-24], packet.Data[len(packet.Data)-2:]...) - } - } + // Removing `Expect: 100-continue` header + packet.Data = append(packet.Data[:len(packet.Data)-24], packet.Data[len(packet.Data)-2:]...) + } + } - // Adding packet to message - message.c_packets <- packet + // Adding packet to message + message.c_packets <- packet } // Receive TCP messages from the listener channel func (t *Listener) Receive() *TCPMessage { - return <-t.c_messages + return <-t.c_messages } diff --git a/raw_socket_listener/tcp_message.go b/raw_socket_listener/tcp_message.go index 69c4b93..32fdaae 100644 --- a/raw_socket_listener/tcp_message.go +++ b/raw_socket_listener/tcp_message.go @@ -1,9 +1,9 @@ package raw_socket import ( - "log" - "sort" - "time" + "log" + "sort" + "time" ) const MSG_EXPIRE = 2000 * time.Millisecond @@ -15,91 +15,92 @@ const MSG_EXPIRE = 2000 * time.Millisecond // Message can be compiled from unique packets with same message_id which sorted by sequence // Message is received if we didn't receive any packets for 2000ms type TCPMessage struct { - ID string // Message ID - packets []*TCPPacket + ID string // Message ID + Ack uint32 + packets []*TCPPacket - timer *time.Timer // Used for expire check + timer *time.Timer // Used for expire check - c_packets chan *TCPPacket + c_packets chan *TCPPacket - c_del_message chan *TCPMessage + c_del_message chan *TCPMessage } // NewTCPMessage pointer created from a Acknowledgment number and a channel of messages readuy to be deleted -func NewTCPMessage(ID string, c_del chan *TCPMessage) (msg *TCPMessage) { - msg = &TCPMessage{ID: ID} +func NewTCPMessage(ID string, c_del chan *TCPMessage, Ack uint32) (msg *TCPMessage) { + msg = &TCPMessage{ID: ID, Ack: Ack} - msg.c_packets = make(chan *TCPPacket) - msg.c_del_message = c_del // used for notifying that message completed or expired + msg.c_packets = make(chan *TCPPacket) + msg.c_del_message = c_del // used for notifying that message completed or expired - // Every time we receive packet we reset this timer - msg.timer = time.AfterFunc(MSG_EXPIRE, msg.Timeout) + // Every time we receive packet we reset this timer + msg.timer = time.AfterFunc(MSG_EXPIRE, msg.Timeout) - go msg.listen() + go msg.listen() - return + return } func (t *TCPMessage) listen() { - for { - select { - case packet, more := <-t.c_packets: - if more { - t.AddPacket(packet) - } else { - // Stop loop if channel closed - return - } - } - } + for { + select { + case packet, more := <-t.c_packets: + if more { + t.AddPacket(packet) + } else { + // Stop loop if channel closed + return + } + } + } } // Timeout notifies message to stop listening, close channel and message ready to be sent func (t *TCPMessage) Timeout() { - select { - // In some cases Timeout can be called multiple times (do not know how yet) - // Ensure that we did not close channel 2 times - case packet, ok := <- t.c_packets: - if ok { - t.AddPacket(packet) - } else { - return - } - default: - close(t.c_packets) - t.c_del_message <- t // Notify RAWListener that message is ready to be send to replay server - } + select { + // In some cases Timeout can be called multiple times (do not know how yet) + // Ensure that we did not close channel 2 times + case packet, ok := <-t.c_packets: + if ok { + t.AddPacket(packet) + } else { + return + } + default: + close(t.c_packets) + t.c_del_message <- t // Notify RAWListener that message is ready to be send to replay server + } } // Bytes sorts packets in right orders and return message content func (t *TCPMessage) Bytes() (output []byte) { - sort.Sort(BySeq(t.packets)) + sort.Sort(BySeq(t.packets)) - for _, v := range t.packets { - output = append(output, v.Data...) - } + for _, v := range t.packets { + output = append(output, v.Data...) + } - return output + return output } // AddPacket to the message and ensure packet uniqueness // TCP allows that packet can be re-send multiple times func (t *TCPMessage) AddPacket(packet *TCPPacket) { - packetFound := false + packetFound := false - for _, pkt := range t.packets { - if packet.Seq == pkt.Seq { - packetFound = true - break - } - } + for _, pkt := range t.packets { + if packet.Seq == pkt.Seq { + packetFound = true + break + } + } - if packetFound { - log.Println("Received packet with same sequence") - } else { - t.packets = append(t.packets, packet) - } + if packetFound { + log.Println("Received packet with same sequence") + } else { + t.packets = append(t.packets, packet) + } - // Reset message timeout timer - t.timer.Reset(MSG_EXPIRE) + // Reset message timeout timer + t.timer.Reset(MSG_EXPIRE) } diff --git a/raw_socket_listener/tcp_packet.go b/raw_socket_listener/tcp_packet.go index 5af8a73..393c0e5 100644 --- a/raw_socket_listener/tcp_packet.go +++ b/raw_socket_listener/tcp_packet.go @@ -1,101 +1,101 @@ package raw_socket import ( - "encoding/binary" - "net" - "strconv" - "strings" + "encoding/binary" + "net" + "strconv" + "strings" ) // TCP Flags const ( - TCP_FIN = 1 << iota - TCP_SYN - TCP_RST - TCP_PSH - TCP_ACK - TCP_URG - TCP_ECE - TCP_CWR - TCP_NS + TCP_FIN = 1 << iota + TCP_SYN + TCP_RST + TCP_PSH + TCP_ACK + TCP_URG + TCP_ECE + TCP_CWR + TCP_NS ) // Simple TCP packet parser // // Packet structure: http://en.wikipedia.org/wiki/Transmission_Control_Protocol type TCPPacket struct { - SrcPort uint16 - DestPort uint16 - Seq uint32 - Ack uint32 - DataOffset uint8 - Flags uint16 - Window uint16 - Checksum uint16 - Urgent uint16 + SrcPort uint16 + DestPort uint16 + Seq uint32 + Ack uint32 + DataOffset uint8 + Flags uint16 + Window uint16 + Checksum uint16 + Urgent uint16 - Data []byte + Data []byte - Addr net.Addr + Addr net.Addr } func ParseTCPPacket(addr net.Addr, b []byte) (p *TCPPacket) { - p = &TCPPacket{Data: b} - p.ParseBasic() - p.Addr = addr + p = &TCPPacket{Data: b} + p.ParseBasic() + p.Addr = addr - return p + return p } // Parse TCP Packet, inspired by: https://github.com/miekg/pcap/blob/master/packet.go func (t *TCPPacket) Parse() { - t.ParseBasic() - t.SrcPort = binary.BigEndian.Uint16(t.Data[0:2]) - t.DestPort = binary.BigEndian.Uint16(t.Data[2:4]) - t.Flags = binary.BigEndian.Uint16(t.Data[12:14]) & 0x1FF - t.Window = binary.BigEndian.Uint16(t.Data[14:16]) - t.Checksum = binary.BigEndian.Uint16(t.Data[16:18]) - t.Urgent = binary.BigEndian.Uint16(t.Data[18:20]) + t.ParseBasic() + t.SrcPort = binary.BigEndian.Uint16(t.Data[0:2]) + t.DestPort = binary.BigEndian.Uint16(t.Data[2:4]) + t.Flags = binary.BigEndian.Uint16(t.Data[12:14]) & 0x1FF + t.Window = binary.BigEndian.Uint16(t.Data[14:16]) + t.Checksum = binary.BigEndian.Uint16(t.Data[16:18]) + t.Urgent = binary.BigEndian.Uint16(t.Data[18:20]) } // ParseBasic set of fields func (t *TCPPacket) ParseBasic() { - t.Seq = binary.BigEndian.Uint32(t.Data[4:8]) - t.Ack = binary.BigEndian.Uint32(t.Data[8:12]) - t.DataOffset = (t.Data[12] & 0xF0) >> 4 + t.Seq = binary.BigEndian.Uint32(t.Data[4:8]) + t.Ack = binary.BigEndian.Uint32(t.Data[8:12]) + t.DataOffset = (t.Data[12] & 0xF0) >> 4 - t.Data = t.Data[t.DataOffset*4:] + t.Data = t.Data[t.DataOffset*4:] } // String output for a TCP Packet func (t *TCPPacket) String() string { - return strings.Join([]string{ - "Source port: " + strconv.Itoa(int(t.SrcPort)), - "Dest port:" + strconv.Itoa(int(t.DestPort)), - "Sequence:" + strconv.Itoa(int(t.Seq)), - "Acknowledgment:" + strconv.Itoa(int(t.Ack)), - "Header len:" + strconv.Itoa(int(t.DataOffset)), + return strings.Join([]string{ + "Source port: " + strconv.Itoa(int(t.SrcPort)), + "Dest port:" + strconv.Itoa(int(t.DestPort)), + "Sequence:" + strconv.Itoa(int(t.Seq)), + "Acknowledgment:" + strconv.Itoa(int(t.Ack)), + "Header len:" + strconv.Itoa(int(t.DataOffset)), - "Flag ns:" + strconv.FormatBool(t.Flags&TCP_NS != 0), - "Flag crw:" + strconv.FormatBool(t.Flags&TCP_CWR != 0), - "Flag ece:" + strconv.FormatBool(t.Flags&TCP_ECE != 0), - "Flag urg:" + strconv.FormatBool(t.Flags&TCP_URG != 0), - "Flag ack:" + strconv.FormatBool(t.Flags&TCP_ACK != 0), - "Flag psh:" + strconv.FormatBool(t.Flags&TCP_PSH != 0), - "Flag rst:" + strconv.FormatBool(t.Flags&TCP_RST != 0), - "Flag syn:" + strconv.FormatBool(t.Flags&TCP_SYN != 0), - "Flag fin:" + strconv.FormatBool(t.Flags&TCP_FIN != 0), + "Flag ns:" + strconv.FormatBool(t.Flags&TCP_NS != 0), + "Flag crw:" + strconv.FormatBool(t.Flags&TCP_CWR != 0), + "Flag ece:" + strconv.FormatBool(t.Flags&TCP_ECE != 0), + "Flag urg:" + strconv.FormatBool(t.Flags&TCP_URG != 0), + "Flag ack:" + strconv.FormatBool(t.Flags&TCP_ACK != 0), + "Flag psh:" + strconv.FormatBool(t.Flags&TCP_PSH != 0), + "Flag rst:" + strconv.FormatBool(t.Flags&TCP_RST != 0), + "Flag syn:" + strconv.FormatBool(t.Flags&TCP_SYN != 0), + "Flag fin:" + strconv.FormatBool(t.Flags&TCP_FIN != 0), - "Window size:" + strconv.Itoa(int(t.Window)), - "Checksum:" + strconv.Itoa(int(t.Checksum)), + "Window size:" + strconv.Itoa(int(t.Window)), + "Checksum:" + strconv.Itoa(int(t.Checksum)), - "Data size:" + strconv.Itoa(len(t.Data)), - "Data:" + string(t.Data), - }, "\n") + "Data size:" + strconv.Itoa(len(t.Data)), + "Data:" + string(t.Data), + }, "\n") } type BySeq []*TCPPacket -func (a BySeq) Len() int { return len(a) } -func (a BySeq) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a BySeq) Less(i, j int) bool { return a[i].Seq < a[j].Seq } +func (a BySeq) Len() int { return len(a) } +func (a BySeq) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a BySeq) Less(i, j int) bool { return a[i].Seq < a[j].Seq } diff --git a/settings.go b/settings.go index 62f089d..31b4989 100644 --- a/settings.go +++ b/settings.go @@ -25,7 +25,7 @@ func (h *MultiOption) Set(value string) error { type AppSettings struct { verbose bool - debug bool + debug bool stats bool splitOutput bool diff --git a/test_input.go b/test_input.go index 2140728..8f62344 100644 --- a/test_input.go +++ b/test_input.go @@ -35,14 +35,14 @@ func (i *TestInput) EmitChunkedPOST() { i.data <- []byte("POST /pub/WWW/ HTTP/1.1\r\nHost: www.w3.org\r\nTransfer-Encoding: chunked\r\n\r\n4\r\nWiki\r\n5\r\npedia\r\ne\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n") } -func (i *TestInput) EmitFile() { - size := 1024 * 1024 // 1 MB +func (i *TestInput) EmitLargePOST() { + size := 5 * 1024 * 1024 // 5 MB rb := make([]byte, size) rand.Read(rb) rs := base64.URLEncoding.EncodeToString(rb) - i.data <- []byte("POST /pub/WWW/ HTTP/1.1\nHost: www.w3.org\r\n\r\n------WebKitFormBoundaryePkpFF7tjBAqx29L\nContent-Disposition: form-data; name=\"uploadedfile\"; filename=\"hello.o\"\nContent-Type: application/x-object\n" + rs + "\r\n\r\n") + i.data <- []byte("POST / HTTP/1.1\nHost: www.w3.org\nContent-Length:5242880\r\n\r\n" + rs) } func (i *TestInput) EmitOPTIONS() {