From 208faec7d298a7a95958bc21ef9427966a7d1d59 Mon Sep 17 00:00:00 2001 From: Leonid Bugaev Date: Tue, 16 Dec 2014 16:25:17 +0500 Subject: [PATCH] Initial support for input modifiers --- Dockerfile | 4 +- Makefile | 8 +-- examples/echo_modifier/echo_modifier.go | 44 +++++++++++++++++ plugins.go | 5 ++ pong_modifier.rb | 13 +++++ settings.go | 4 ++ traffic_modifier.go | 65 +++++++++++++++++++++++++ 7 files changed, 138 insertions(+), 5 deletions(-) create mode 100644 examples/echo_modifier/echo_modifier.go create mode 100755 pong_modifier.rb create mode 100644 traffic_modifier.go diff --git a/Dockerfile b/Dockerfile index 2b174ce..7352439 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,8 +2,10 @@ FROM google/golang RUN cd /goroot/src/ && GOOS=linux GOARCH=386 ./make.bash --no-clean +RUN apt-get install ruby -y + WORKDIR /gopath/src/gor ADD . /gopath/src/gor -RUN go get \ No newline at end of file +RUN go get diff --git a/Makefile b/Makefile index 037e311..4b68a9e 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,10 @@ -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 +SOURCE = emitter.go gor.go traffic_modifier.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 release-x64: docker run -v `pwd`:/gopath/src/gor -t --env GOOS=linux --env GOARCH=amd64 --env CGO_ENABLED=0 -i gor go build && tar -czf gor_x64.tar.gz gor && rm gor - + release-x86: docker run -v `pwd`:/gopath/src/gor -t --env GOOS=linux --env GOARCH=386 --env CGO_ENABLED=0 -i gor go build && tar -czf gor_x86.tar.gz gor && rm gor @@ -22,7 +22,7 @@ dbench: # Used mainly for debugging, because docker container do not have access to parent machine ports drun: - 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 + docker run -v `pwd`:/gopath/src/gor -t -i gor go run $(SOURCE) --input-modifier="./pong_modifier.rb" --input-dummy=0 --input-http=:9000 --output-http="http://localhost:9000" --verbose -dbash: +dbash: docker run -v `pwd`:/gopath/src/gor -t -i gor /bin/bash \ No newline at end of file diff --git a/examples/echo_modifier/echo_modifier.go b/examples/echo_modifier/echo_modifier.go new file mode 100644 index 0000000..9dd0ea6 --- /dev/null +++ b/examples/echo_modifier/echo_modifier.go @@ -0,0 +1,44 @@ +package main + +import ( + "os" + "bufio" +) + +func main() { + reader := bufio.NewReader(os.Stdin) + data := make(chan []byte) + + go ReadStdin(data) + + for { + os.Stdout.Print(<- data, '¶') + } +} + +func ReadStdin(data chan []byte){ + for { + buf, err := reader.ReadBytes('¶') + buf_len := len(buf) + if buf_len > 0 { + new_buf_len := len(buf) - 2 + if new_buf_len > 0 { + new_buf := make([]byte, new_buf_len) + copy(new_buf, buf[:new_buf_len]) + data <- new_buf + if err != nil { + if err != io.EOF { + log.Printf("error: %s\n", err) + } + } + } + } + } +} + +while data = STDIN.gets(separator) + STDERR.puts "==== Start ====" + STDERR.puts data + puts data + STDERR.puts "==== End ====" +end \ No newline at end of file diff --git a/plugins.go b/plugins.go index 7f96ae0..0cc8ab5 100644 --- a/plugins.go +++ b/plugins.go @@ -9,6 +9,8 @@ import ( type InOutPlugins struct { Inputs []io.Reader Outputs []io.Writer + + Modifiers []TrafficModifier } type ReaderOrWriter interface{} @@ -54,6 +56,9 @@ func registerPlugin(constructor interface{}, options ...interface{}) { } if _, ok := plugin.(io.Reader); ok { + for _, options := range Settings.inputModifier { + plugin_wrapper = NewTrafficModifier(plugin_wrapper, options) + } Plugins.Inputs = append(Plugins.Inputs, plugin_wrapper.(io.Reader)) } diff --git a/pong_modifier.rb b/pong_modifier.rb new file mode 100755 index 0000000..e702507 --- /dev/null +++ b/pong_modifier.rb @@ -0,0 +1,13 @@ +#!/usr/bin/env ruby +# encoding: utf-8 +require "base64" + +STDERR.puts "Starting modifier" +puts "Starting modifier" + +while data = STDIN.gets.chomp + STDERR.puts "==== Start ====" + STDERR.puts Base64.encode64(data) + puts data + STDERR.puts "==== End ====" +end \ No newline at end of file diff --git a/settings.go b/settings.go index f7973c1..e59a43a 100644 --- a/settings.go +++ b/settings.go @@ -29,6 +29,8 @@ type AppSettings struct { inputRAW MultiOption + inputModifier MultiOption + inputHTTP MultiOption outputHTTP MultiOption outputHTTPHeaders HTTPHeaders @@ -70,6 +72,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.inputModifier, "input-modifier", "Used for modifying input traffic using external command") + 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") diff --git a/traffic_modifier.go b/traffic_modifier.go new file mode 100644 index 0000000..0e8a09a --- /dev/null +++ b/traffic_modifier.go @@ -0,0 +1,65 @@ +package main + +import ( + "fmt" + "log" + "io" + "os/exec" + "os" + "encoding/base64" +) + +type TrafficModifier struct { + plugin interface{} + command string + + data chan []byte + + Stdin io.Writer + Stdout io.Reader +} + +func NewTrafficModifier(plugin interface{}, command string) io.Reader { + m := new(TrafficModifier) + m.plugin = plugin + m.command = command + + cmd := exec.Command("bash", "-c", command) + cmd.Stderr = os.Stderr + + m.Stdout, _ = cmd.StdoutPipe() + m.Stdin, _ = cmd.StdinPipe() + + m.Stdout = base64.NewDecoder(base64.StdEncoding, m.Stdout) + + go m.copy(m.Stdin, m.plugin.(io.Reader)) + + err := cmd.Run() + + if (err != nil) { + log.Fatal(err) + } + + return m +} + +func (m *TrafficModifier) copy(to io.Writer, from io.Reader) { + buf := make([]byte, 5*1024*1024) + + for { + nr, er := from.Read(buf) + if nr > 0 && len(buf) > nr { + to.Write(base64.StdEncoding.Encode(buf)) + } + } +} + +func (m *TrafficModifier) Read(data []byte) (n int, err error) { + n, err = m.Stdout.Read(data) + + return +} + +func (m *TrafficModifier) String() string { + return fmt.Sprintf("Modifying traffic for %s using '%s' command", m.plugin, m.command) +}