AdguardTeam / gomitmproxy

Simple golang mitm proxy implementation
https://adguard.com/
GNU General Public License v3.0
293 stars 59 forks source link

routines:CONNECT_CR_SRVR_HELLO:wrong version number #2

Open domyway opened 4 years ago

domyway commented 4 years ago

HTTP proxy OnRequest change the body . but get error info :

routines:CONNECT_CR_SRVR_HELLO:wrong version number

if I dont change anything, all fine.
need some help thanks.

ameshkov commented 4 years ago

Could you please show your code?

domyway commented 4 years ago
func main() {
    proxy := gomitmproxy.NewProxy(gomitmproxy.Config{
        // TLSConfig: tlsConfig,
        ListenAddr: &net.TCPAddr{
            IP:   net.IPv4(0, 0, 0, 0),
            Port: 8080,
        },
        OnRequest: func(session *gomitmproxy.Session) (request *http.Request, response *http.Response) {
            req := session.Request()

            log.Printf("onRequest: %s %s", req.Method, req.URL.String())

            body := strings.NewReader("<html><body><h1>Replaced response</h1></body></html>")
            res := proxyutil.NewResponse(http.StatusOK, body, req)
            log.Printf("onRequest: %s %s %+v", req.Method, req.URL.String(), res)

            if req.URL.Host == "www.google.com:443" {
                body := strings.NewReader("<html><body><h1>Replaced response</h1></body></html>")
                res := proxyutil.NewResponse(http.StatusOK, body, req)
                res.Header.Set("Content-Type", "text/html")

                // Use session props to pass the information about request being blocked
                // session.SetProp("blocked", true)
                return nil, res
            }

            return nil, nil
        },
        OnResponse: func(session *gomitmproxy.Session) *http.Response {
            log.Printf("onResponse: %s", session.Request().URL.String())

            if _, ok := session.GetProp("blocked"); ok {
                log.Printf("onResponse: was blocked")
            }

            res := session.Response()
            req := session.Request()

            if strings.Index(res.Header.Get("Content-Type"), "text/html") != 0 {
                // Do nothing with non-HTML responses
                return nil
            }

            b, err := proxyutil.ReadDecompressedBody(res)
            // Close the original body
            _ = res.Body.Close()
            if err != nil {
                return proxyutil.NewErrorResponse(req, err)
            }

            // Use latin1 before modifying the body
            // Using this 1-byte encoding will let us preserve all original characters
            // regardless of what exactly is the encoding
            body, err := proxyutil.DecodeLatin1(bytes.NewReader(b))
            if err != nil {
                return proxyutil.NewErrorResponse(session.Request(), err)
            }

            // Modifying the original body
            modifiedBody, err := proxyutil.EncodeLatin1(body + "<!-- EDITED -->")
            if err != nil {
                return proxyutil.NewErrorResponse(session.Request(), err)
            }

            res.Body = ioutil.NopCloser(bytes.NewReader(modifiedBody))
            res.Header.Del("Content-Encoding")
            // res.ContentLength = int64(len(modifiedBody))
            log.Printf("onResponse: %+v", res)
            return res
        },
    })
    err := proxy.Start()
    if err != nil {
        log.Fatal(err)
    }

    signalChannel := make(chan os.Signal, 1)
    signal.Notify(signalChannel, syscall.SIGINT, syscall.SIGTERM)
    <-signalChannel

    // Clean up
    proxy.Close()
}

export https_proxy=127.0.0.1:8080
export http_proxy=127.0.0.1:8080

curl -v https://www.google.com   
szolin commented 4 years ago

CONNECT www.google.com:443 HTTP/1.1

curl sends a CONNECT method request to your app, then it sends TLS handshake data, but receives

body := strings.NewReader("<html><body><h1>Replaced response</h1></body></html>")

back. HTML data is not a valid TLS server handshake data!

domyway commented 4 years ago

how can i do to change the https response body ?

I trace the code if I dont change the body, then app goes to handleConnect(session *Session) finally

session.res = proxyutil.NewResponse(http.StatusOK, nil, session.req)

then

err = p.writeResponse(session)

It looks like the same thing what the OnRequest Func do

body := strings.NewReader("<html><body><h1>Replaced response</h1></body></html>")
res := proxyutil.NewResponse(http.StatusOK, body, req)

// then go here
p.writeResponse(session)