commit 34f28e8273062c1f6f6fa60f7816dca63a191a5e Author: David Fifield david@bamsoftware.com Date: Sat Feb 2 01:52:10 2019 -0700
socks5 proxy support for uTLS. --- meek-client/meek-client.go | 2 +- meek-client/utls.go | 59 ++++++++++++++++++++++++++++++++++++---------- 2 files changed, 48 insertions(+), 13 deletions(-)
diff --git a/meek-client/meek-client.go b/meek-client/meek-client.go index 462eeb3..5cc889c 100644 --- a/meek-client/meek-client.go +++ b/meek-client/meek-client.go @@ -323,7 +323,7 @@ func handler(conn *pt.SocksConn) error { } info.RoundTripper = helperRoundTripper } else if utlsOK { - info.RoundTripper, err = NewUTLSRoundTripper(utlsName, nil) + info.RoundTripper, err = NewUTLSRoundTripper(utlsName, nil, options.ProxyURL) if err != nil { return err } diff --git a/meek-client/utls.go b/meek-client/utls.go index 7372c20..67cf104 100644 --- a/meek-client/utls.go +++ b/meek-client/utls.go @@ -45,6 +45,7 @@ import (
utls "github.com/refraction-networking/utls" "golang.org/x/net/http2" + "golang.org/x/net/proxy" )
// Copy the public fields (fields for which CanSet is true) from src to dst. @@ -85,12 +86,8 @@ func addrForDial(url *url.URL) (string, error) {
// Analogous to tls.Dial. Connect to the given address and initiate a TLS // handshake using the given ClientHelloID, returning the resulting connection. -func dialUTLS(network, addr string, cfg *utls.Config, clientHelloID *utls.ClientHelloID) (*utls.UConn, error) { - if options.ProxyURL != nil { - return nil, fmt.Errorf("no proxy allowed with uTLS") - } - - conn, err := net.Dial(network, addr) +func dialUTLS(network, addr string, cfg *utls.Config, clientHelloID *utls.ClientHelloID, forward proxy.Dialer) (*utls.UConn, error) { + conn, err := forward.Dial(network, addr) if err != nil { return nil, err } @@ -116,6 +113,7 @@ type UTLSRoundTripper struct {
clientHelloID *utls.ClientHelloID config *utls.Config + proxyDialer proxy.Dialer rt http.RoundTripper }
@@ -136,7 +134,7 @@ func (rt *UTLSRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) // On the first call, make an http.Transport or http2.Transport // as appropriate. var err error - rt.rt, err = makeRoundTripper(req.URL, rt.clientHelloID, rt.config) + rt.rt, err = makeRoundTripper(req.URL, rt.clientHelloID, rt.config, rt.proxyDialer) if err != nil { return nil, err } @@ -145,16 +143,48 @@ func (rt *UTLSRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) return rt.rt.RoundTrip(req) }
-func makeRoundTripper(url *url.URL, clientHelloID *utls.ClientHelloID, cfg *utls.Config) (http.RoundTripper, error) { +func makeProxyDialer(proxyURL *url.URL) (proxy.Dialer, error) { + var proxyDialer proxy.Dialer = proxy.Direct + if proxyURL == nil { + return proxyDialer, nil + } + + proxyAddr, err := addrForDial(proxyURL) + if err != nil { + return nil, err + } + + var auth *proxy.Auth + if userpass := proxyURL.User; userpass != nil { + auth = &proxy.Auth{ + User: userpass.Username(), + } + if password, ok := userpass.Password(); ok { + auth.Password = password + } + } + + switch proxyURL.Scheme { + case "socks5": + proxyDialer, err = proxy.SOCKS5("tcp", proxyAddr, auth, proxyDialer) + default: + return nil, fmt.Errorf("cannot use proxy scheme %q with uTLS", proxyURL.Scheme) + } + + return proxyDialer, err +} + +func makeRoundTripper(url *url.URL, clientHelloID *utls.ClientHelloID, cfg *utls.Config, proxyDialer proxy.Dialer) (http.RoundTripper, error) { addr, err := addrForDial(url) if err != nil { return nil, err }
- // Connect to the given address and initiate a TLS handshake using - // the given ClientHelloID. Return the resulting connection. + // Connect to the given address, through a proxy if requested, and + // initiate a TLS handshake using the given ClientHelloID. Return the + // resulting connection. dial := func(network, addr string) (*utls.UConn, error) { - return dialUTLS(network, addr, cfg, clientHelloID) + return dialUTLS(network, addr, cfg, clientHelloID, proxyDialer) }
bootstrapConn, err := dial("tcp", addr) @@ -237,7 +267,7 @@ var clientHelloIDMap = map[string]*utls.ClientHelloID{ "helloios_11_1": &utls.HelloIOS_11_1, }
-func NewUTLSRoundTripper(name string, cfg *utls.Config) (http.RoundTripper, error) { +func NewUTLSRoundTripper(name string, cfg *utls.Config, proxyURL *url.URL) (http.RoundTripper, error) { // Lookup is case-insensitive. clientHelloID, ok := clientHelloIDMap[strings.ToLower(name)] if !ok { @@ -247,8 +277,13 @@ func NewUTLSRoundTripper(name string, cfg *utls.Config) (http.RoundTripper, erro // Special case for "none" and HelloGolang. return httpRoundTripper, nil } + proxyDialer, err := makeProxyDialer(proxyURL) + if err != nil { + return nil, err + } return &UTLSRoundTripper{ clientHelloID: clientHelloID, config: cfg, + proxyDialer: proxyDialer, }, nil }