From d229c9f93d398482a428b3081ca7c1c1aa9ac966 Mon Sep 17 00:00:00 2001 From: Philip Hamer Date: Tue, 16 Nov 2021 23:26:29 -0500 Subject: [PATCH 1/9] try https proxy --- proxy.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/proxy.go b/proxy.go index e87a8c9..26bbd0f 100644 --- a/proxy.go +++ b/proxy.go @@ -6,6 +6,7 @@ package websocket import ( "bufio" + "crypto/tls" "encoding/base64" "errors" "net" @@ -20,10 +21,21 @@ func (fn netDialerFunc) Dial(network, addr string) (net.Conn, error) { return fn(network, addr) } +type tlsDialer struct { +} + +func (t *tlsDialer) Dial(network, addr string) (c net.Conn, err error) { + return tls.DialWithDialer(&net.Dialer{}, network, addr, &tls.Config{}) +} + func init() { proxy_RegisterDialerType("http", func(proxyURL *url.URL, forwardDialer proxy_Dialer) (proxy_Dialer, error) { return &httpProxyDialer{proxyURL: proxyURL, forwardDial: forwardDialer.Dial}, nil }) + proxy_RegisterDialerType("https", func(proxyURL *url.URL, forwardDialer proxy_Dialer) (proxy_Dialer, error) { + dialer := &tlsDialer{} + return &httpProxyDialer{proxyURL: proxyURL, forwardDial: dialer.Dial}, nil + }) } type httpProxyDialer struct { From 2a082eee69a1bf414564f1971cfe1a5553b65dde Mon Sep 17 00:00:00 2001 From: Philip Hamer Date: Mon, 29 Nov 2021 14:19:00 -0500 Subject: [PATCH 2/9] simplify proxying with tls proxy --- client.go | 8 ++++++++ proxy.go | 11 +---------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/client.go b/client.go index c4b62fb..23678e3 100644 --- a/client.go +++ b/client.go @@ -274,6 +274,14 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h return nil, nil, err } if proxyURL != nil { + if proxyURL.Scheme == "https" { + netDial = func(network, addr string) (net.Conn, error) { + t := tls.Dialer{} + t.Config = d.TLSClientConfig + t.NetDialer = &net.Dialer{} + return t.DialContext(ctx, network, addr) + } + } dialer, err := proxy_FromURL(proxyURL, netDialerFunc(netDial)) if err != nil { return nil, nil, err diff --git a/proxy.go b/proxy.go index 26bbd0f..f84d881 100644 --- a/proxy.go +++ b/proxy.go @@ -6,7 +6,6 @@ package websocket import ( "bufio" - "crypto/tls" "encoding/base64" "errors" "net" @@ -21,20 +20,12 @@ func (fn netDialerFunc) Dial(network, addr string) (net.Conn, error) { return fn(network, addr) } -type tlsDialer struct { -} - -func (t *tlsDialer) Dial(network, addr string) (c net.Conn, err error) { - return tls.DialWithDialer(&net.Dialer{}, network, addr, &tls.Config{}) -} - func init() { proxy_RegisterDialerType("http", func(proxyURL *url.URL, forwardDialer proxy_Dialer) (proxy_Dialer, error) { return &httpProxyDialer{proxyURL: proxyURL, forwardDial: forwardDialer.Dial}, nil }) proxy_RegisterDialerType("https", func(proxyURL *url.URL, forwardDialer proxy_Dialer) (proxy_Dialer, error) { - dialer := &tlsDialer{} - return &httpProxyDialer{proxyURL: proxyURL, forwardDial: dialer.Dial}, nil + return &httpProxyDialer{proxyURL: proxyURL, forwardDial: forwardDialer.Dial}, nil }) } From 7f3a5bcae0faa3e6ef1882e8f0db575f03b8f34f Mon Sep 17 00:00:00 2001 From: Philip Hamer Date: Mon, 29 Nov 2021 14:49:47 -0500 Subject: [PATCH 3/9] make it more intuitive for tls proxy --- client.go | 6 ++++-- proxy.go | 31 ++++++++++++++++++++++++++----- x_net_proxy.go | 14 ++++++++++++++ 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/client.go b/client.go index 23678e3..efcdc5d 100644 --- a/client.go +++ b/client.go @@ -274,15 +274,17 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h return nil, nil, err } if proxyURL != nil { + proxyDialer := &netDialerFunc{fn: netDial} if proxyURL.Scheme == "https" { - netDial = func(network, addr string) (net.Conn, error) { + proxyDialer.usesTLS = true + proxyDialer.fn = func(network, addr string) (net.Conn, error) { t := tls.Dialer{} t.Config = d.TLSClientConfig t.NetDialer = &net.Dialer{} return t.DialContext(ctx, network, addr) } } - dialer, err := proxy_FromURL(proxyURL, netDialerFunc(netDial)) + dialer, err := proxy_FromURL(proxyURL, proxyDialer) if err != nil { return nil, nil, err } diff --git a/proxy.go b/proxy.go index f84d881..c332633 100644 --- a/proxy.go +++ b/proxy.go @@ -6,6 +6,7 @@ package websocket import ( "bufio" + "crypto/tls" "encoding/base64" "errors" "net" @@ -14,24 +15,40 @@ import ( "strings" ) -type netDialerFunc func(network, addr string) (net.Conn, error) +type netDialerFunc struct { + fn func(network, addr string) (net.Conn, error) + usesTLS bool +} -func (fn netDialerFunc) Dial(network, addr string) (net.Conn, error) { - return fn(network, addr) +func (ndf *netDialerFunc) Dial(network, addr string) (net.Conn, error) { + return ndf.fn(network, addr) +} + +func (ndf *netDialerFunc) UsesTLS() bool { + return ndf.usesTLS } func init() { proxy_RegisterDialerType("http", func(proxyURL *url.URL, forwardDialer proxy_Dialer) (proxy_Dialer, error) { - return &httpProxyDialer{proxyURL: proxyURL, forwardDial: forwardDialer.Dial}, nil + return &httpProxyDialer{proxyURL: proxyURL, forwardDial: forwardDialer.Dial, usesTLS: forwardDialer.UsesTLS()}, nil }) proxy_RegisterDialerType("https", func(proxyURL *url.URL, forwardDialer proxy_Dialer) (proxy_Dialer, error) { - return &httpProxyDialer{proxyURL: proxyURL, forwardDial: forwardDialer.Dial}, nil + fwd := forwardDialer.Dial + if !forwardDialer.UsesTLS() { + tlsDialer := &tls.Dialer{ + Config: &tls.Config{}, + NetDialer: &net.Dialer{}, + } + fwd = tlsDialer.Dial + } + return &httpProxyDialer{proxyURL: proxyURL, forwardDial: fwd, usesTLS: true}, nil }) } type httpProxyDialer struct { proxyURL *url.URL forwardDial func(network, addr string) (net.Conn, error) + usesTLS bool } func (hpd *httpProxyDialer) Dial(network string, addr string) (net.Conn, error) { @@ -78,3 +95,7 @@ func (hpd *httpProxyDialer) Dial(network string, addr string) (net.Conn, error) } return conn, nil } + +func (hpd *httpProxyDialer) UsesTLS() bool { + return hpd.usesTLS +} diff --git a/x_net_proxy.go b/x_net_proxy.go index 2e668f6..f05a113 100644 --- a/x_net_proxy.go +++ b/x_net_proxy.go @@ -27,6 +27,10 @@ func (proxy_direct) Dial(network, addr string) (net.Conn, error) { return net.Dial(network, addr) } +func (proxy_direct) UsesTLS() bool { + return false +} + // A PerHost directs connections to a default Dialer unless the host name // requested matches one of a number of exceptions. type proxy_PerHost struct { @@ -59,6 +63,10 @@ func (p *proxy_PerHost) Dial(network, addr string) (c net.Conn, err error) { return p.dialerForRequest(host).Dial(network, addr) } +func (p *proxy_PerHost) UsesTLS() bool { + return p.def.UsesTLS() || p.bypass.UsesTLS() +} + func (p *proxy_PerHost) dialerForRequest(host string) proxy_Dialer { if ip := net.ParseIP(host); ip != nil { for _, net := range p.bypassNetworks { @@ -161,6 +169,8 @@ func (p *proxy_PerHost) AddHost(host string) { type proxy_Dialer interface { // Dial connects to the given address via the proxy. Dial(network, addr string) (c net.Conn, err error) + // UsesTLS indicates whether we expect to dial to a TLS proxy + UsesTLS() bool } // Auth contains authentication parameters that specific Dialers may require. @@ -338,6 +348,10 @@ func (s *proxy_socks5) Dial(network, addr string) (net.Conn, error) { return conn, nil } +func (s *proxy_socks5) UsesTLS() bool { + return s.forward.UsesTLS() +} + // connect takes an existing connection to a socks5 proxy server, // and commands the server to extend that connection to target, // which must be a canonical address with a host and port. From f724bd6a6c789dd719b46cec00a635561b76b450 Mon Sep 17 00:00:00 2001 From: Philip Hamer Date: Fri, 3 Dec 2021 15:59:00 -0500 Subject: [PATCH 4/9] do not edit the generated x_net_proxy.go --- proxy.go | 11 +++++++++-- x_net_proxy.go | 14 -------------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/proxy.go b/proxy.go index c332633..e9a4cd0 100644 --- a/proxy.go +++ b/proxy.go @@ -15,6 +15,13 @@ import ( "strings" ) +// // proxyDialerEx extends the generated proxy_Dialer +type proxyDialerEx interface { + proxy_Dialer + // UsesTLS indicates whether we expect to dial to a TLS proxy + UsesTLS() bool +} + type netDialerFunc struct { fn func(network, addr string) (net.Conn, error) usesTLS bool @@ -30,11 +37,11 @@ func (ndf *netDialerFunc) UsesTLS() bool { func init() { proxy_RegisterDialerType("http", func(proxyURL *url.URL, forwardDialer proxy_Dialer) (proxy_Dialer, error) { - return &httpProxyDialer{proxyURL: proxyURL, forwardDial: forwardDialer.Dial, usesTLS: forwardDialer.UsesTLS()}, nil + return &httpProxyDialer{proxyURL: proxyURL, forwardDial: forwardDialer.Dial, usesTLS: false}, nil }) proxy_RegisterDialerType("https", func(proxyURL *url.URL, forwardDialer proxy_Dialer) (proxy_Dialer, error) { fwd := forwardDialer.Dial - if !forwardDialer.UsesTLS() { + if dialerEx, ok := forwardDialer.(proxyDialerEx); !ok || !dialerEx.UsesTLS() { tlsDialer := &tls.Dialer{ Config: &tls.Config{}, NetDialer: &net.Dialer{}, diff --git a/x_net_proxy.go b/x_net_proxy.go index f05a113..2e668f6 100644 --- a/x_net_proxy.go +++ b/x_net_proxy.go @@ -27,10 +27,6 @@ func (proxy_direct) Dial(network, addr string) (net.Conn, error) { return net.Dial(network, addr) } -func (proxy_direct) UsesTLS() bool { - return false -} - // A PerHost directs connections to a default Dialer unless the host name // requested matches one of a number of exceptions. type proxy_PerHost struct { @@ -63,10 +59,6 @@ func (p *proxy_PerHost) Dial(network, addr string) (c net.Conn, err error) { return p.dialerForRequest(host).Dial(network, addr) } -func (p *proxy_PerHost) UsesTLS() bool { - return p.def.UsesTLS() || p.bypass.UsesTLS() -} - func (p *proxy_PerHost) dialerForRequest(host string) proxy_Dialer { if ip := net.ParseIP(host); ip != nil { for _, net := range p.bypassNetworks { @@ -169,8 +161,6 @@ func (p *proxy_PerHost) AddHost(host string) { type proxy_Dialer interface { // Dial connects to the given address via the proxy. Dial(network, addr string) (c net.Conn, err error) - // UsesTLS indicates whether we expect to dial to a TLS proxy - UsesTLS() bool } // Auth contains authentication parameters that specific Dialers may require. @@ -348,10 +338,6 @@ func (s *proxy_socks5) Dial(network, addr string) (net.Conn, error) { return conn, nil } -func (s *proxy_socks5) UsesTLS() bool { - return s.forward.UsesTLS() -} - // connect takes an existing connection to a socks5 proxy server, // and commands the server to extend that connection to target, // which must be a canonical address with a host and port. From 2553869a2901e485070e758e9d1bb6fa9277d190 Mon Sep 17 00:00:00 2001 From: Philip Hamer Date: Mon, 6 Dec 2021 09:30:42 -0500 Subject: [PATCH 5/9] clean up comment --- proxy.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy.go b/proxy.go index e9a4cd0..c66b46c 100644 --- a/proxy.go +++ b/proxy.go @@ -15,7 +15,7 @@ import ( "strings" ) -// // proxyDialerEx extends the generated proxy_Dialer +// proxyDialerEx extends the generated proxy_Dialer type proxyDialerEx interface { proxy_Dialer // UsesTLS indicates whether we expect to dial to a TLS proxy From d16969baa130b799f99b0b13417013b515726479 Mon Sep 17 00:00:00 2001 From: Philip Hamer Date: Mon, 6 Dec 2021 10:29:48 -0500 Subject: [PATCH 6/9] add unit test for https proxy --- client_server_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/client_server_test.go b/client_server_test.go index 5fd2c85..116b043 100644 --- a/client_server_test.go +++ b/client_server_test.go @@ -185,6 +185,47 @@ func TestProxyDial(t *testing.T) { sendRecv(t, ws) } +func TestHttpsProxy(t *testing.T) { + + sTLS := newTLSServer(t) + defer sTLS.Close() + s := newServer(t) + defer s.Close() + + surlTLS, _ := url.Parse(sTLS.Server.URL) + + cstDialer := cstDialer // make local copy for modification on next line. + cstDialer.Proxy = http.ProxyURL(surlTLS) + + connect := false + origHandler := sTLS.Server.Config.Handler + + // Capture the request Host header. + sTLS.Server.Config.Handler = http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + if r.Method == "CONNECT" { + connect = true + w.WriteHeader(http.StatusOK) + return + } + + if !connect { + t.Log("connect not received") + http.Error(w, "connect not received", http.StatusMethodNotAllowed) + return + } + origHandler.ServeHTTP(w, r) + }) + + cstDialer.TLSClientConfig = &tls.Config{RootCAs: rootCAs(t, sTLS.Server)} + ws, _, err := cstDialer.Dial(s.URL, nil) + if err != nil { + t.Fatalf("Dial: %v", err) + } + defer ws.Close() + sendRecv(t, ws) +} + func TestProxyAuthorizationDial(t *testing.T) { s := newServer(t) defer s.Close() From b484a6e5a0a3cd8fe48314ff7afa6b7d09af4b61 Mon Sep 17 00:00:00 2001 From: Philip Hamer Date: Mon, 6 Dec 2021 18:49:39 -0500 Subject: [PATCH 7/9] try compatibility with pre 1.15 as noop --- client.go | 10 +------ client_server_httpsproxy_test.go | 51 ++++++++++++++++++++++++++++++++ client_server_test.go | 41 ------------------------- proxy.go | 13 +------- proxy_https.go | 36 ++++++++++++++++++++++ proxy_https_legacy.go | 15 ++++++++++ 6 files changed, 104 insertions(+), 62 deletions(-) create mode 100644 client_server_httpsproxy_test.go create mode 100644 proxy_https.go create mode 100644 proxy_https_legacy.go diff --git a/client.go b/client.go index efcdc5d..ce07329 100644 --- a/client.go +++ b/client.go @@ -275,15 +275,7 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h } if proxyURL != nil { proxyDialer := &netDialerFunc{fn: netDial} - if proxyURL.Scheme == "https" { - proxyDialer.usesTLS = true - proxyDialer.fn = func(network, addr string) (net.Conn, error) { - t := tls.Dialer{} - t.Config = d.TLSClientConfig - t.NetDialer = &net.Dialer{} - return t.DialContext(ctx, network, addr) - } - } + modifyProxyDialer(ctx, d, proxyURL, proxyDialer) dialer, err := proxy_FromURL(proxyURL, proxyDialer) if err != nil { return nil, nil, err diff --git a/client_server_httpsproxy_test.go b/client_server_httpsproxy_test.go new file mode 100644 index 0000000..214ce33 --- /dev/null +++ b/client_server_httpsproxy_test.go @@ -0,0 +1,51 @@ +// +build go1.15 + +package websocket + +import ( + "crypto/tls" + "net/http" + "net/url" + "testing" +) + +func TestHttpsProxy(t *testing.T) { + + sTLS := newTLSServer(t) + defer sTLS.Close() + s := newServer(t) + defer s.Close() + + surlTLS, _ := url.Parse(sTLS.Server.URL) + + cstDialer := cstDialer // make local copy for modification on next line. + cstDialer.Proxy = http.ProxyURL(surlTLS) + + connect := false + origHandler := sTLS.Server.Config.Handler + + // Capture the request Host header. + sTLS.Server.Config.Handler = http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + if r.Method == "CONNECT" { + connect = true + w.WriteHeader(http.StatusOK) + return + } + + if !connect { + t.Log("connect not received") + http.Error(w, "connect not received", http.StatusMethodNotAllowed) + return + } + origHandler.ServeHTTP(w, r) + }) + + cstDialer.TLSClientConfig = &tls.Config{RootCAs: rootCAs(t, sTLS.Server)} + ws, _, err := cstDialer.Dial(s.URL, nil) + if err != nil { + t.Fatalf("Dial: %v", err) + } + defer ws.Close() + sendRecv(t, ws) +} diff --git a/client_server_test.go b/client_server_test.go index 116b043..5fd2c85 100644 --- a/client_server_test.go +++ b/client_server_test.go @@ -185,47 +185,6 @@ func TestProxyDial(t *testing.T) { sendRecv(t, ws) } -func TestHttpsProxy(t *testing.T) { - - sTLS := newTLSServer(t) - defer sTLS.Close() - s := newServer(t) - defer s.Close() - - surlTLS, _ := url.Parse(sTLS.Server.URL) - - cstDialer := cstDialer // make local copy for modification on next line. - cstDialer.Proxy = http.ProxyURL(surlTLS) - - connect := false - origHandler := sTLS.Server.Config.Handler - - // Capture the request Host header. - sTLS.Server.Config.Handler = http.HandlerFunc( - func(w http.ResponseWriter, r *http.Request) { - if r.Method == "CONNECT" { - connect = true - w.WriteHeader(http.StatusOK) - return - } - - if !connect { - t.Log("connect not received") - http.Error(w, "connect not received", http.StatusMethodNotAllowed) - return - } - origHandler.ServeHTTP(w, r) - }) - - cstDialer.TLSClientConfig = &tls.Config{RootCAs: rootCAs(t, sTLS.Server)} - ws, _, err := cstDialer.Dial(s.URL, nil) - if err != nil { - t.Fatalf("Dial: %v", err) - } - defer ws.Close() - sendRecv(t, ws) -} - func TestProxyAuthorizationDial(t *testing.T) { s := newServer(t) defer s.Close() diff --git a/proxy.go b/proxy.go index c66b46c..8abf01d 100644 --- a/proxy.go +++ b/proxy.go @@ -6,7 +6,6 @@ package websocket import ( "bufio" - "crypto/tls" "encoding/base64" "errors" "net" @@ -39,17 +38,7 @@ func init() { proxy_RegisterDialerType("http", func(proxyURL *url.URL, forwardDialer proxy_Dialer) (proxy_Dialer, error) { return &httpProxyDialer{proxyURL: proxyURL, forwardDial: forwardDialer.Dial, usesTLS: false}, nil }) - proxy_RegisterDialerType("https", func(proxyURL *url.URL, forwardDialer proxy_Dialer) (proxy_Dialer, error) { - fwd := forwardDialer.Dial - if dialerEx, ok := forwardDialer.(proxyDialerEx); !ok || !dialerEx.UsesTLS() { - tlsDialer := &tls.Dialer{ - Config: &tls.Config{}, - NetDialer: &net.Dialer{}, - } - fwd = tlsDialer.Dial - } - return &httpProxyDialer{proxyURL: proxyURL, forwardDial: fwd, usesTLS: true}, nil - }) + registerDialerHttps() } type httpProxyDialer struct { diff --git a/proxy_https.go b/proxy_https.go new file mode 100644 index 0000000..3cc2baa --- /dev/null +++ b/proxy_https.go @@ -0,0 +1,36 @@ +// +build go1.15 + +package websocket + +import ( + "context" + "crypto/tls" + "net" + "net/url" +) + +func registerDialerHttps() { + proxy_RegisterDialerType("https", func(proxyURL *url.URL, forwardDialer proxy_Dialer) (proxy_Dialer, error) { + fwd := forwardDialer.Dial + if dialerEx, ok := forwardDialer.(proxyDialerEx); !ok || !dialerEx.UsesTLS() { + tlsDialer := &tls.Dialer{ + Config: &tls.Config{}, + NetDialer: &net.Dialer{}, + } + fwd = tlsDialer.Dial + } + return &httpProxyDialer{proxyURL: proxyURL, forwardDial: fwd, usesTLS: true}, nil + }) +} + +func modifyProxyDialer(ctx context.Context, d *Dialer, proxyURL *url.URL, proxyDialer *netDialerFunc) { + if proxyURL.Scheme == "https" { + proxyDialer.usesTLS = true + proxyDialer.fn = func(network, addr string) (net.Conn, error) { + t := tls.Dialer{} + t.Config = d.TLSClientConfig + t.NetDialer = &net.Dialer{} + return t.DialContext(ctx, network, addr) + } + } +} diff --git a/proxy_https_legacy.go b/proxy_https_legacy.go new file mode 100644 index 0000000..2c8595c --- /dev/null +++ b/proxy_https_legacy.go @@ -0,0 +1,15 @@ +// +build !go1.15 + +package websocket + +import ( + "context" + "net/url" +) + +func registerDialerHttps() { +} + +func modifyProxyDialer(ctx context.Context, d *Dialer, proxyURL *url.URL, proxyDialer *netDialerFunc) { + return nil +} From bb146cd3fdc56ff65dd55368b8f4660dae7157b4 Mon Sep 17 00:00:00 2001 From: Philip Hamer Date: Mon, 6 Dec 2021 18:52:57 -0500 Subject: [PATCH 8/9] fix build error --- proxy_https_legacy.go | 1 - 1 file changed, 1 deletion(-) diff --git a/proxy_https_legacy.go b/proxy_https_legacy.go index 2c8595c..461339e 100644 --- a/proxy_https_legacy.go +++ b/proxy_https_legacy.go @@ -11,5 +11,4 @@ func registerDialerHttps() { } func modifyProxyDialer(ctx context.Context, d *Dialer, proxyURL *url.URL, proxyDialer *netDialerFunc) { - return nil } From 444f3c080f867017ed7b99f06558e5cbbf58041b Mon Sep 17 00:00:00 2001 From: Philip Hamer Date: Thu, 17 Feb 2022 13:00:00 -0500 Subject: [PATCH 9/9] gofmt --- client_server_httpsproxy_test.go | 1 + proxy_https.go | 1 + proxy_https_legacy.go | 1 + 3 files changed, 3 insertions(+) diff --git a/client_server_httpsproxy_test.go b/client_server_httpsproxy_test.go index 214ce33..71f0d55 100644 --- a/client_server_httpsproxy_test.go +++ b/client_server_httpsproxy_test.go @@ -1,3 +1,4 @@ +//go:build go1.15 // +build go1.15 package websocket diff --git a/proxy_https.go b/proxy_https.go index 3cc2baa..f74258a 100644 --- a/proxy_https.go +++ b/proxy_https.go @@ -1,3 +1,4 @@ +//go:build go1.15 // +build go1.15 package websocket diff --git a/proxy_https_legacy.go b/proxy_https_legacy.go index 461339e..40bc5e2 100644 --- a/proxy_https_legacy.go +++ b/proxy_https_legacy.go @@ -1,3 +1,4 @@ +//go:build !go1.15 // +build !go1.15 package websocket