[tor-commits] [meek/utls_2] Honor proxy even with http URLs in UTLSRoundTripper.

dcf at torproject.org dcf at torproject.org
Thu Feb 7 05:57:10 UTC 2019


commit cbd1cea4c22bc9f15311219ec2a0055f07a553b7
Author: David Fifield <david at bamsoftware.com>
Date:   Wed Feb 6 22:49:57 2019 -0700

    Honor proxy even with http URLs in UTLSRoundTripper.
    
    Formerly, RoundTrip simply deferred to the global httpRoundTripper (the
    one we would use if we were not using uTLS) for any http (as opposed to
    https) requests. However, we allow setting a proxy on UTLSRoundTripper
    that can be different from the proxy set on the global httpRoundTripper.
    
    In practice, the global httpRoundTripper probably *would* have the same
    proxy setting, just because of how the PT protocol only allows a single
    proxy and how we initialize everything together. But there's nothing at
    the UTLSRoundTripper layer that enforces that, so it's better if we
    don't assume it. UTLSRoundTripper is already slightly weird because of
    the rule about not mixing http/1.1 and h2; this change means that http
    URLs don't add any additional weirdness.
---
 meek-client/utls.go      | 18 +++++++++++++++--
 meek-client/utls_test.go | 51 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 67 insertions(+), 2 deletions(-)

diff --git a/meek-client/utls.go b/meek-client/utls.go
index 9a49317..a527d7e 100644
--- a/meek-client/utls.go
+++ b/meek-client/utls.go
@@ -120,13 +120,17 @@ type UTLSRoundTripper struct {
 	config        *utls.Config
 	proxyDialer   proxy.Dialer
 	rt            http.RoundTripper
+
+	// Transport for HTTP requests, which don't use uTLS.
+	httpRT *http.Transport
 }
 
 func (rt *UTLSRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
 	switch req.URL.Scheme {
 	case "http":
-		// If http, we don't invoke uTLS; just pass it to the global http.Transport.
-		return httpRoundTripper.RoundTrip(req)
+		// If http, we don't invoke uTLS; just pass it to an ordinary
+		// http.Transport.
+		return rt.httpRT.RoundTrip(req)
 	case "https":
 	default:
 		return nil, fmt.Errorf("unsupported URL scheme %q", req.URL.Scheme)
@@ -295,13 +299,23 @@ func NewUTLSRoundTripper(name string, cfg *utls.Config, proxyURL *url.URL) (http
 		// Special case for "none" and HelloGolang.
 		return httpRoundTripper, nil
 	}
+
 	proxyDialer, err := makeProxyDialer(proxyURL, cfg, clientHelloID)
 	if err != nil {
 		return nil, err
 	}
+
+	// This special-case RoundTripper is used for HTTP requests, which don't
+	// use uTLS but should use the specified proxy.
+	httpRT := &http.Transport{}
+	copyPublicFields(httpRT, httpRoundTripper)
+	httpRT.Proxy = http.ProxyURL(proxyURL)
+
 	return &UTLSRoundTripper{
 		clientHelloID: clientHelloID,
 		config:        cfg,
 		proxyDialer:   proxyDialer,
+		// rt will be set in the first call to RoundTrip.
+		httpRT: httpRT,
 	}, nil
 }
diff --git a/meek-client/utls_test.go b/meek-client/utls_test.go
index 2eb72af..eebb1db 100644
--- a/meek-client/utls_test.go
+++ b/meek-client/utls_test.go
@@ -230,3 +230,54 @@ func TestUTLSServerName(t *testing.T) {
 		t.Errorf("expected \"test.example\" server_name extension with given ServerName and hostname dial")
 	}
 }
+
+// Test that HTTP requests (which don't go through the uTLS code path) still use
+// any proxy that's configured on the UTLSRoundTripper.
+func TestUTLSHTTPWithProxy(t *testing.T) {
+	// Make a web server that we should *not* be able to reach.
+	server := &http.Server{
+		Handler: http.NotFoundHandler(),
+	}
+	serverLn, err := net.Listen("tcp", "127.0.0.1:0")
+	if err != nil {
+		panic(err)
+	}
+	defer serverLn.Close()
+	go server.Serve(serverLn)
+
+	// Make a non-functional proxy server.
+	proxyLn, err := net.Listen("tcp", "127.0.0.1:0")
+	if err != nil {
+		panic(err)
+	}
+	defer proxyLn.Close()
+	go func() {
+		for {
+			conn, err := proxyLn.Accept()
+			if err == nil {
+				conn.Close() // go away
+			}
+		}
+	}()
+
+	// Try to access the web server through the non-functional proxy.
+	for _, proxyURL := range []url.URL{
+		url.URL{Scheme: "socks5", Host: proxyLn.Addr().String()},
+		url.URL{Scheme: "http", Host: proxyLn.Addr().String()},
+		url.URL{Scheme: "https", Host: proxyLn.Addr().String()},
+	} {
+		rt, err := NewUTLSRoundTripper("HelloFirefox_63", &utls.Config{InsecureSkipVerify: true}, &proxyURL)
+		if err != nil {
+			panic(err)
+		}
+		fetchURL := url.URL{Scheme: "http", Host: serverLn.Addr().String()}
+		req, err := http.NewRequest("GET", fetchURL.String(), nil)
+		if err != nil {
+			panic(err)
+		}
+		_, err = rt.RoundTrip(req)
+		if err == nil {
+			t.Errorf("fetch of %s through %s proxy should have failed", &fetchURL, proxyURL.Scheme)
+		}
+	}
+}



More information about the tor-commits mailing list