Skip to content
Snippets Groups Projects
Commit e4020b18 authored by Yawning Angel's avatar Yawning Angel
Browse files

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.
parent 4d453dab
No related branches found
No related tags found
No related merge requests found
......@@ -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{}),
......
......@@ -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() {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment