From 10c4e15b2db32b0c7c3dc1b48c33259c12f357df Mon Sep 17 00:00:00 2001 From: Stochastic <138764723+Asthenia0412@users.noreply.github.com> Date: Mon, 28 Sep 2026 11:14:33 +0800 Subject: [PATCH] connection: validate QUIC response before hijacking --- connection/quic_connection.go | 5 ++- connection/quic_response_writer_test.go | 42 +++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 connection/quic_response_writer_test.go diff --git a/connection/quic_connection.go b/connection/quic_connection.go index b0a0ac2158b..15668485dfa 100644 --- a/connection/quic_connection.go +++ b/connection/quic_connection.go @@ -334,6 +334,9 @@ func (hrw *httpResponseAdapter) WriteHeader(status int) { } func (hrw *httpResponseAdapter) Hijack() (net.Conn, *bufio.ReadWriter, error) { + if !hrw.connectResponseSent { + return nil, nil, fmt.Errorf("status not yet written before attempting to hijack connection") + } conn := &localProxyConnection{hrw.ReadWriteCloser} readWriter := bufio.NewReadWriter( bufio.NewReader(hrw.ReadWriteCloser), @@ -476,4 +479,4 @@ func (np *nopCloserReadWriter) Close() error { // Closing only the receive side satisfies that contract without preventing // the response from being written on this bidirectional QUIC stream. return np.CloseRead() -} +} \ No newline at end of file diff --git a/connection/quic_response_writer_test.go b/connection/quic_response_writer_test.go new file mode 100644 index 00000000000..b220f9bed0d --- /dev/null +++ b/connection/quic_response_writer_test.go @@ -0,0 +1,42 @@ +package connection + +import ( + "net" + "testing" + + rpcquic "github.com/cloudflare/cloudflared/tunnelrpc/quic" + "github.com/stretchr/testify/require" +) + +func TestHTTPResponseAdapterHijackRequiresResponse(t *testing.T) { + t.Parallel() + + stream, peer := net.Pipe() + defer stream.Close() + defer peer.Close() + adapter := httpResponseAdapter{ + RequestServerStream: &rpcquic.RequestServerStream{ReadWriteCloser: stream}, + } + conn, readWriter, err := adapter.Hijack() + + require.Nil(t, conn) + require.Nil(t, readWriter) + require.EqualError(t, err, "status not yet written before attempting to hijack connection") +} + +func TestHTTPResponseAdapterHijackAfterResponse(t *testing.T) { + t.Parallel() + + stream, peer := net.Pipe() + defer stream.Close() + defer peer.Close() + adapter := httpResponseAdapter{ + RequestServerStream: &rpcquic.RequestServerStream{ReadWriteCloser: stream}, + connectResponseSent: true, + } + conn, readWriter, err := adapter.Hijack() + + require.NoError(t, err) + require.NotNil(t, conn) + require.NotNil(t, readWriter) +} \ No newline at end of file