From bda22e47ee7c312c08ef4020d485f4a8c8b30544 Mon Sep 17 00:00:00 2001 From: listomebao <1421967301@qq.com> Date: Sat, 9 Jul 2016 19:40:23 +0800 Subject: [PATCH 01/10] =?UTF-8?q?=E4=BF=AE=E6=94=B9listome.com/log?= =?UTF-8?q?=E6=9C=AA=E5=8C=85=E5=90=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- genKey.go | 8 +++----- gomitmproxy.go | 1 - 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/genKey.go b/genKey.go index 2f961f5..0a54766 100644 --- a/genKey.go +++ b/genKey.go @@ -13,8 +13,6 @@ import ( "net" "os" "time" - - "listome.com/log" ) const ( @@ -87,7 +85,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 { - log.Debugf("Unable to close file: %v", err) + logger.Printf("Unable to close file: %v", err) } return } @@ -238,7 +236,7 @@ func (cert *Certificate) WriteToFile(filename string) (err error) { } defer func() { if err := certOut.Close(); err != nil { - log.Debugf("Unable to close file: %v", err) + logger.Printf("Unable to close file: %v", err) } }() return pem.Encode(certOut, cert.pemBlock()) @@ -266,7 +264,7 @@ func (cert *Certificate) WriteToDERFile(filename string) (err error) { } defer func() { if err := certOut.Close(); err != nil { - log.Debugf("Unable to close file: %v", err) + logger.Printf("Unable to close file: %v", err) } }() _, err = certOut.Write(cert.derBytes) diff --git a/gomitmproxy.go b/gomitmproxy.go index 8ba8add..c358b15 100644 --- a/gomitmproxy.go +++ b/gomitmproxy.go @@ -59,7 +59,6 @@ func gomitmproxy(conf *Cfg) { Handler: handler, ReadTimeout: 1 * time.Hour, WriteTimeout: 1 * time.Hour, - TLSConfig: tlsConfig.ServerTLSConfig, } go func() { From ddbecae506956ae3c2aa931255fce3cbb18b4de8 Mon Sep 17 00:00:00 2001 From: listomebao <1421967301@qq.com> Date: Thu, 14 Jul 2016 00:08:52 +0800 Subject: [PATCH 02/10] =?UTF-8?q?=E4=BF=AE=E6=AD=A3http=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E5=92=8Chttp=20post=E8=AF=B7=E6=B1=82=E7=9A=84=E6=8A=93?= =?UTF-8?q?=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dump.go | 14 +++ mitm.go | 346 ++++++++------------------------------------------------ 2 files changed, 63 insertions(+), 297 deletions(-) diff --git a/dump.go b/dump.go index d1a6290..e437a9c 100644 --- a/dump.go +++ b/dump.go @@ -36,7 +36,21 @@ func httpDump(req *http.Request, resp *http.Response) { for headerName, headerContext := range resp.Header { fmt.Printf("%s: %s\n", Blue(headerName), headerContext) } + if req.Method == "POST" { + fmt.Println(Green("URLEncoded form")) + err := req.ParseForm() + fmt.Printf("%#v\n", req.Form) + fmt.Printf("%#v\n", req.PostForm) + if err != nil { + logger.Println("parseForm error:", err) + } else { + for k, v := range req.Form { + fmt.Printf("%s: %s\n", Blue(k), v) + } + } + + } respBody, err := ioutil.ReadAll(resp.Body) if err != nil { logger.Println("func httpDump read resp body err:", err) diff --git a/mitm.go b/mitm.go index 29656cb..c358b15 100644 --- a/mitm.go +++ b/mitm.go @@ -1,332 +1,84 @@ +// This example shows a proxy server that uses go-mitm to man-in-the-middle +// HTTPS connections opened with CONNECT requests + package main import ( - "bufio" - "crypto/tls" - "errors" - "fmt" - "io" + "flag" "log" - "net" "net/http" - "net/http/httputil" - "net/url" - "regexp" - "strings" + "os" "sync" "time" ) -type HandlerWrapper struct { - MyConfig *Cfg - tlsConfig *TlsConfig - wrapped http.Handler - pk *PrivateKey - pkPem []byte - issuingCert *Certificate - issuingCertPem []byte - serverTLSConfig *tls.Config - dynamicCerts *Cache - certMutex sync.Mutex - https bool -} - -func (hw *HandlerWrapper) GenerateCertForClient() (err error) { - if hw.tlsConfig.Organization == "" { - hw.tlsConfig.Organization = "gomitmproxy" + Version - } - if hw.tlsConfig.CommonName == "" { - hw.tlsConfig.CommonName = "gomitmproxy" - } - if hw.pk, err = LoadPKFromFile(hw.tlsConfig.PrivateKeyFile); err != nil { - hw.pk, err = GeneratePK(2048) - if err != nil { - return fmt.Errorf("Unable to generate private key: %s", err) - } - hw.pk.WriteToFile(hw.tlsConfig.PrivateKeyFile) - } - hw.pkPem = hw.pk.PEMEncoded() - hw.issuingCert, err = LoadCertificateFromFile(hw.tlsConfig.CertFile) - if err != nil || hw.issuingCert.ExpiresBefore(time.Now().AddDate(0, ONE_MONTH, 0)) { - hw.issuingCert, err = hw.pk.TLSCertificateFor( - hw.tlsConfig.Organization, - hw.tlsConfig.CommonName, - time.Now().AddDate(ONE_YEAR, 0, 0), - true, - nil) - if err != nil { - return fmt.Errorf("Unable to generate self-signed issuing certificate: %s", err) - } - hw.issuingCert.WriteToFile(hw.tlsConfig.CertFile) - } - hw.issuingCertPem = hw.issuingCert.PEMEncoded() - return -} - -func (hw *HandlerWrapper) FakeCertForName(name string) (cert *tls.Certificate, err error) { - kpCandidateIf, found := hw.dynamicCerts.Get(name) - if found { - return kpCandidateIf.(*tls.Certificate), nil - } - - hw.certMutex.Lock() - defer hw.certMutex.Unlock() - kpCandidateIf, found = hw.dynamicCerts.Get(name) - if found { - return kpCandidateIf.(*tls.Certificate), nil - } - - //create certificate - certTTL := TWO_WEEKS - generatedCert, err := hw.pk.TLSCertificateFor( - hw.tlsConfig.Organization, - name, - time.Now().Add(certTTL), - false, - hw.issuingCert) - if err != nil { - return nil, fmt.Errorf("Unable to issue certificate: %s", err) - } - keyPair, err := tls.X509KeyPair(generatedCert.PEMEncoded(), hw.pkPem) - if err != nil { - return nil, fmt.Errorf("Unable to parse keypair for tls: %s", err) - } - - cacheTTL := certTTL - ONE_DAY - hw.dynamicCerts.Set(name, &keyPair, cacheTTL) - return &keyPair, nil -} - -func (hw *HandlerWrapper) DumpHTTPAndHTTPs(resp http.ResponseWriter, req *http.Request) { - req.Header.Del("Proxy-Connection") - req.Header.Set("Connection", "Keep-Alive") - - // handle connection - connIn, _, err := resp.(http.Hijacker).Hijack() - if err != nil { - logger.Println("hijack error:", err) - } - defer connIn.Close() - - var respOut *http.Response - host := req.Host - - matched, _ := regexp.MatchString(":[0-9]+$", host) - - if !hw.https { - if !matched { - host += ":80" - } - - connOut, err := net.DialTimeout("tcp", host, time.Second*30) - if err != nil { - logger.Println("dial to", host, "error:", err) - } - respOut, err = http.ReadResponse(bufio.NewReader(connOut), req) - if err != nil && err != io.EOF { - logger.Println("read response error:", err) - } - - } else { - if !matched { - host += ":443" - } - - 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 { - logger.Println("send to server error", err) - } +const ( + Version = "1.1" +) - respOut, err = http.ReadResponse(bufio.NewReader(connOut), req) - if err != nil && err != io.EOF { - logger.Println("read response error:", err) - } +var ( + wg sync.WaitGroup +) - } +var logFile *os.File +var logger *log.Logger - if respOut == nil { - log.Println("respOut is nil") - return - } +func main() { + var conf Cfg - respDump, err := httputil.DumpResponse(respOut, true) - if err != nil { - logger.Println("respDump error:", err) - } + 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() - _, err = connIn.Write(respDump) + var err error + logFile, err = os.Create(*conf.Log) if err != nil { - logger.Println("connIn write error:", err) + log.Fatalln("fail to create log file!") } - if *hw.MyConfig.Monitor { - go httpDump(req, respOut) - } + logger = log.New(logFile, "[gomitmproxy]", log.LstdFlags|log.Llongfile) + wg.Add(1) + gomitmproxy(&conf) + wg.Wait() } -func (hw *HandlerWrapper) ServeHTTP(resp http.ResponseWriter, req *http.Request) { +func gomitmproxy(conf *Cfg) { + tlsConfig := NewTlsConfig("gomitmproxy-ca-pk.pem", "gomitmproxy-ca-cert.pem", "", "") - raddr := *hw.MyConfig.Raddr - if len(raddr) != 0 { - hw.Forward(resp, req, raddr) - } else { - if req.Method == "CONNECT" { - hw.https = true - hw.InterceptHTTPs(resp, req) - } else { - hw.DumpHTTPAndHTTPs(resp, req) - } - } -} - -func (hw *HandlerWrapper) InterceptHTTPs(resp http.ResponseWriter, req *http.Request) { - addr := req.Host - host := strings.Split(addr, ":")[0] - - cert, err := hw.FakeCertForName(host) + handler, err := InitConfig(conf, tlsConfig) if err != nil { - msg := fmt.Sprintf("Could not get mitm cert for name: %s\nerror: %s", host, err) - respBadGateway(resp, msg) - return + logger.Fatalf("InitConfig error: %s", err) } - // handle connection - connIn, _, err := resp.(http.Hijacker).Hijack() - if err != nil { - msg := fmt.Sprintf("Unable to access underlying connection from client: %s", err) - respBadGateway(resp, msg) - return + server := &http.Server{ + Addr: ":" + *conf.Port, + Handler: handler, + ReadTimeout: 1 * time.Hour, + WriteTimeout: 1 * time.Hour, } - tlsConfig := copyTlsConfig(hw.tlsConfig.ServerTLSConfig) - tlsConfig.Certificates = []tls.Certificate{*cert} - tlsConnIn := tls.Server(connIn, tlsConfig) - listener := &mitmListener{tlsConnIn} - handler := http.HandlerFunc(func(resp2 http.ResponseWriter, req2 *http.Request) { - req2.URL.Scheme = "https" - req2.URL.Host = req2.Host - hw.DumpHTTPAndHTTPs(resp2, req2) - - }) go func() { - err = http.Serve(listener, handler) - if err != nil && err != io.EOF { - logger.Printf("Error serving mitm'ed connection: %s", err) - } - }() + log.Printf("proxy listening port:%s", *conf.Port) - connIn.Write([]byte("HTTP/1.1 200 OK\r\n\r\n")) -} - -func (hw *HandlerWrapper) Forward(resp http.ResponseWriter, req *http.Request, raddr string) { - connIn, _, err := resp.(http.Hijacker).Hijack() - connOut, err := net.Dial("tcp", raddr) - if err != nil { - logger.Println("dial tcp error", err) - } - - err = connectProxyServer(connOut, raddr) - if err != nil { - logger.Println("connectProxyServer error:", err) - } - - if req.Method == "CONNECT" { - b := []byte("HTTP/1.1 200 Connection Established\r\n" + - "Proxy-Agent: gomitmproxy/" + Version + "\r\n\r\n") - _, err := connIn.Write(b) - if err != nil { - logger.Println("Write Connect err:", err) - return + if *conf.Tls { + log.Println("ListenAndServeTLS") + err = server.ListenAndServeTLS("gomitmproxy-ca-cert.pem", "gomitmproxy-ca-pk.pem") + } else { + log.Println("ListenAndServe") + err = server.ListenAndServe() } - } 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) - return + if err != nil { + logger.Fatalf("Unable to start HTTP proxy: %s", err) } - } - err = Transport(connIn, connOut) - if err != nil { - log.Println("trans error ", err) - } -} -func InitConfig(conf *Cfg, tlsConfig *TlsConfig) (*HandlerWrapper, error) { - hw := &HandlerWrapper{ - MyConfig: conf, - tlsConfig: tlsConfig, - dynamicCerts: NewCache(), - } - err := hw.GenerateCertForClient() - if err != nil { - return nil, err - } - return hw, nil -} - -func copyTlsConfig(template *tls.Config) *tls.Config { - tlsConfig := &tls.Config{} - if template != nil { - *tlsConfig = *template - } - return tlsConfig -} + wg.Done() -func respBadGateway(resp http.ResponseWriter, msg string) { - log.Println(msg) - resp.WriteHeader(502) - resp.Write([]byte(msg)) -} - -//两个io口的连接 -func Transport(conn1, conn2 net.Conn) (err error) { - rChan := make(chan error, 1) - wChan := make(chan error, 1) - - go MyCopy(conn1, conn2, wChan) - go MyCopy(conn2, conn1, rChan) - - select { - case err = <-wChan: - case err = <-rChan: - } + log.Printf("gomitmproxy stop!!!!") + }() return } - -func MyCopy(src io.Reader, dst io.Writer, ch chan<- error) { - _, err := io.Copy(dst, src) - ch <- err -} - -func connectProxyServer(conn net.Conn, addr string) error { - - req := &http.Request{ - Method: "CONNECT", - URL: &url.URL{Host: addr}, - Host: addr, - ProtoMajor: 1, - ProtoMinor: 1, - Header: make(http.Header), - } - req.Header.Set("Proxy-Connection", "keep-alive") - - if err := req.Write(conn); err != nil { - return err - } - - resp, err := http.ReadResponse(bufio.NewReader(conn), req) - if err != nil { - return err - } - if resp.StatusCode != http.StatusOK { - return errors.New(resp.Status) - } - return nil -} From 6a999726d2e44f89a09baf8235a8874d1785cffd Mon Sep 17 00:00:00 2001 From: listomebao <1421967301@qq.com> Date: Thu, 14 Jul 2016 00:10:29 +0800 Subject: [PATCH 03/10] =?UTF-8?q?=E4=BF=AE=E6=AD=A3http=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E5=92=8Chttp=20post=E8=AF=B7=E6=B1=82=E7=9A=84=E6=8A=93?= =?UTF-8?q?=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mitm.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/mitm.go b/mitm.go index c358b15..6a1a8f2 100644 --- a/mitm.go +++ b/mitm.go @@ -1,6 +1,3 @@ -// This example shows a proxy server that uses go-mitm to man-in-the-middle -// HTTPS connections opened with CONNECT requests - package main import ( From 3d4555e1dfac8c5f306a3a4ef22c6797bc40aa34 Mon Sep 17 00:00:00 2001 From: listomebao <1421967301@qq.com> Date: Fri, 15 Jul 2016 00:03:15 +0800 Subject: [PATCH 04/10] =?UTF-8?q?=E6=9B=B4=E6=94=B9=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E7=9A=84mitm=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dump.go | 9 +- mitm.go | 353 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 312 insertions(+), 50 deletions(-) diff --git a/dump.go b/dump.go index e437a9c..ca261a1 100644 --- a/dump.go +++ b/dump.go @@ -32,10 +32,6 @@ func httpDump(req *http.Request, resp *http.Response) { 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) - } if req.Method == "POST" { fmt.Println(Green("URLEncoded form")) err := req.ParseForm() @@ -51,6 +47,11 @@ func httpDump(req *http.Request, resp *http.Response) { } } + 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) diff --git a/mitm.go b/mitm.go index 6a1a8f2..9f8a54e 100644 --- a/mitm.go +++ b/mitm.go @@ -1,81 +1,342 @@ package main import ( - "flag" + "bufio" + "crypto/tls" + "errors" + "fmt" + "io" "log" + "net" "net/http" - "os" + "net/http/httputil" + "net/url" + "regexp" + "strings" "sync" "time" ) -const ( - Version = "1.1" -) +type HandlerWrapper struct { + MyConfig *Cfg + tlsConfig *TlsConfig + wrapped http.Handler + pk *PrivateKey + pkPem []byte + issuingCert *Certificate + issuingCertPem []byte + serverTLSConfig *tls.Config + dynamicCerts *Cache + certMutex sync.Mutex + https bool +} -var ( - wg sync.WaitGroup -) +func (hw *HandlerWrapper) GenerateCertForClient() (err error) { + if hw.tlsConfig.Organization == "" { + hw.tlsConfig.Organization = "gomitmproxy" + Version + } + if hw.tlsConfig.CommonName == "" { + hw.tlsConfig.CommonName = "gomitmproxy" + } + if hw.pk, err = LoadPKFromFile(hw.tlsConfig.PrivateKeyFile); err != nil { + hw.pk, err = GeneratePK(2048) + if err != nil { + return fmt.Errorf("Unable to generate private key: %s", err) + } + hw.pk.WriteToFile(hw.tlsConfig.PrivateKeyFile) + } + hw.pkPem = hw.pk.PEMEncoded() + hw.issuingCert, err = LoadCertificateFromFile(hw.tlsConfig.CertFile) + if err != nil || hw.issuingCert.ExpiresBefore(time.Now().AddDate(0, ONE_MONTH, 0)) { + hw.issuingCert, err = hw.pk.TLSCertificateFor( + hw.tlsConfig.Organization, + hw.tlsConfig.CommonName, + time.Now().AddDate(ONE_YEAR, 0, 0), + true, + nil) + if err != nil { + return fmt.Errorf("Unable to generate self-signed issuing certificate: %s", err) + } + hw.issuingCert.WriteToFile(hw.tlsConfig.CertFile) + } + hw.issuingCertPem = hw.issuingCert.PEMEncoded() + return +} -var logFile *os.File -var logger *log.Logger +func (hw *HandlerWrapper) FakeCertForName(name string) (cert *tls.Certificate, err error) { + kpCandidateIf, found := hw.dynamicCerts.Get(name) + if found { + return kpCandidateIf.(*tls.Certificate), nil + } -func main() { - var conf Cfg + hw.certMutex.Lock() + defer hw.certMutex.Unlock() + kpCandidateIf, found = hw.dynamicCerts.Get(name) + if found { + return kpCandidateIf.(*tls.Certificate), nil + } - 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() + //create certificate + certTTL := TWO_WEEKS + generatedCert, err := hw.pk.TLSCertificateFor( + hw.tlsConfig.Organization, + name, + time.Now().Add(certTTL), + false, + hw.issuingCert) + if err != nil { + return nil, fmt.Errorf("Unable to issue certificate: %s", err) + } + keyPair, err := tls.X509KeyPair(generatedCert.PEMEncoded(), hw.pkPem) + if err != nil { + return nil, fmt.Errorf("Unable to parse keypair for tls: %s", err) + } - var err error - logFile, err = os.Create(*conf.Log) + cacheTTL := certTTL - ONE_DAY + hw.dynamicCerts.Set(name, &keyPair, cacheTTL) + return &keyPair, nil +} + +func (hw *HandlerWrapper) DumpHTTPAndHTTPs(resp http.ResponseWriter, req *http.Request) { + req.Header.Del("Proxy-Connection") + req.Header.Set("Connection", "Keep-Alive") + + // handle connection + connIn, _, err := resp.(http.Hijacker).Hijack() if err != nil { - log.Fatalln("fail to create log file!") + logger.Println("hijack error:", err) } + defer connIn.Close() - logger = log.New(logFile, "[gomitmproxy]", log.LstdFlags|log.Llongfile) + var respOut *http.Response + host := req.Host - wg.Add(1) - gomitmproxy(&conf) - wg.Wait() -} + matched, _ := regexp.MatchString(":[0-9]+$", host) + + if !hw.https { + if !matched { + host += ":80" + } + + connOut, err := net.DialTimeout("tcp", host, time.Second*30) + if err != nil { + logger.Println("dial to", host, "error:", err) + return + } + + if err = req.Write(connOut); err != nil { + logger.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) + } + + } else { + if !matched { + host += ":443" + } + + connOut, err := tls.Dial("tcp", host, hw.tlsConfig.ServerTLSConfig) + + if err != nil { + logger.Panicln("tls dial to", host, "error:", err) + return + } + if err = req.Write(connOut); err != nil { + logger.Println("send to server error", err) + return + } -func gomitmproxy(conf *Cfg) { - tlsConfig := NewTlsConfig("gomitmproxy-ca-pk.pem", "gomitmproxy-ca-cert.pem", "", "") + respOut, err = http.ReadResponse(bufio.NewReader(connOut), req) + if err != nil && err != io.EOF { + logger.Println("read response error:", err) + } + + } - handler, err := InitConfig(conf, tlsConfig) + if respOut == nil { + log.Println("respOut is nil") + return + } + + respDump, err := httputil.DumpResponse(respOut, true) if err != nil { - logger.Fatalf("InitConfig error: %s", err) + logger.Println("respDump error:", err) } - server := &http.Server{ - Addr: ":" + *conf.Port, - Handler: handler, - ReadTimeout: 1 * time.Hour, - WriteTimeout: 1 * time.Hour, + _, err = connIn.Write(respDump) + if err != nil { + logger.Println("connIn write error:", err) } - go func() { - log.Printf("proxy listening port:%s", *conf.Port) + if *hw.MyConfig.Monitor { + go httpDump(req, respOut) + } + +} - if *conf.Tls { - log.Println("ListenAndServeTLS") - err = server.ListenAndServeTLS("gomitmproxy-ca-cert.pem", "gomitmproxy-ca-pk.pem") +func (hw *HandlerWrapper) ServeHTTP(resp http.ResponseWriter, req *http.Request) { + + raddr := *hw.MyConfig.Raddr + if len(raddr) != 0 { + hw.Forward(resp, req, raddr) + } else { + if req.Method == "CONNECT" { + hw.https = true + hw.InterceptHTTPs(resp, req) } else { - log.Println("ListenAndServe") - err = server.ListenAndServe() + hw.https = false + hw.DumpHTTPAndHTTPs(resp, req) } + } +} + +func (hw *HandlerWrapper) InterceptHTTPs(resp http.ResponseWriter, req *http.Request) { + addr := req.Host + host := strings.Split(addr, ":")[0] + + cert, err := hw.FakeCertForName(host) + if err != nil { + msg := fmt.Sprintf("Could not get mitm cert for name: %s\nerror: %s", host, err) + respBadGateway(resp, msg) + return + } + + // handle connection + connIn, _, err := resp.(http.Hijacker).Hijack() + if err != nil { + msg := fmt.Sprintf("Unable to access underlying connection from client: %s", err) + respBadGateway(resp, msg) + return + } + tlsConfig := copyTlsConfig(hw.tlsConfig.ServerTLSConfig) + tlsConfig.Certificates = []tls.Certificate{*cert} + tlsConnIn := tls.Server(connIn, tlsConfig) + listener := &mitmListener{tlsConnIn} + handler := http.HandlerFunc(func(resp2 http.ResponseWriter, req2 *http.Request) { + req2.URL.Scheme = "https" + req2.URL.Host = req2.Host + hw.DumpHTTPAndHTTPs(resp2, req2) + + }) + + go func() { + err = http.Serve(listener, handler) + if err != nil && err != io.EOF { + logger.Printf("Error serving mitm'ed connection: %s", err) + } + }() + + connIn.Write([]byte("HTTP/1.1 200 OK\r\n\r\n")) +} + +func (hw *HandlerWrapper) Forward(resp http.ResponseWriter, req *http.Request, raddr string) { + connIn, _, err := resp.(http.Hijacker).Hijack() + connOut, err := net.Dial("tcp", raddr) + if err != nil { + logger.Println("dial tcp error", err) + } + + err = connectProxyServer(connOut, raddr) + if err != nil { + logger.Println("connectProxyServer error:", err) + } + + if req.Method == "CONNECT" { + b := []byte("HTTP/1.1 200 Connection Established\r\n" + + "Proxy-Agent: gomitmproxy/" + Version + "\r\n\r\n") + _, err := connIn.Write(b) if err != nil { - logger.Fatalf("Unable to start HTTP proxy: %s", err) + logger.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) + return } + } + err = Transport(connIn, connOut) + if err != nil { + log.Println("trans error ", err) + } +} - wg.Done() +func InitConfig(conf *Cfg, tlsConfig *TlsConfig) (*HandlerWrapper, error) { + hw := &HandlerWrapper{ + MyConfig: conf, + tlsConfig: tlsConfig, + dynamicCerts: NewCache(), + } + err := hw.GenerateCertForClient() + if err != nil { + return nil, err + } + return hw, nil +} - log.Printf("gomitmproxy stop!!!!") - }() +func copyTlsConfig(template *tls.Config) *tls.Config { + tlsConfig := &tls.Config{} + if template != nil { + *tlsConfig = *template + } + return tlsConfig +} + +func respBadGateway(resp http.ResponseWriter, msg string) { + log.Println(msg) + resp.WriteHeader(502) + resp.Write([]byte(msg)) +} + +//两个io口的连接 +func Transport(conn1, conn2 net.Conn) (err error) { + rChan := make(chan error, 1) + wChan := make(chan error, 1) + + go MyCopy(conn1, conn2, wChan) + go MyCopy(conn2, conn1, rChan) + + select { + case err = <-wChan: + case err = <-rChan: + } return } + +func MyCopy(src io.Reader, dst io.Writer, ch chan<- error) { + _, err := io.Copy(dst, src) + ch <- err +} + +func connectProxyServer(conn net.Conn, addr string) error { + + req := &http.Request{ + Method: "CONNECT", + URL: &url.URL{Host: addr}, + Host: addr, + ProtoMajor: 1, + ProtoMinor: 1, + Header: make(http.Header), + } + req.Header.Set("Proxy-Connection", "keep-alive") + + if err := req.Write(conn); err != nil { + return err + } + + resp, err := http.ReadResponse(bufio.NewReader(conn), req) + if err != nil { + return err + } + if resp.StatusCode != http.StatusOK { + return errors.New(resp.Status) + } + return nil +} From 1c14528edc3f5e64f630fa3ea4e3baaca8bc39ba Mon Sep 17 00:00:00 2001 From: zhengbaoyang Date: Wed, 27 Jul 2016 18:06:05 +0800 Subject: [PATCH 05/10] =?UTF-8?q?=E5=B0=9D=E8=AF=95post=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E7=9A=84=E6=8A=93=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dump.go | 11 ++--------- mitm.go | 4 ++++ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/dump.go b/dump.go index ca261a1..5629329 100644 --- a/dump.go +++ b/dump.go @@ -34,16 +34,9 @@ func httpDump(req *http.Request, resp *http.Response) { } if req.Method == "POST" { fmt.Println(Green("URLEncoded form")) - err := req.ParseForm() - fmt.Printf("%#v\n", req.Form) - fmt.Printf("%#v\n", req.PostForm) - if err != nil { - logger.Println("parseForm error:", err) - } else { - for k, v := range req.Form { - fmt.Printf("%s: %s\n", Blue(k), v) - } + for k, v := range req.Form { + fmt.Printf("%s: %s\n", Blue(k), v) } } diff --git a/mitm.go b/mitm.go index 9f8a54e..edcb998 100644 --- a/mitm.go +++ b/mitm.go @@ -179,7 +179,11 @@ func (hw *HandlerWrapper) DumpHTTPAndHTTPs(resp http.ResponseWriter, req *http.R } func (hw *HandlerWrapper) ServeHTTP(resp http.ResponseWriter, req *http.Request) { + err := req.ParseForm() + if err != nil { + logger.Println("parseForm error:", err) + } raddr := *hw.MyConfig.Raddr if len(raddr) != 0 { hw.Forward(resp, req, raddr) From c5e10c274be457e148f3e7e0c346fd1815a2a355 Mon Sep 17 00:00:00 2001 From: zhengbaoyang Date: Mon, 8 Aug 2016 10:21:27 +0800 Subject: [PATCH 06/10] develog --- dump.go | 33 ++++++++++++++++++++++++++------- mitm.go | 29 ++++++++++++++++++++++++----- 2 files changed, 50 insertions(+), 12 deletions(-) diff --git a/dump.go b/dump.go index 5629329..4a458f0 100644 --- a/dump.go +++ b/dump.go @@ -12,7 +12,7 @@ import ( "strconv" ) -func httpDump(req *http.Request, resp *http.Response) { +func httpDump(reqDump []byte, resp *http.Response) { defer resp.Body.Close() var respStatusStr string respStatus := resp.StatusCode @@ -27,19 +27,33 @@ func httpDump(req *http.Request, resp *http.Response) { case 5: respStatusStr = Red("<--" + strconv.Itoa(respStatus)) } - fmt.Println(Green("Request:")) - fmt.Printf("%s %s %s\n", Blue(req.Method), req.RequestURI, respStatusStr) + fmt.Println(Green("Request:"), respStatusStr) + ParseReq(reqDump) + /*req, _ := ParseReq(reqDump) + for k, v := range req { + fmt.Println(k, ":::::", v) + }*/ + /*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) - } - if req.Method == "POST" { + }*/ + /*if req.Method == "POST" { fmt.Println(Green("URLEncoded form")) - for k, v := range req.Form { + err := req.ParseForm() + if err != nil { + logger.Println("parseForm error:", err) + } else { + for k, v := range req.Form { + fmt.Printf("%s: %s\n", Blue(k), v) + } + } + values, _ := ParsePostValues(reqDump) + for k, v := range values { fmt.Printf("%s: %s\n", Blue(k), v) } - } + }*/ fmt.Println(Green("Response:")) for headerName, headerContext := range resp.Header { fmt.Printf("%s: %s\n", Blue(headerName), headerContext) @@ -77,3 +91,8 @@ func httpDump(req *http.Request, resp *http.Response) { fmt.Printf("%s%s%s\n", Black("####################"), Cyan("END"), Black("####################")) } + +func ParseReq(b []byte) error { + str := string(b) + +} diff --git a/mitm.go b/mitm.go index edcb998..4817d3a 100644 --- a/mitm.go +++ b/mitm.go @@ -102,6 +102,10 @@ func (hw *HandlerWrapper) DumpHTTPAndHTTPs(resp http.ResponseWriter, req *http.R req.Header.Set("Connection", "Keep-Alive") // handle connection + reqDump, err := httputil.DumpRequest(req, true) + if err != nil { + logger.Println("DumpRequest error ", err) + } connIn, _, err := resp.(http.Hijacker).Hijack() if err != nil { logger.Println("hijack error:", err) @@ -173,17 +177,13 @@ func (hw *HandlerWrapper) DumpHTTPAndHTTPs(resp http.ResponseWriter, req *http.R } if *hw.MyConfig.Monitor { - go httpDump(req, respOut) + go httpDump(reqDump, respOut) } } func (hw *HandlerWrapper) ServeHTTP(resp http.ResponseWriter, req *http.Request) { - err := req.ParseForm() - if err != nil { - logger.Println("parseForm error:", err) - } raddr := *hw.MyConfig.Raddr if len(raddr) != 0 { hw.Forward(resp, req, raddr) @@ -344,3 +344,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 +} +*/ From a990253ee4b464fe7d72208f1823db61bf2bd944 Mon Sep 17 00:00:00 2001 From: zhengbaoyang Date: Tue, 23 Aug 2016 18:30:13 +0800 Subject: [PATCH 07/10] try catch post --- dump.go | 59 ++++++++++++++++++++++++++++++-------------------- gomitmproxy.go | 4 ++++ 2 files changed, 40 insertions(+), 23 deletions(-) diff --git a/dump.go b/dump.go index 4a458f0..b7f5cee 100644 --- a/dump.go +++ b/dump.go @@ -6,6 +6,7 @@ import ( "compress/flate" "compress/gzip" "fmt" + "io" "io/ioutil" "math" "net/http" @@ -28,32 +29,38 @@ func httpDump(reqDump []byte, resp *http.Response) { respStatusStr = Red("<--" + strconv.Itoa(respStatus)) } fmt.Println(Green("Request:"), respStatusStr) - ParseReq(reqDump) - /*req, _ := ParseReq(reqDump) - for k, v := range req { - fmt.Println(k, ":::::", v) - }*/ - /*fmt.Printf("%s %s %s\n", Blue(req.Method), req.RequestURI, respStatusStr) + req, _ := ParseReq(reqDump) + // for k, v := range req { + // fmt.Println(k, ":::::", v) + // } + 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) - }*/ - /*if req.Method == "POST" { - fmt.Println(Green("URLEncoded form")) + } - err := req.ParseForm() + fmt.Printf("req:---->%#v\n", req) + if req.Method == "POST" { + fmt.Println(Green("URLEncoded form")) + fmt.Println(reqDump) + body, err := ioutil.ReadAll(req.Body) if err != nil { - logger.Println("parseForm error:", err) - } else { - for k, v := range req.Form { - fmt.Printf("%s: %s\n", Blue(k), v) - } - } - values, _ := ParsePostValues(reqDump) - for k, v := range values { - fmt.Printf("%s: %s\n", Blue(k), v) + fmt.Println("readall ", err) } + fmt.Println(string(body)) + // err := req.ParseForm() + // if err != nil { + // logger.Println("parseForm error:", err) + // } else { + // for k, v := range req.Form { + // fmt.Printf("%s: %s\n", Blue(k), v) + // } + // } + // values, _ := ParsePostValues(reqDump) + // for k, v := range values { + // fmt.Printf("%s: %s\n", Blue(k), v) + // } - }*/ + } fmt.Println(Green("Response:")) for headerName, headerContext := range resp.Header { fmt.Printf("%s: %s\n", Blue(headerName), headerContext) @@ -92,7 +99,13 @@ func httpDump(reqDump []byte, resp *http.Response) { fmt.Printf("%s%s%s\n", Black("####################"), Cyan("END"), Black("####################")) } -func ParseReq(b []byte) error { - str := string(b) - +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/gomitmproxy.go b/gomitmproxy.go index c358b15..7641918 100644 --- a/gomitmproxy.go +++ b/gomitmproxy.go @@ -31,8 +31,12 @@ func main() { 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") + help := flag.Bool("h", false, "help") flag.Parse() + if *help { + flag.PrintDefaults() + } var err error logFile, err = os.Create(*conf.Log) if err != nil { From baf79e0b3fb09353dea7c07c4c4f2ea0fc3dabc4 Mon Sep 17 00:00:00 2001 From: zhengbaoyang Date: Tue, 23 Aug 2016 19:37:11 +0800 Subject: [PATCH 08/10] =?UTF-8?q?http=20post=E8=AF=B7=E6=B1=82=E7=9A=84?= =?UTF-8?q?=E6=8A=93=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dump.go | 31 ++++++++----------------------- mitm.go | 11 ++++++++++- 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/dump.go b/dump.go index b7f5cee..da5c07f 100644 --- a/dump.go +++ b/dump.go @@ -30,36 +30,21 @@ func httpDump(reqDump []byte, resp *http.Response) { } fmt.Println(Green("Request:"), respStatusStr) req, _ := ParseReq(reqDump) - // for k, v := range req { - // fmt.Println(k, ":::::", v) - // } - fmt.Printf("%s %s %s\n", Blue(req.Method), req.RequestURI, respStatusStr) + fmt.Printf("%s %s %s\n", Blue(req.Method), req.Host+req.RequestURI, respStatusStr) for headerName, headerContext := range req.Header { fmt.Printf("%s: %s\n", Blue(headerName), headerContext) } - fmt.Printf("req:---->%#v\n", req) if req.Method == "POST" { - fmt.Println(Green("URLEncoded form")) - fmt.Println(reqDump) - body, err := ioutil.ReadAll(req.Body) + fmt.Println(Green("POST Param:")) + err := req.ParseForm() if err != nil { - fmt.Println("readall ", err) + logger.Println("parseForm error:", err) + } else { + for k, v := range req.Form { + fmt.Printf("\t%s: %s\n", Blue(k), v) + } } - fmt.Println(string(body)) - // err := req.ParseForm() - // if err != nil { - // logger.Println("parseForm error:", err) - // } else { - // for k, v := range req.Form { - // fmt.Printf("%s: %s\n", Blue(k), v) - // } - // } - // values, _ := ParsePostValues(reqDump) - // for k, v := range values { - // fmt.Printf("%s: %s\n", Blue(k), v) - // } - } fmt.Println(Green("Response:")) for headerName, headerContext := range resp.Header { diff --git a/mitm.go b/mitm.go index 4817d3a..bbd8337 100644 --- a/mitm.go +++ b/mitm.go @@ -101,8 +101,14 @@ func (hw *HandlerWrapper) DumpHTTPAndHTTPs(resp http.ResponseWriter, req *http.R req.Header.Del("Proxy-Connection") req.Header.Set("Connection", "Keep-Alive") + var reqDump []byte + var err error + ch := make(chan bool) // handle connection - reqDump, err := httputil.DumpRequest(req, true) + go func() { + reqDump, err = httputil.DumpRequestOut(req, true) + ch <- true + }() if err != nil { logger.Println("DumpRequest error ", err) } @@ -177,7 +183,10 @@ func (hw *HandlerWrapper) DumpHTTPAndHTTPs(resp http.ResponseWriter, req *http.R } if *hw.MyConfig.Monitor { + <-ch go httpDump(reqDump, respOut) + } else { + <-ch } } From 44b8b2480ca29bd223f77817ce25200426b1c39c Mon Sep 17 00:00:00 2001 From: zhengbaoyang Date: Tue, 23 Aug 2016 19:43:24 +0800 Subject: [PATCH 09/10] removeaddr --- dump.go | 1 + 1 file changed, 1 insertion(+) diff --git a/dump.go b/dump.go index da5c07f..b1045f0 100644 --- a/dump.go +++ b/dump.go @@ -31,6 +31,7 @@ func httpDump(reqDump []byte, resp *http.Response) { fmt.Println(Green("Request:"), respStatusStr) req, _ := ParseReq(reqDump) fmt.Printf("%s %s %s\n", Blue(req.Method), req.Host+req.RequestURI, respStatusStr) + fmt.Printf("%s %s\n", Blue("RemoteAddr:"), req.RemoteAddr) for headerName, headerContext := range req.Header { fmt.Printf("%s: %s\n", Blue(headerName), headerContext) } From d132001d326016706580221e51b952d3310667dd Mon Sep 17 00:00:00 2001 From: sheepbao <331541471@qq.com> Date: Mon, 3 Apr 2017 00:24:48 +0800 Subject: [PATCH 10/10] =?UTF-8?q?=E9=87=8D=E6=9E=84=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.sh | 2 + gomitmproxy.go | 84 --------------------- readme.md | 4 +- goproxy.png => src/doc/goproxy.png | Bin proxy.png => src/doc/proxy.png | Bin src/main/main.go | 41 ++++++++++ tcolor.go => src/vendor/color/tcolor.go | 2 +- config.go => src/vendor/config/config.go | 14 +--- cache.go => src/vendor/mitm/cache.go | 2 +- dump.go => src/vendor/mitm/dump.go | 32 ++++---- genKey.go => src/vendor/mitm/genKey.go | 9 ++- src/vendor/mitm/gomitmproxy.go | 48 ++++++++++++ listener.go => src/vendor/mitm/listener.go | 2 +- mitm.go => src/vendor/mitm/mitm.go | 69 ++++++++++------- src/vendor/mylog/my_log.go | 30 ++++++++ src/vendor/mylog/my_log_test.go | 17 +++++ src/vendor/mylog/test.log | 1 + 17 files changed, 211 insertions(+), 146 deletions(-) create mode 100755 build.sh delete mode 100644 gomitmproxy.go rename goproxy.png => src/doc/goproxy.png (100%) rename proxy.png => src/doc/proxy.png (100%) create mode 100644 src/main/main.go rename tcolor.go => src/vendor/color/tcolor.go (99%) rename config.go => src/vendor/config/config.go (87%) rename cache.go => src/vendor/mitm/cache.go (98%) rename dump.go => src/vendor/mitm/dump.go (57%) rename genKey.go => src/vendor/mitm/genKey.go (98%) create mode 100644 src/vendor/mitm/gomitmproxy.go rename listener.go => src/vendor/mitm/listener.go (96%) rename mitm.go => src/vendor/mitm/mitm.go (84%) create mode 100644 src/vendor/mylog/my_log.go create mode 100644 src/vendor/mylog/my_log_test.go create mode 100644 src/vendor/mylog/test.log 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/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/dump.go b/src/vendor/mitm/dump.go similarity index 57% rename from dump.go rename to src/vendor/mitm/dump.go index 5629329..e19536f 100644 --- a/dump.go +++ b/src/vendor/mitm/dump.go @@ -1,13 +1,15 @@ -package main +package mitm import ( "bufio" "bytes" + "color" "compress/flate" "compress/gzip" "fmt" "io/ioutil" "math" + "mylog" "net/http" "strconv" ) @@ -19,35 +21,35 @@ func httpDump(req *http.Request, resp *http.Response) { respStatusHeader := int(math.Floor(float64(respStatus / 100))) switch respStatusHeader { case 2: - respStatusStr = Green("<--" + strconv.Itoa(respStatus)) + respStatusStr = color.Green("<--" + strconv.Itoa(respStatus)) case 3: - respStatusStr = Yellow("<--" + strconv.Itoa(respStatus)) + respStatusStr = color.Yellow("<--" + strconv.Itoa(respStatus)) case 4: - respStatusStr = Magenta("<--" + strconv.Itoa(respStatus)) + respStatusStr = color.Magenta("<--" + strconv.Itoa(respStatus)) case 5: - respStatusStr = Red("<--" + strconv.Itoa(respStatus)) + respStatusStr = color.Red("<--" + strconv.Itoa(respStatus)) } - fmt.Println(Green("Request:")) - fmt.Printf("%s %s %s\n", Blue(req.Method), req.RequestURI, respStatusStr) + fmt.Println(color.Green("Request:")) + fmt.Printf("%s %s %s\n", color.Blue(req.Method), req.RequestURI, respStatusStr) for headerName, headerContext := range req.Header { - fmt.Printf("%s: %s\n", Blue(headerName), headerContext) + fmt.Printf("%s: %s\n", color.Blue(headerName), headerContext) } if req.Method == "POST" { - fmt.Println(Green("URLEncoded form")) + fmt.Println(color.Green("URLEncoded form")) for k, v := range req.Form { - fmt.Printf("%s: %s\n", Blue(k), v) + fmt.Printf("%s: %s\n", color.Blue(k), v) } } - fmt.Println(Green("Response:")) + fmt.Println(color.Green("Response:")) for headerName, headerContext := range resp.Header { - fmt.Printf("%s: %s\n", Blue(headerName), headerContext) + fmt.Printf("%s: %s\n", color.Blue(headerName), headerContext) } respBody, err := ioutil.ReadAll(resp.Body) if err != nil { - logger.Println("func httpDump read resp body err:", err) + mylog.Println("func httpDump read resp body err:", err) } else { acceptEncode := resp.Header["Content-Encoding"] var respBodyBin bytes.Buffer @@ -59,7 +61,7 @@ func httpDump(req *http.Request, resp *http.Response) { case "gzip": r, err := gzip.NewReader(&respBodyBin) if err != nil { - logger.Println("gzip reader err:", err) + mylog.Println("gzip reader err:", err) } else { defer r.Close() respBody, _ = ioutil.ReadAll(r) @@ -75,5 +77,5 @@ func httpDump(req *http.Request, resp *http.Response) { fmt.Printf("%s\n", string(respBody)) } - fmt.Printf("%s%s%s\n", Black("####################"), Cyan("END"), Black("####################")) + fmt.Printf("%s%s%s\n", color.Black("####################"), color.Cyan("END"), color.Black("####################")) } 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 84% rename from mitm.go rename to src/vendor/mitm/mitm.go index edcb998..c82805a 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,16 @@ 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") + reqTmp := copyHTTPRequest(req) + err := reqTmp.ParseForm() + if err != nil { + mylog.Println("parseForm error:", err) + } // handle connection connIn, _, err := resp.(http.Hijacker).Hijack() if err != nil { - logger.Println("hijack error:", err) + mylog.Println("hijack error:", err) } defer connIn.Close() @@ -120,18 +135,18 @@ 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 { @@ -142,17 +157,17 @@ 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) + mylog.Panicln("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,26 +179,21 @@ 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) + go httpDump(reqTmp, respOut) } - } func (hw *HandlerWrapper) ServeHTTP(resp http.ResponseWriter, req *http.Request) { - err := req.ParseForm() - if err != nil { - logger.Println("parseForm error:", err) - } raddr := *hw.MyConfig.Raddr if len(raddr) != 0 { hw.Forward(resp, req, raddr) @@ -230,7 +240,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) } }() @@ -241,12 +251,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" { @@ -254,24 +264,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, @@ -292,6 +302,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) @@ -320,7 +338,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}, 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]