From 529f206d33c35bcdfd6911aa37d604ff5338495b Mon Sep 17 00:00:00 2001 From: mmmray <142015632+mmmray@users.noreply.github.com> Date: Sun, 21 Jul 2024 02:29:50 +0200 Subject: [PATCH] Fix serverside TLS support of SplitHTTP H1/H2 (#3567) Fix #3566 Also update testsuite so that all tests read and write some data. Opening a connection is not enough to trigger connection errors, because the connection is so lazy. --- transport/internet/splithttp/hub.go | 24 ++++--- .../internet/splithttp/splithttp_test.go | 64 +++++++++++++++++-- 2 files changed, 73 insertions(+), 15 deletions(-) diff --git a/transport/internet/splithttp/hub.go b/transport/internet/splithttp/hub.go index 1ce8da6b0966..d4579bc72d12 100644 --- a/transport/internet/splithttp/hub.go +++ b/transport/internet/splithttp/hub.go @@ -269,7 +269,6 @@ func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSet tlsConfig := getTLSConfig(streamSettings) l.isH3 = len(tlsConfig.NextProtos) == 1 && tlsConfig.NextProtos[0] == "h3" - if port == net.Port(0) { // unix listener, err = internet.ListenSystem(ctx, &net.UnixAddr{ Name: address.Domain(), @@ -285,9 +284,9 @@ func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSet Port: int(port), }, streamSettings.SocketSettings) if err != nil { - return nil, errors.New("failed to listen UDP(for SH3) on ", address, ":", port).Base(err) + return nil, errors.New("failed to listen UDP(for SH3) on ", address, ":", port).Base(err) } - h3listener, err := quic.ListenEarly(Conn,tlsConfig, nil) + h3listener, err := quic.ListenEarly(Conn, tlsConfig, nil) if err != nil { return nil, errors.New("failed to listen QUIC(for SH3) on ", address, ":", port).Base(err) } @@ -314,7 +313,6 @@ func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSet if err != nil { return nil, errors.New("failed to listen TCP(for SH) on ", address, ":", port).Base(err) } - l.listener = listener errors.LogInfo(ctx, "listening TCP(for SH) on ", address, ":", port) // h2cHandler can handle both plaintext HTTP/1.1 and h2c @@ -324,18 +322,24 @@ func ListenSH(ctx context.Context, address net.Address, port net.Port, streamSet ReadHeaderTimeout: time.Second * 4, MaxHeaderBytes: 8192, } + } + + // tcp/unix (h1/h2) + if listener != nil { + if config := v2tls.ConfigFromStreamSettings(streamSettings); config != nil { + if tlsConfig := config.GetTLSConfig(); tlsConfig != nil { + listener = tls.NewListener(listener, tlsConfig) + } + } + + l.listener = listener + go func() { if err := l.server.Serve(l.listener); err != nil { errors.LogWarningInner(ctx, err, "failed to serve http for splithttp") } }() } - l.listener = listener - if config := v2tls.ConfigFromStreamSettings(streamSettings); config != nil { - if tlsConfig := config.GetTLSConfig(); tlsConfig != nil { - listener = tls.NewListener(listener, tlsConfig) - } - } return l, err } diff --git a/transport/internet/splithttp/splithttp_test.go b/transport/internet/splithttp/splithttp_test.go index 5f59a738caa2..a3b609ab48ac 100644 --- a/transport/internet/splithttp/splithttp_test.go +++ b/transport/internet/splithttp/splithttp_test.go @@ -2,6 +2,7 @@ package splithttp_test import ( "context" + "crypto/rand" gotls "crypto/tls" "fmt" gonet "net" @@ -10,7 +11,9 @@ import ( "testing" "time" + "github.com/google/go-cmp/cmp" "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol/tls/cert" "github.com/xtls/xray-core/testing/servers/tcp" @@ -143,7 +146,16 @@ func Test_listenSHAndDial_TLS(t *testing.T) { } listen, err := ListenSH(context.Background(), net.LocalHostIP, listenPort, streamSettings, func(conn stat.Connection) { go func() { - _ = conn.Close() + defer conn.Close() + + var b [1024]byte + conn.SetReadDeadline(time.Now().Add(2 * time.Second)) + _, err := conn.Read(b[:]) + if err != nil { + return + } + + common.Must2(conn.Write([]byte("Response"))) }() }) common.Must(err) @@ -151,7 +163,15 @@ func Test_listenSHAndDial_TLS(t *testing.T) { conn, err := Dial(context.Background(), net.TCPDestination(net.DomainAddress("localhost"), listenPort), streamSettings) common.Must(err) - _ = conn.Close() + + _, err = conn.Write([]byte("Test connection 1")) + common.Must(err) + + var b [1024]byte + n, _ := conn.Read(b[:]) + if string(b[:n]) != "Response" { + t.Error("response: ", string(b[:n])) + } end := time.Now() if !end.Before(start.Add(time.Second * 5)) { @@ -229,18 +249,52 @@ func Test_listenSHAndDial_QUIC(t *testing.T) { } listen, err := ListenSH(context.Background(), net.LocalHostIP, listenPort, streamSettings, func(conn stat.Connection) { go func() { - _ = conn.Close() + defer conn.Close() + + b := buf.New() + defer b.Release() + + for { + b.Clear() + if _, err := b.ReadFrom(conn); err != nil { + return + } + common.Must2(conn.Write(b.Bytes())) + } }() }) common.Must(err) defer listen.Close() + time.Sleep(time.Second) + conn, err := Dial(context.Background(), net.UDPDestination(net.DomainAddress("localhost"), listenPort), streamSettings) common.Must(err) - _ = conn.Close() + defer conn.Close() + + const N = 1024 + b1 := make([]byte, N) + common.Must2(rand.Read(b1)) + b2 := buf.New() + + common.Must2(conn.Write(b1)) + + b2.Clear() + common.Must2(b2.ReadFullFrom(conn, N)) + if r := cmp.Diff(b2.Bytes(), b1); r != "" { + t.Error(r) + } + + common.Must2(conn.Write(b1)) + + b2.Clear() + common.Must2(b2.ReadFullFrom(conn, N)) + if r := cmp.Diff(b2.Bytes(), b1); r != "" { + t.Error(r) + } end := time.Now() if !end.Before(start.Add(time.Second * 5)) { t.Error("end: ", end, " start: ", start) } -} \ No newline at end of file +}