From cfbfc1221a2f12a875842563859b337a922c105c Mon Sep 17 00:00:00 2001 From: Brian Dwyer Date: Thu, 7 Nov 2019 23:57:23 -0500 Subject: [PATCH] Bump goproxy for Websocket support --- go.mod | 2 +- go.sum | 4 +- vendor/github.com/elazarl/goproxy/https.go | 7 +- vendor/github.com/elazarl/goproxy/proxy.go | 5 + .../github.com/elazarl/goproxy/websocket.go | 121 ++++++++++++++++++ vendor/modules.txt | 2 +- 6 files changed, 136 insertions(+), 5 deletions(-) create mode 100644 vendor/github.com/elazarl/goproxy/websocket.go diff --git a/go.mod b/go.mod index f4ae284..9912fe2 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,6 @@ go 1.13 require ( github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5 github.com/bhendo/concord v0.0.0-20180530163943-7f15987d4520 - github.com/elazarl/goproxy v0.0.0-20190911111923-ecfe977594f1 + github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484 golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd ) diff --git a/go.sum b/go.sum index db24d32..f11d54f 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5 h1:P5U+E4x5OkVEK github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5/go.mod h1:976q2ETgjT2snVCf2ZaBnyBbVoPERGjUz+0sofzEfro= github.com/bhendo/concord v0.0.0-20180530163943-7f15987d4520 h1:n4NdU6snY/KrOQ9O2czyAuwRD6TUzUOIYZIG+ftEs4g= github.com/bhendo/concord v0.0.0-20180530163943-7f15987d4520/go.mod h1:GNf3/5IyUGgjg6NdDGajgwhYj/QbJaBpVoBqoP4V7DI= -github.com/elazarl/goproxy v0.0.0-20190911111923-ecfe977594f1 h1:yY9rWGoXv1U5pl4gxqlULARMQD7x0QG85lqEXTWysik= -github.com/elazarl/goproxy v0.0.0-20190911111923-ecfe977594f1/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= +github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484 h1:pEtiCjIXx3RvGjlUJuCNxNOw0MNblyR9Wi+vJGBFh+8= +github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 h1:dWB6v3RcOy03t/bUadywsbyrQwCqZeNIEX6M1OtSZOM= github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= diff --git a/vendor/github.com/elazarl/goproxy/https.go b/vendor/github.com/elazarl/goproxy/https.go index 12de751..7c425c5 100644 --- a/vendor/github.com/elazarl/goproxy/https.go +++ b/vendor/github.com/elazarl/goproxy/https.go @@ -209,6 +209,11 @@ func (proxy *ProxyHttpServer) handleHttps(w http.ResponseWriter, r *http.Request req, resp := proxy.filterRequest(req, ctx) if resp == nil { + if isWebSocketRequest(req) { + ctx.Logf("Request looks like websocket upgrade.") + proxy.serveWebsocketTLS(ctx, w, req, tlsConfig, rawClientTls) + return + } if err != nil { ctx.Warnf("Illegal URL %s", "https://"+r.Host+req.URL.Path) return @@ -362,7 +367,7 @@ func (proxy *ProxyHttpServer) NewConnectDialToProxyWithHandler(https_proxy strin return c, nil } } - if u.Scheme == "https" { + if u.Scheme == "https" || u.Scheme == "wss" { if strings.IndexRune(u.Host, ':') == -1 { u.Host += ":443" } diff --git a/vendor/github.com/elazarl/goproxy/proxy.go b/vendor/github.com/elazarl/goproxy/proxy.go index 2509f6d..b6eb20d 100644 --- a/vendor/github.com/elazarl/goproxy/proxy.go +++ b/vendor/github.com/elazarl/goproxy/proxy.go @@ -113,6 +113,11 @@ func (proxy *ProxyHttpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) r, resp := proxy.filterRequest(r, ctx) if resp == nil { + if isWebSocketRequest(r) { + ctx.Logf("Request looks like websocket upgrade.") + proxy.serveWebsocket(ctx, w, r) + } + removeProxyHeaders(ctx, r) resp, err = ctx.RoundTrip(r) if err != nil { diff --git a/vendor/github.com/elazarl/goproxy/websocket.go b/vendor/github.com/elazarl/goproxy/websocket.go new file mode 100644 index 0000000..2a96991 --- /dev/null +++ b/vendor/github.com/elazarl/goproxy/websocket.go @@ -0,0 +1,121 @@ +package goproxy + +import ( + "bufio" + "crypto/tls" + "io" + "net/http" + "net/url" + "strings" +) + +func headerContains(header http.Header, name string, value string) bool { + for _, v := range header[name] { + for _, s := range strings.Split(v, ",") { + if strings.EqualFold(value, strings.TrimSpace(s)) { + return true + } + } + } + return false +} + +func isWebSocketRequest(r *http.Request) bool { + return headerContains(r.Header, "Connection", "upgrade") && + headerContains(r.Header, "Upgrade", "websocket") +} + +func (proxy *ProxyHttpServer) serveWebsocketTLS(ctx *ProxyCtx, w http.ResponseWriter, req *http.Request, tlsConfig *tls.Config, clientConn *tls.Conn) { + targetURL := url.URL{Scheme: "wss", Host: req.URL.Host, Path: req.URL.Path} + + // Connect to upstream + targetConn, err := tls.Dial("tcp", targetURL.Host, tlsConfig) + if err != nil { + ctx.Warnf("Error dialing target site: %v", err) + return + } + defer targetConn.Close() + + // Perform handshake + if err := proxy.websocketHandshake(ctx, req, targetConn, clientConn); err != nil { + ctx.Warnf("Websocket handshake error: %v", err) + return + } + + // Proxy wss connection + proxy.proxyWebsocket(ctx, targetConn, clientConn) +} + +func (proxy *ProxyHttpServer) serveWebsocket(ctx *ProxyCtx, w http.ResponseWriter, req *http.Request) { + targetURL := url.URL{Scheme: "ws", Host: req.URL.Host, Path: req.URL.Path} + + targetConn, err := proxy.connectDial("tcp", targetURL.Host) + if err != nil { + ctx.Warnf("Error dialing target site: %v", err) + return + } + defer targetConn.Close() + + // Connect to Client + hj, ok := w.(http.Hijacker) + if !ok { + panic("httpserver does not support hijacking") + } + clientConn, _, err := hj.Hijack() + if err != nil { + ctx.Warnf("Hijack error: %v", err) + return + } + + // Perform handshake + if err := proxy.websocketHandshake(ctx, req, targetConn, clientConn); err != nil { + ctx.Warnf("Websocket handshake error: %v", err) + return + } + + // Proxy ws connection + proxy.proxyWebsocket(ctx, targetConn, clientConn) +} + +func (proxy *ProxyHttpServer) websocketHandshake(ctx *ProxyCtx, req *http.Request, targetSiteConn io.ReadWriter, clientConn io.ReadWriter) error { + // write handshake request to target + err := req.Write(targetSiteConn) + if err != nil { + ctx.Warnf("Error writing upgrade request: %v", err) + return err + } + + targetTLSReader := bufio.NewReader(targetSiteConn) + + // Read handshake response from target + resp, err := http.ReadResponse(targetTLSReader, req) + if err != nil { + ctx.Warnf("Error reading handhsake response %v", err) + return err + } + + // Run response through handlers + resp = proxy.filterResponse(resp, ctx) + + // Proxy handshake back to client + err = resp.Write(clientConn) + if err != nil { + ctx.Warnf("Error writing handshake response: %v", err) + return err + } + return nil +} + +func (proxy *ProxyHttpServer) proxyWebsocket(ctx *ProxyCtx, dest io.ReadWriter, source io.ReadWriter) { + errChan := make(chan error, 2) + cp := func(dst io.Writer, src io.Reader) { + _, err := io.Copy(dst, src) + ctx.Warnf("Websocket error: %v", err) + errChan <- err + } + + // Start proxying websocket data + go cp(dest, source) + go cp(source, dest) + <-errChan +} diff --git a/vendor/modules.txt b/vendor/modules.txt index e4d6751..cb962e0 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -4,7 +4,7 @@ github.com/alexbrainman/sspi/ntlm # github.com/bhendo/concord v0.0.0-20180530163943-7f15987d4520 github.com/bhendo/concord github.com/bhendo/concord/handshakers -# github.com/elazarl/goproxy v0.0.0-20190911111923-ecfe977594f1 +# github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484 github.com/elazarl/goproxy # golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd golang.org/x/sys/windows