From 3aebaaad012e7220e1120bfd17dc0613a57025e2 Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Sat, 5 Mar 2022 18:05:07 +0000 Subject: [PATCH] TUN-5836: QUIC transport no longer sets body to nil in any condition Setting the body to nil was rendering cloudflared to crashing with a SIGSEGV in the odd case where the hostname accessed maps to a TCP origin (e.g. SSH/RDP/...) but the eyeball sends a plain HTTP request that does not go through cloudflared access (thus not wrapped in websocket as it should). Instead, QUIC transport now sets http.noBody in that condition, which deals with the situation gracefully. --- connection/quic.go | 2 +- connection/quic_test.go | 2 +- proxy/proxy_test.go | 27 +++++++++++++++++++++++++-- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/connection/quic.go b/connection/quic.go index 1b9f2e55..eb2529b8 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -342,7 +342,7 @@ func buildHTTPRequest(connectRequest *quicpogs.ConnectRequest, body io.ReadClose // * there is no transfer-encoding=chunked already set. // So, if transfer cannot be chunked and content length is 0, we dont set a request body. if !isWebsocket && !isTransferEncodingChunked(req) && req.ContentLength == 0 { - req.Body = nil + req.Body = http.NoBody } stripWebsocketUpgradeHeader(req) return req, err diff --git a/connection/quic_test.go b/connection/quic_test.go index 9763ae33..0f42b08d 100644 --- a/connection/quic_test.go +++ b/connection/quic_test.go @@ -345,7 +345,7 @@ func TestBuildHTTPRequest(t *testing.T) { }, ContentLength: 0, Host: "cf.host", - Body: nil, + Body: http.NoBody, }, body: io.NopCloser(&bytes.Buffer{}), }, diff --git a/proxy/proxy_test.go b/proxy/proxy_test.go index db8747f7..7bc3e21b 100644 --- a/proxy/proxy_test.go +++ b/proxy/proxy_test.go @@ -60,6 +60,11 @@ func (w *mockHTTPRespWriter) Read(data []byte) (int, error) { return 0, fmt.Errorf("mockHTTPRespWriter doesn't implement io.Reader") } +// respHeaders is a test function to read respHeaders +func (w *mockHTTPRespWriter) headers() http.Header { + return w.Header() +} + type mockWSRespWriter struct { *mockHTTPRespWriter writeNotification chan []byte @@ -554,6 +559,24 @@ func TestConnections(t *testing.T) { }, }, }, + { + // Send (unexpected) HTTP when origin expects WS (to unwrap for raw TCP) + name: "http-(ws)tcp proxy", + args: args{ + ingressServiceScheme: "tcp://", + originService: runEchoTCPService, + eyeballResponseWriter: newMockHTTPRespWriter(), + eyeballRequestBody: http.NoBody, + connectionType: connection.TypeHTTP, + requestHeaders: map[string][]string{ + "Cf-Cloudflared-Proxy-Src": {"non-blank-value"}, + }, + }, + want: want{ + message: []byte{}, + headers: map[string][]string{}, + }, + }, { name: "tcp-tcp proxy without warpRoutingService enabled", args: args{ @@ -650,8 +673,8 @@ func TestConnections(t *testing.T) { }() } if test.args.connectionType == connection.TypeTCP { - rws := connection.NewHTTPResponseReadWriterAcker(respWriter, req) - err = proxy.ProxyTCP(ctx, rws, &connection.TCPRequest{Dest: dest}) + rwa := connection.NewHTTPResponseReadWriterAcker(respWriter, req) + err = proxy.ProxyTCP(ctx, rwa, &connection.TCPRequest{Dest: dest}) } else { err = proxy.ProxyHTTP(respWriter, req, test.args.connectionType == connection.TypeWebsocket) }