commit e4020b18f7aaafe9f4cb345630bfe18a5e44a8d2 Author: Yawning Angel yawning@schwanenlied.me Date: Mon Jan 21 18:47:42 2019 +0000
transports/meeklite: Add `utls` argument to configure behavior
Per dcf: > As for the TODO, my plan was was to expose a "utls" SOCKS arg > to make it configurable per bridge, and just reuse the utls > Client Hello ID names: > utls=HelloChrome_Auto
This adds support for all currently supported utls ClientHello IDs with the following caveats/differences:
* `none` - Disables using utls entirely, forces `crypto/tls`. * `HelloGolang` - Alias of `none`, since using utls is pointless. * `HelloCustom` - Omitted as pointless. --- transports/meeklite/meek.go | 21 ++++++++++++++- transports/meeklite/transport.go | 58 ++++++++++++++++++++++++++++++---------- 2 files changed, 64 insertions(+), 15 deletions(-)
diff --git a/transports/meeklite/meek.go b/transports/meeklite/meek.go index fb6aa0d..e009916 100644 --- a/transports/meeklite/meek.go +++ b/transports/meeklite/meek.go @@ -44,6 +44,8 @@ import ( "sync" "time"
+ utls "github.com/refraction-networking/utls" + "git.torproject.org/pluggable-transports/goptlib.git" "gitlab.com/yawning/obfs4.git/transports/base" ) @@ -51,6 +53,7 @@ import ( const ( urlArg = "url" frontArg = "front" + utlsArg = "utls"
maxChanBacklog = 16
@@ -73,6 +76,8 @@ var ( type meekClientArgs struct { url *gourl.URL front string + + utls *utls.ClientHelloID }
func (ca *meekClientArgs) Network() string { @@ -104,6 +109,12 @@ func newClientArgs(args *pt.Args) (ca *meekClientArgs, err error) { // Parse the (optional) front argument. ca.front, _ = args.Get(frontArg)
+ // Parse the (optional) utls argument. + utlsOpt, _ := args.Get(utlsArg) + if ca.utls, err = parseClientHelloID(utlsOpt); err != nil { + return nil, err + } + return ca, nil }
@@ -343,10 +354,18 @@ func newMeekConn(network, addr string, dialFn base.DialFunc, ca *meekClientArgs) return nil, err }
+ var rt http.RoundTripper + switch ca.utls { + case nil: + rt = &http.Transport{Dial: dialFn} + default: + rt = newRoundTripper(dialFn, ca.utls) + } + conn := &meekConn{ args: ca, sessionID: id, - roundTripper: newRoundTripper(dialFn), + roundTripper: rt, workerWrChan: make(chan []byte, maxChanBacklog), workerRdChan: make(chan []byte, maxChanBacklog), workerCloseChan: make(chan struct{}), diff --git a/transports/meeklite/transport.go b/transports/meeklite/transport.go index 6ccef31..59000a3 100644 --- a/transports/meeklite/transport.go +++ b/transports/meeklite/transport.go @@ -33,13 +33,35 @@ import ( "gitlab.com/yawning/obfs4.git/transports/base" )
-var errProtocolNegotiated = errors.New("meek_lite: protocol negotiated") +var ( + errProtocolNegotiated = errors.New("meek_lite: protocol negotiated") + + // This should be kept in sync with what is available in utls. + clientHelloIDMap = map[string]*utls.ClientHelloID{ + "hellogolang": nil, // Don't bother with utls. + "hellorandomized": &utls.HelloRandomized, + "hellorandomizedalpn": &utls.HelloRandomizedALPN, + "hellorandomizednoalpn": &utls.HelloRandomizedNoALPN, + "hellofirefox_auto": &utls.HelloFirefox_Auto, + "hellofirefox_55": &utls.HelloFirefox_55, + "hellofirefox_56": &utls.HelloFirefox_56, + "hellofirefox_63": &utls.HelloFirefox_63, + "hellochrome_auto": &utls.HelloChrome_Auto, + "hellochrome_58": &utls.HelloChrome_58, + "hellochrome_62": &utls.HelloChrome_62, + "hellochrome_70": &utls.HelloChrome_70, + "helloios_auto": &utls.HelloIOS_Auto, + "helloios_11_1": &utls.HelloIOS_11_1, + } + defaultClientHello = &utls.HelloChrome_Auto +)
type roundTripper struct { sync.Mutex
- transport http.RoundTripper - dialFn base.DialFunc + clientHelloID *utls.ClientHelloID + dialFn base.DialFunc + transport http.RoundTripper
initConn net.Conn } @@ -105,15 +127,7 @@ func (rt *roundTripper) dialTLS(network, addr string) (net.Conn, error) { host = addr }
- // TODO: Make this configurable. What "works" is host dependent. - // * HelloChrome_Auto - Failures in a stand alone testcase against google.com - // * HelloFirefox_Auto - Fails with the azure bridge, incompatible group. - // * HelloIOS_Auto - Seems to work. - // - // Since HelloChrome_Auto works with azure, that's what'll be used for - // now, since that's what the overwelming vast majority of people will - // use. - conn := utls.UClient(rawConn, &utls.Config{ServerName: host}, utls.HelloChrome_Auto) + conn := utls.UClient(rawConn, &utls.Config{ServerName: host}, *rt.clientHelloID) if err = conn.Handshake(); err != nil { conn.Close() return nil, err @@ -154,10 +168,26 @@ func getDialTLSAddr(u *url.URL) string { return net.JoinHostPort(u.Host, u.Scheme) }
-func newRoundTripper(dialFn base.DialFunc) http.RoundTripper { +func newRoundTripper(dialFn base.DialFunc, clientHelloID *utls.ClientHelloID) http.RoundTripper { return &roundTripper{ - dialFn: dialFn, + clientHelloID: clientHelloID, + dialFn: dialFn, + } +} + +func parseClientHelloID(s string) (*utls.ClientHelloID, error) { + s = strings.ToLower(s) + switch s { + case "none": + return nil, nil + case "": + return defaultClientHello, nil + default: + if ret := clientHelloIDMap[s]; ret != nil { + return ret, nil + } } + return nil, fmt.Errorf("invalid ClientHelloID: '%v'", s) }
func init() {
tor-commits@lists.torproject.org