[tor-commits] [obfs4/master] transports/meeklite: Add `utls` argument to configure behavior

yawning at torproject.org yawning at torproject.org
Mon Jan 21 18:52:42 UTC 2019


commit e4020b18f7aaafe9f4cb345630bfe18a5e44a8d2
Author: Yawning Angel <yawning at 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() {



More information about the tor-commits mailing list