diff --git a/Makefile b/Makefile index 7e05ba1..a59084f 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -SOURCE = emitter.go gor.go gor_stat.go input_dummy.go input_file.go input_raw.go input_tcp.go limiter.go output_dummy.go output_file.go output_http.go output_tcp.go plugins.go settings.go settings_header_filters.go settings_header_hash_filters.go settings_headers.go settings_methods.go settings_option.go settings_url_regexp.go test_input.go elasticsearch.go settings_url_map.go +SOURCE = emitter.go gor.go gor_stat.go input_dummy.go input_file.go input_raw.go input_tcp.go limiter.go output_dummy.go output_file.go input_http.go output_http.go output_tcp.go plugins.go settings.go settings_header_filters.go settings_header_hash_filters.go settings_headers.go settings_methods.go settings_option.go settings_url_regexp.go test_input.go elasticsearch.go settings_url_map.go release: release-x86 release-x64 @@ -15,7 +15,7 @@ dtest: docker run -v `pwd`:/gopath/src/gor -t -i --env GORACE="halt_on_error=1" gor go test -race -v drun: - docker run -v `pwd`:/gopath/src/gor -t -i gor go run $(SOURCE) --input-dummy=0 --output-dummy=0 --verbose + docker run -v `pwd`:/gopath/src/gor -t -i gor go run $(SOURCE) --input-dummy=0 --input-http=:9000 --output-http="http://localhost:9000" --verbose dbash: docker run -v `pwd`:/gopath/src/gor -t -i gor /bin/bash \ No newline at end of file diff --git a/input_http.go b/input_http.go new file mode 100644 index 0000000..88640fb --- /dev/null +++ b/input_http.go @@ -0,0 +1,63 @@ +package main + +import ( + "net" + "net/http" + "log" + "net/http/httputil" +) + +type HTTPInput struct { + data chan []byte + address string + listener net.Listener +} + +func NewHTTPInput(address string) (i *HTTPInput) { + i = new(HTTPInput) + i.data = make(chan []byte) + i.address = address + + i.listen(address) + + return +} + +func (i *HTTPInput) Read(data []byte) (int, error) { + buf := <-i.data + copy(data, buf) + + return len(buf), nil +} + +func (i *HTTPInput) handler(w http.ResponseWriter, r *http.Request) { + buf, _ := httputil.DumpRequest(r, true) + + i.data <- buf + + http.Error(w, http.StatusText(200), 200) +} + +func (i *HTTPInput) listen(address string) { + var err error + + mux := http.NewServeMux() + + mux.HandleFunc("/", i.handler) + + i.listener, err = net.Listen("tcp", address) + if err != nil { + log.Fatal("HTTP input listener failure:", err) + } + + go func(){ + err = http.Serve(i.listener, mux) + if err != nil { + log.Fatal("HTTP input serve failure:", err) + } + }() +} + +func (i *HTTPInput) String() string { + return "HTTP input: " + i.address +} diff --git a/input_http_test.go b/input_http_test.go new file mode 100644 index 0000000..c8db28a --- /dev/null +++ b/input_http_test.go @@ -0,0 +1,35 @@ +package main + +import ( + "io" + "sync" + "testing" + "net/http" + "strings" +) + +func TestHTTPInput(t *testing.T) { + wg := new(sync.WaitGroup) + quit := make(chan int) + + input := NewHTTPInput(":0") + output := NewTestOutput(func(data []byte) { + wg.Done() + }) + + Plugins.Inputs = []io.Reader{input} + Plugins.Outputs = []io.Writer{output} + + go Start(quit) + + address := strings.Replace(input.listener.Addr().String(), "[::]", "127.0.0.1", -1) + + for i := 0; i < 100; i++ { + wg.Add(1) + http.Get("http://" + address + "/") + } + + wg.Wait() + + close(quit) +} \ No newline at end of file diff --git a/plugins.go b/plugins.go index 1cd5ae2..ed21d42 100644 --- a/plugins.go +++ b/plugins.go @@ -40,6 +40,10 @@ func InitPlugins() { Plugins.Outputs = append(Plugins.Outputs, NewFileOutput(options)) } + for _, options := range Settings.inputHTTP { + Plugins.Inputs = append(Plugins.Inputs, NewHTTPInput(options)) + } + for _, options := range Settings.outputHTTP { Plugins.Outputs = append(Plugins.Outputs, NewHTTPOutput(options, Settings.outputHTTPHeaders, Settings.outputHTTPMethods, Settings.outputHTTPUrlRegexp, Settings.outputHTTPHeaderFilters, Settings.outputHTTPHeaderHashFilters, Settings.outputHTTPElasticSearch, Settings.outputHTTPUrlRewrite)) } diff --git a/settings.go b/settings.go index 305ec0e..f66fff0 100644 --- a/settings.go +++ b/settings.go @@ -29,6 +29,7 @@ type AppSettings struct { inputRAW MultiOption + inputHTTP MultiOption outputHTTP MultiOption outputHTTPHeaders HTTPHeaders outputHTTPMethods HTTPMethods @@ -69,6 +70,8 @@ func init() { flag.Var(&Settings.inputRAW, "input-raw", "Capture traffic from given port (use RAW sockets and require *sudo* access):\n\t# Capture traffic from 8080 port\n\tgor --input-raw :8080 --output-http staging.com") + flag.Var(&Settings.inputHTTP, "input-http", "Read requests from HTTP, should be explicitly sent from your application:\n\t# Listen for http on 9000\n\tgor --input-http :9000 --output-http staging.com") + flag.Var(&Settings.outputHTTP, "output-http", "Forwards incoming requests to given http address.\n\t# Redirect all incoming requests to staging.com address \n\tgor --input-raw :80 --output-http http://staging.com") flag.Var(&Settings.outputHTTPHeaders, "output-http-header", "Inject additional headers to http reqest:\n\tgor --input-raw :8080 --output-http staging.com --output-http-header 'User-Agent: Gor'") flag.Var(&Settings.outputHTTPMethods, "output-http-method", "Whitelist of HTTP methods to replay. Anything else will be dropped:\n\tgor --input-raw :8080 --output-http staging.com --output-http-method GET --output-http-method OPTIONS")