diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..522ae1b --- /dev/null +++ b/build.sh @@ -0,0 +1,2 @@ +export GOPATH=`pwd` +go build -o bin/gomitmproxy src/main/*.go \ No newline at end of file diff --git a/dump.go b/dump.go deleted file mode 100644 index d1a6290..0000000 --- a/dump.go +++ /dev/null @@ -1,71 +0,0 @@ -package main - -import ( - "bufio" - "bytes" - "compress/flate" - "compress/gzip" - "fmt" - "io/ioutil" - "math" - "net/http" - "strconv" -) - -func httpDump(req *http.Request, resp *http.Response) { - defer resp.Body.Close() - var respStatusStr string - respStatus := resp.StatusCode - respStatusHeader := int(math.Floor(float64(respStatus / 100))) - switch respStatusHeader { - case 2: - respStatusStr = Green("<--" + strconv.Itoa(respStatus)) - case 3: - respStatusStr = Yellow("<--" + strconv.Itoa(respStatus)) - case 4: - respStatusStr = Magenta("<--" + strconv.Itoa(respStatus)) - case 5: - respStatusStr = Red("<--" + strconv.Itoa(respStatus)) - } - fmt.Println(Green("Request:")) - fmt.Printf("%s %s %s\n", Blue(req.Method), req.RequestURI, respStatusStr) - for headerName, headerContext := range req.Header { - fmt.Printf("%s: %s\n", Blue(headerName), headerContext) - } - fmt.Println(Green("Response:")) - for headerName, headerContext := range resp.Header { - fmt.Printf("%s: %s\n", Blue(headerName), headerContext) - } - - respBody, err := ioutil.ReadAll(resp.Body) - if err != nil { - logger.Println("func httpDump read resp body err:", err) - } else { - acceptEncode := resp.Header["Content-Encoding"] - var respBodyBin bytes.Buffer - w := bufio.NewWriter(&respBodyBin) - w.Write(respBody) - w.Flush() - for _, compress := range acceptEncode { - switch compress { - case "gzip": - r, err := gzip.NewReader(&respBodyBin) - if err != nil { - logger.Println("gzip reader err:", err) - } else { - defer r.Close() - respBody, _ = ioutil.ReadAll(r) - } - break - case "deflate": - r := flate.NewReader(&respBodyBin) - defer r.Close() - respBody, _ = ioutil.ReadAll(r) - break - } - } - fmt.Printf("%s\n", string(respBody)) - } - - fmt.Printf("%s%s%s\n", Black("####################"), Cyan("END"), Black("####################")) -} diff --git a/gomitmproxy.go b/gomitmproxy.go deleted file mode 100644 index c358b15..0000000 --- a/gomitmproxy.go +++ /dev/null @@ -1,84 +0,0 @@ -// This example shows a proxy server that uses go-mitm to man-in-the-middle -// HTTPS connections opened with CONNECT requests - -package main - -import ( - "flag" - "log" - "net/http" - "os" - "sync" - "time" -) - -const ( - Version = "1.1" -) - -var ( - wg sync.WaitGroup -) - -var logFile *os.File -var logger *log.Logger - -func main() { - var conf Cfg - - conf.Port = flag.String("port", "8080", "Listen port") - conf.Raddr = flag.String("raddr", "", "Remote addr") - conf.Log = flag.String("log", "./error.log", "log file path") - conf.Monitor = flag.Bool("m", false, "monitor mode") - conf.Tls = flag.Bool("tls", false, "tls connect") - flag.Parse() - - var err error - logFile, err = os.Create(*conf.Log) - if err != nil { - log.Fatalln("fail to create log file!") - } - - logger = log.New(logFile, "[gomitmproxy]", log.LstdFlags|log.Llongfile) - - wg.Add(1) - gomitmproxy(&conf) - wg.Wait() -} - -func gomitmproxy(conf *Cfg) { - tlsConfig := NewTlsConfig("gomitmproxy-ca-pk.pem", "gomitmproxy-ca-cert.pem", "", "") - - handler, err := InitConfig(conf, tlsConfig) - if err != nil { - logger.Fatalf("InitConfig error: %s", err) - } - - server := &http.Server{ - Addr: ":" + *conf.Port, - Handler: handler, - ReadTimeout: 1 * time.Hour, - WriteTimeout: 1 * time.Hour, - } - - go func() { - log.Printf("proxy listening port:%s", *conf.Port) - - if *conf.Tls { - log.Println("ListenAndServeTLS") - err = server.ListenAndServeTLS("gomitmproxy-ca-cert.pem", "gomitmproxy-ca-pk.pem") - } else { - log.Println("ListenAndServe") - err = server.ListenAndServe() - } - if err != nil { - logger.Fatalf("Unable to start HTTP proxy: %s", err) - } - - wg.Done() - - log.Printf("gomitmproxy stop!!!!") - }() - - return -} diff --git a/readme.md b/readme.md index c809f05..464fea6 100644 --- a/readme.md +++ b/readme.md @@ -40,7 +40,7 @@ gomitmproxy gomitmproxy -m ``` -![fetch http](https://raw.githubusercontent.com/sheepbao/gomitmproxy/develop/goproxy.png) +![fetch http](https://raw.githubusercontent.com/sheepbao/gomitmproxy/develop/doc/goproxy.png) 加 -m 参数,表示抓取http请求和响应 @@ -59,7 +59,7 @@ gomitmproxy -m ``` 然后浏览器设置代理,ip为localhost,端口为8080,即可实现科学上网 -![proxy](https://raw.githubusercontent.com/sheepbao/gomitmproxy/master/proxy.png) +![proxy](https://raw.githubusercontent.com/sheepbao/gomitmproxy/develop/doc/proxy.png) ## 最后 diff --git a/goproxy.png b/src/doc/goproxy.png similarity index 100% rename from goproxy.png rename to src/doc/goproxy.png diff --git a/proxy.png b/src/doc/proxy.png similarity index 100% rename from proxy.png rename to src/doc/proxy.png diff --git a/src/main/main.go b/src/main/main.go new file mode 100644 index 0000000..3a3ab49 --- /dev/null +++ b/src/main/main.go @@ -0,0 +1,41 @@ +package main + +import ( + "config" + "flag" + "io" + "mitm" + "mylog" + "os" + "sync" +) + +func main() { + var log io.WriteCloser + var err error + // cofig + conf := new(config.Cfg) + conf.Port = flag.String("port", "8080", "Listen port") + conf.Raddr = flag.String("raddr", "", "Remote addr") + conf.Log = flag.String("logFile", "", "log file path") + conf.Monitor = flag.Bool("m", false, "monitor mode") + conf.Tls = flag.Bool("tls", false, "tls connect") + flag.Parse() + + // init log + if *conf.Log != "" { + log, err = os.Create(*conf.Log) + if err != nil { + mylog.Fatalln("fail to create log file!") + } + } else { + log = os.Stderr + } + mylog.SetLog(log) + + // start mitm proxy + wg := new(sync.WaitGroup) + wg.Add(1) + mitm.Gomitmproxy(conf, wg) + wg.Wait() +} diff --git a/tcolor.go b/src/vendor/color/tcolor.go similarity index 99% rename from tcolor.go rename to src/vendor/color/tcolor.go index 5a6672c..5d92142 100644 --- a/tcolor.go +++ b/src/vendor/color/tcolor.go @@ -1,4 +1,4 @@ -package main +package color import ( "fmt" diff --git a/config.go b/src/vendor/config/config.go similarity index 87% rename from config.go rename to src/vendor/config/config.go index 6198be5..945ed42 100644 --- a/config.go +++ b/src/vendor/config/config.go @@ -1,16 +1,6 @@ -package main +package config -import ( - "crypto/tls" - "time" -) - -const ( - ONE_DAY = 24 * time.Hour - TWO_WEEKS = ONE_DAY * 14 - ONE_MONTH = 1 - ONE_YEAR = 1 -) +import "crypto/tls" type Cfg struct { Port *string diff --git a/cache.go b/src/vendor/mitm/cache.go similarity index 98% rename from cache.go rename to src/vendor/mitm/cache.go index 0a1c08e..d2c8022 100644 --- a/cache.go +++ b/src/vendor/mitm/cache.go @@ -1,6 +1,6 @@ // package cache implements a really primitive cache that associates expiring // values with string keys. This cache never clears itself out. -package main +package mitm import ( "sync" diff --git a/src/vendor/mitm/dump.go b/src/vendor/mitm/dump.go new file mode 100644 index 0000000..6887eff --- /dev/null +++ b/src/vendor/mitm/dump.go @@ -0,0 +1,100 @@ +package mitm + +import ( + "bufio" + "bytes" + "color" + "compress/flate" + "compress/gzip" + "fmt" + "io" + "io/ioutil" + "math" + "mylog" + "net/http" + "strconv" +) + +func httpDump(reqDump []byte, resp *http.Response) { + defer resp.Body.Close() + var respStatusStr string + respStatus := resp.StatusCode + respStatusHeader := int(math.Floor(float64(respStatus / 100))) + switch respStatusHeader { + case 2: + respStatusStr = color.Green("<--" + strconv.Itoa(respStatus)) + case 3: + respStatusStr = color.Yellow("<--" + strconv.Itoa(respStatus)) + case 4: + respStatusStr = color.Magenta("<--" + strconv.Itoa(respStatus)) + case 5: + respStatusStr = color.Red("<--" + strconv.Itoa(respStatus)) + } + + fmt.Println(color.Green("Request:"), respStatusStr) + req, _ := ParseReq(reqDump) + fmt.Printf("%s %s %s\n", color.Blue(req.Method), req.Host+req.RequestURI, respStatusStr) + fmt.Printf("%s %s\n", color.Blue("RemoteAddr:"), req.RemoteAddr) + for headerName, headerContext := range req.Header { + fmt.Printf("%s: %s\n", color.Blue(headerName), headerContext) + } + + if req.Method == "POST" { + fmt.Println(color.Green("POST Param:")) + err := req.ParseForm() + if err != nil { + mylog.Println("parseForm error:", err) + } else { + for k, v := range req.Form { + fmt.Printf("\t%s: %s\n", color.Blue(k), v) + } + } + } + fmt.Println(color.Green("Response:")) + for headerName, headerContext := range resp.Header { + fmt.Printf("%s: %s\n", color.Blue(headerName), headerContext) + } + + respBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + mylog.Println("func httpDump read resp body err:", err) + } else { + acceptEncode := resp.Header["Content-Encoding"] + var respBodyBin bytes.Buffer + w := bufio.NewWriter(&respBodyBin) + w.Write(respBody) + w.Flush() + for _, compress := range acceptEncode { + switch compress { + case "gzip": + r, err := gzip.NewReader(&respBodyBin) + if err != nil { + mylog.Println("gzip reader err:", err) + } else { + defer r.Close() + respBody, _ = ioutil.ReadAll(r) + } + break + case "deflate": + r := flate.NewReader(&respBodyBin) + defer r.Close() + respBody, _ = ioutil.ReadAll(r) + break + } + } + fmt.Printf("%s\n", string(respBody)) + } + + fmt.Printf("%s%s%s\n", color.Black("####################"), color.Cyan("END"), color.Black("####################")) +} + +func ParseReq(b []byte) (*http.Request, error) { + // func ReadRequest(b *bufio.Reader) (req *Request, err error) { return readRequest(b, deleteHostHeader) } + fmt.Println(string(b)) + fmt.Println("-----------------------") + var buf io.ReadWriter + buf = new(bytes.Buffer) + buf.Write(b) + bufr := bufio.NewReader(buf) + return http.ReadRequest(bufr) +} diff --git a/genKey.go b/src/vendor/mitm/genKey.go similarity index 98% rename from genKey.go rename to src/vendor/mitm/genKey.go index 0a54766..d83e076 100644 --- a/genKey.go +++ b/src/vendor/mitm/genKey.go @@ -1,5 +1,5 @@ // Package keyman provides convenience APIs around Go's built-in crypto APIs. -package main +package mitm import ( "crypto/rand" @@ -10,6 +10,7 @@ import ( "fmt" "io/ioutil" "math/big" + "mylog" "net" "os" "time" @@ -85,7 +86,7 @@ func (key *PrivateKey) WriteToFile(filename string) (err error) { return fmt.Errorf("Unable to PEM encode private key: %s", err) } if err := keyOut.Close(); err != nil { - logger.Printf("Unable to close file: %v", err) + mylog.Printf("Unable to close file: %v", err) } return } @@ -236,7 +237,7 @@ func (cert *Certificate) WriteToFile(filename string) (err error) { } defer func() { if err := certOut.Close(); err != nil { - logger.Printf("Unable to close file: %v", err) + mylog.Printf("Unable to close file: %v", err) } }() return pem.Encode(certOut, cert.pemBlock()) @@ -264,7 +265,7 @@ func (cert *Certificate) WriteToDERFile(filename string) (err error) { } defer func() { if err := certOut.Close(); err != nil { - logger.Printf("Unable to close file: %v", err) + mylog.Printf("Unable to close file: %v", err) } }() _, err = certOut.Write(cert.derBytes) diff --git a/src/vendor/mitm/gomitmproxy.go b/src/vendor/mitm/gomitmproxy.go new file mode 100644 index 0000000..a050d02 --- /dev/null +++ b/src/vendor/mitm/gomitmproxy.go @@ -0,0 +1,48 @@ +// This example shows a proxy server that uses go-mitm to man-in-the-middle +// HTTPS connections opened with CONNECT requests + +package mitm + +import ( + "config" + "mylog" + "net/http" + "sync" + "time" +) + +func Gomitmproxy(conf *config.Cfg, wg *sync.WaitGroup) { + tlsConfig := config.NewTlsConfig("gomitmproxy-ca-pk.pem", "gomitmproxy-ca-cert.pem", "", "") + + handler, err := InitConfig(conf, tlsConfig) + if err != nil { + mylog.Fatalf("InitConfig error: %s", err) + } + + server := &http.Server{ + Addr: ":" + *conf.Port, + Handler: handler, + ReadTimeout: 1 * time.Hour, + WriteTimeout: 1 * time.Hour, + } + + go func() { + mylog.Printf("Gomitmproxy Listening On: %s", *conf.Port) + if *conf.Tls { + mylog.Println("Listen And Serve HTTP TLS") + err = server.ListenAndServeTLS("gomitmproxy-ca-cert.pem", "gomitmproxy-ca-pk.pem") + } else { + mylog.Println("Listen And Serve HTTP") + err = server.ListenAndServe() + } + if err != nil { + mylog.Fatalf("Unable To Start HTTP proxy: %s", err) + } + + wg.Done() + + mylog.Printf("Gomitmproxy Stop!!!!") + }() + + return +} diff --git a/listener.go b/src/vendor/mitm/listener.go similarity index 96% rename from listener.go rename to src/vendor/mitm/listener.go index cb852d8..c1d14a5 100644 --- a/listener.go +++ b/src/vendor/mitm/listener.go @@ -1,4 +1,4 @@ -package main +package mitm import ( "io" diff --git a/mitm.go b/src/vendor/mitm/mitm.go similarity index 79% rename from mitm.go rename to src/vendor/mitm/mitm.go index 9f8a54e..4e0c956 100644 --- a/mitm.go +++ b/src/vendor/mitm/mitm.go @@ -1,12 +1,14 @@ -package main +package mitm import ( "bufio" + "config" "crypto/tls" "errors" "fmt" "io" "log" + "mylog" "net" "net/http" "net/http/httputil" @@ -17,9 +19,17 @@ import ( "time" ) +const ( + Version = "1.1" + ONE_DAY = 24 * time.Hour + TWO_WEEKS = ONE_DAY * 14 + ONE_MONTH = 1 + ONE_YEAR = 1 +) + type HandlerWrapper struct { - MyConfig *Cfg - tlsConfig *TlsConfig + MyConfig *config.Cfg + tlsConfig *config.TlsConfig wrapped http.Handler pk *PrivateKey pkPem []byte @@ -100,11 +110,20 @@ func (hw *HandlerWrapper) FakeCertForName(name string) (cert *tls.Certificate, e func (hw *HandlerWrapper) DumpHTTPAndHTTPs(resp http.ResponseWriter, req *http.Request) { req.Header.Del("Proxy-Connection") req.Header.Set("Connection", "Keep-Alive") - + var reqDump []byte + var err error + ch := make(chan bool) // handle connection + go func() { + reqDump, err = httputil.DumpRequestOut(req, true) + ch <- true + }() + if err != nil { + mylog.Println("DumpRequest error ", err) + } connIn, _, err := resp.(http.Hijacker).Hijack() if err != nil { - logger.Println("hijack error:", err) + mylog.Println("hijack error:", err) } defer connIn.Close() @@ -120,18 +139,20 @@ func (hw *HandlerWrapper) DumpHTTPAndHTTPs(resp http.ResponseWriter, req *http.R connOut, err := net.DialTimeout("tcp", host, time.Second*30) if err != nil { - logger.Println("dial to", host, "error:", err) + + mylog.Println("dial to", host, "error:", err) return } if err = req.Write(connOut); err != nil { - logger.Println("send to server error", err) + + mylog.Println("send to server error", err) return } respOut, err = http.ReadResponse(bufio.NewReader(connOut), req) if err != nil && err != io.EOF { - logger.Println("read response error:", err) + mylog.Println("read response error:", err) } } else { @@ -140,19 +161,21 @@ func (hw *HandlerWrapper) DumpHTTPAndHTTPs(resp http.ResponseWriter, req *http.R } connOut, err := tls.Dial("tcp", host, hw.tlsConfig.ServerTLSConfig) - if err != nil { - logger.Panicln("tls dial to", host, "error:", err) + + } + if err = req.Write(connOut); err != nil { + mylog.Println("tls dial to", host, "error:", err) return } if err = req.Write(connOut); err != nil { - logger.Println("send to server error", err) + mylog.Println("send to server error", err) return } respOut, err = http.ReadResponse(bufio.NewReader(connOut), req) if err != nil && err != io.EOF { - logger.Println("read response error:", err) + mylog.Println("read response error:", err) } } @@ -164,18 +187,20 @@ func (hw *HandlerWrapper) DumpHTTPAndHTTPs(resp http.ResponseWriter, req *http.R respDump, err := httputil.DumpResponse(respOut, true) if err != nil { - logger.Println("respDump error:", err) + mylog.Println("respDump error:", err) } _, err = connIn.Write(respDump) if err != nil { - logger.Println("connIn write error:", err) + mylog.Println("connIn write error:", err) } if *hw.MyConfig.Monitor { - go httpDump(req, respOut) + <-ch + go httpDump(reqDump, respOut) + } else { + <-ch } - } func (hw *HandlerWrapper) ServeHTTP(resp http.ResponseWriter, req *http.Request) { @@ -226,7 +251,7 @@ func (hw *HandlerWrapper) InterceptHTTPs(resp http.ResponseWriter, req *http.Req go func() { err = http.Serve(listener, handler) if err != nil && err != io.EOF { - logger.Printf("Error serving mitm'ed connection: %s", err) + mylog.Printf("Error serving mitm'ed connection: %s", err) } }() @@ -237,12 +262,12 @@ func (hw *HandlerWrapper) Forward(resp http.ResponseWriter, req *http.Request, r connIn, _, err := resp.(http.Hijacker).Hijack() connOut, err := net.Dial("tcp", raddr) if err != nil { - logger.Println("dial tcp error", err) + mylog.Println("dial tcp error", err) } err = connectProxyServer(connOut, raddr) if err != nil { - logger.Println("connectProxyServer error:", err) + mylog.Println("connectProxyServer error:", err) } if req.Method == "CONNECT" { @@ -250,24 +275,24 @@ func (hw *HandlerWrapper) Forward(resp http.ResponseWriter, req *http.Request, r "Proxy-Agent: gomitmproxy/" + Version + "\r\n\r\n") _, err := connIn.Write(b) if err != nil { - logger.Println("Write Connect err:", err) + mylog.Println("Write Connect err:", err) return } } else { req.Header.Del("Proxy-Connection") req.Header.Set("Connection", "Keep-Alive") if err = req.Write(connOut); err != nil { - logger.Println("send to server err", err) + mylog.Println("send to server err", err) return } } err = Transport(connIn, connOut) if err != nil { - log.Println("trans error ", err) + mylog.Println("trans error ", err) } } -func InitConfig(conf *Cfg, tlsConfig *TlsConfig) (*HandlerWrapper, error) { +func InitConfig(conf *config.Cfg, tlsConfig *config.TlsConfig) (*HandlerWrapper, error) { hw := &HandlerWrapper{ MyConfig: conf, tlsConfig: tlsConfig, @@ -288,6 +313,14 @@ func copyTlsConfig(template *tls.Config) *tls.Config { return tlsConfig } +func copyHTTPRequest(template *http.Request) *http.Request { + req := &http.Request{} + if template != nil { + *req = *template + } + return req +} + func respBadGateway(resp http.ResponseWriter, msg string) { log.Println(msg) resp.WriteHeader(502) @@ -316,7 +349,6 @@ func MyCopy(src io.Reader, dst io.Writer, ch chan<- error) { } func connectProxyServer(conn net.Conn, addr string) error { - req := &http.Request{ Method: "CONNECT", URL: &url.URL{Host: addr}, @@ -340,3 +372,22 @@ func connectProxyServer(conn net.Conn, addr string) error { } return nil } + +/*func ReadNotDrain(r *http.Request) (content []byte, err error) { + content, err = ioutil.ReadAll(r.Body) + r.Body = io.ReadCloser(bytes.NewBuffer(content)) + return +} + +func ParsePostValues(req *http.Request) (url.Values, error) { + c, err := ReadNotDrain(req) + if err != nil { + return nil, err + } + values, err := url.ParseQuery(string(c)) + if err != nil { + return nil, err + } + return values, nil +} +*/ diff --git a/src/vendor/mylog/my_log.go b/src/vendor/mylog/my_log.go new file mode 100644 index 0000000..55e6a8c --- /dev/null +++ b/src/vendor/mylog/my_log.go @@ -0,0 +1,30 @@ +package mylog + +import "log" +import "io" + +var logger *log.Logger + +func SetLog(l io.WriteCloser) { + logger = log.New(l, "[gomitmproxy]", log.LstdFlags) +} + +func Fatalf(format string, v ...interface{}) { + logger.Fatalf(format, v) +} + +func Fatalln(v ...interface{}) { + logger.Fatalln(v) +} + +func Printf(format string, v ...interface{}) { + logger.Printf(format, v) +} + +func Println(v ...interface{}) { + logger.Println(v) +} + +func Panicln(v ...interface{}) { + logger.Panicln(v) +} diff --git a/src/vendor/mylog/my_log_test.go b/src/vendor/mylog/my_log_test.go new file mode 100644 index 0000000..e3698d1 --- /dev/null +++ b/src/vendor/mylog/my_log_test.go @@ -0,0 +1,17 @@ +package mylog + +import ( + "log" + "os" + "testing" +) + +func TestMyLog(t *testing.T) { + logFile, err := os.Create("test.log") + if err != nil { + log.Fatalln("fail to create log file!") + } + logger := log.New(logFile, "[gomitmproxy]", log.LstdFlags|log.Llongfile) + SetLog(logger) + Println("log test") +} diff --git a/src/vendor/mylog/test.log b/src/vendor/mylog/test.log new file mode 100644 index 0000000..2a76a76 --- /dev/null +++ b/src/vendor/mylog/test.log @@ -0,0 +1 @@ +[gomitmproxy]2017/04/03 00:00:25 /Users/bao/program/go/gowork/gomitmproxy/src/vendor/mylog/my_log.go:20: [log test]