From 5a98c4447e8d3cb14e53b9ef15d6faa812aed226 Mon Sep 17 00:00:00 2001 From: David Fifield Date: Tue, 1 Jan 2019 22:12:00 -0700 Subject: [PATCH] First try at utls integration. Disables proxy support: I'm using http.Transport.DialTLS to hook into utls, and http.Transport.Proxy is ignored when DialTLS is set. We'll have to manually make our own proxy connections within DialTLS. It doesn't work properly yet because the HelloChrome_Auto fingerprint we're using includes "h2" in the ALPN extension, which causes the server to think it should use HTTP/2. The client side, however, uses HTTP/1.1 because it disable automatic HTTP/2 configuration when DialTLS is set. So you have an HTTP/1.1 client talking to an HTTP/2 server, and an error message like this: error in handling request: net/http: HTTP/1.x transport connection broken: malformed HTTP response "\x00\x00\x12\x04\x00\x00\x00\x00\x00\x00\x05\x00\x10\x00\x00\x00\x03\x00\x00\x00\xfa\x00\x06\x00\x10\x01@" As a workaround, I'm not calling UConn.Handshake() before returning the UConn from DialTLS. This seems to cause the caller to remove the ALPN extension, thereby negotiating HTTP/1.1 on both sides. The downside are the loss of HTTP/2, and the fact that a missing ALPN extension means our fingerprint doesn't match what it's supposed to. --- meek-client/meek-client.go | 41 ++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/meek-client/meek-client.go b/meek-client/meek-client.go index bbacbb4..5853428 100644 --- a/meek-client/meek-client.go +++ b/meek-client/meek-client.go @@ -48,6 +48,7 @@ import ( "time" "git.torproject.org/pluggable-transports/goptlib.git" + tls "github.com/refraction-networking/utls" ) const ( @@ -337,10 +338,7 @@ func acceptLoop(ln *pt.SocksListener) error { // configuration. func checkProxyURL(u *url.URL) error { if options.HelperAddr == nil { - // Without the helper we only support HTTP proxies. - if u.Scheme != "http" { - return fmt.Errorf("don't understand proxy URL scheme %q", u.Scheme) - } + return fmt.Errorf("no proxy allowed") } else { // With the helper we can use HTTP and SOCKS (because it is the // browser that does the proxying, not us). @@ -433,6 +431,41 @@ func main() { } } + httpTransport.DialContext = nil + httpTransport.Dial = func(network, addr string) (net.Conn, error) { + panic("disable plaintext") + } + httpTransport.DialTLS = func(network, addr string) (net.Conn, error) { + if httpTransport.TLSClientConfig != nil { + return nil, fmt.Errorf("error: non-nil TLSClientConfig") + } + config := &tls.Config{} + + colonPos := strings.LastIndex(addr, ":") + if colonPos == -1 { + colonPos = len(addr) + } + config.ServerName = addr[:colonPos] + + conn, err := net.Dial(network, addr) + if err != nil { + return nil, err + } + uconn := tls.UClient(conn, config, tls.HelloChrome_Auto) + // We cannot call uconn.Handshake() here: it causes the server + // to use HTTP/2, when the client is still using HTTP/1.1, + // because net/http disables automatic HTTP/2 support when using + // DialTLS. + // https://github.com/golang/go/issues/21753 + // "Auto-HTTP/2 is disabled by DialTLS being set" + // https://github.com/golang/go/issues/21336 + // But: returning without calling uconn.Handshake causes the + // ClientHello to lack the ALPN extension entirely... + // + // err = uconn.Handshake() + return uconn, err + } + listeners := make([]net.Listener, 0) for _, methodName := range ptInfo.MethodNames { switch methodName { -- GitLab