Browse Source

Add custom tls client support for v2ray h2/grpclite transports

世界 3 years ago
parent
commit
a2d1f89922

+ 1 - 1
common/tls/client.go

@@ -30,7 +30,7 @@ func NewClient(router adapter.Router, serverAddress string, options option.Outbo
 	}
 	}
 }
 }
 
 
-func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (net.Conn, error) {
+func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, error) {
 	tlsConn := config.Client(conn)
 	tlsConn := config.Client(conn)
 	ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout)
 	ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout)
 	defer cancel()
 	defer cancel()

+ 3 - 0
common/tls/config.go

@@ -15,6 +15,8 @@ type (
 )
 )
 
 
 type Config interface {
 type Config interface {
+	NextProtos() []string
+	SetNextProtos(nextProto []string)
 	Config() (*STDConfig, error)
 	Config() (*STDConfig, error)
 	Client(conn net.Conn) Conn
 	Client(conn net.Conn) Conn
 }
 }
@@ -28,6 +30,7 @@ type ServerConfig interface {
 type Conn interface {
 type Conn interface {
 	net.Conn
 	net.Conn
 	HandshakeContext(ctx context.Context) error
 	HandshakeContext(ctx context.Context) error
+	ConnectionState() tls.ConnectionState
 }
 }
 
 
 func ParseTLSVersion(version string) (uint16, error) {
 func ParseTLSVersion(version string) (uint16, error) {

+ 41 - 10
common/tls/ech_client.go

@@ -4,6 +4,7 @@ package tls
 
 
 import (
 import (
 	"context"
 	"context"
+	"crypto/tls"
 	"crypto/x509"
 	"crypto/x509"
 	"encoding/base64"
 	"encoding/base64"
 	"net"
 	"net"
@@ -12,7 +13,7 @@ import (
 
 
 	"github.com/sagernet/sing-box/adapter"
 	"github.com/sagernet/sing-box/adapter"
 	"github.com/sagernet/sing-box/option"
 	"github.com/sagernet/sing-box/option"
-	"github.com/sagernet/sing-box/transport/cloudflaretls"
+	cftls "github.com/sagernet/sing-box/transport/cloudflaretls"
 	"github.com/sagernet/sing-dns"
 	"github.com/sagernet/sing-dns"
 	E "github.com/sagernet/sing/common/exceptions"
 	E "github.com/sagernet/sing/common/exceptions"
 
 
@@ -21,7 +22,15 @@ import (
 )
 )
 
 
 type echClientConfig struct {
 type echClientConfig struct {
-	config *tls.Config
+	config *cftls.Config
+}
+
+func (e *echClientConfig) NextProtos() []string {
+	return e.config.NextProtos
+}
+
+func (e *echClientConfig) SetNextProtos(nextProto []string) {
+	e.config.NextProtos = nextProto
 }
 }
 
 
 func (e *echClientConfig) Config() (*STDConfig, error) {
 func (e *echClientConfig) Config() (*STDConfig, error) {
@@ -29,7 +38,29 @@ func (e *echClientConfig) Config() (*STDConfig, error) {
 }
 }
 
 
 func (e *echClientConfig) Client(conn net.Conn) Conn {
 func (e *echClientConfig) Client(conn net.Conn) Conn {
-	return tls.Client(conn, e.config)
+	return &echConnWrapper{cftls.Client(conn, e.config)}
+}
+
+type echConnWrapper struct {
+	*cftls.Conn
+}
+
+func (c *echConnWrapper) ConnectionState() tls.ConnectionState {
+	state := c.Conn.ConnectionState()
+	return tls.ConnectionState{
+		Version:                     state.Version,
+		HandshakeComplete:           state.HandshakeComplete,
+		DidResume:                   state.DidResume,
+		CipherSuite:                 state.CipherSuite,
+		NegotiatedProtocol:          state.NegotiatedProtocol,
+		NegotiatedProtocolIsMutual:  state.NegotiatedProtocolIsMutual,
+		ServerName:                  state.ServerName,
+		PeerCertificates:            state.PeerCertificates,
+		VerifiedChains:              state.VerifiedChains,
+		SignedCertificateTimestamps: state.SignedCertificateTimestamps,
+		OCSPResponse:                state.OCSPResponse,
+		TLSUnique:                   state.TLSUnique,
+	}
 }
 }
 
 
 func newECHClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
 func newECHClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
@@ -45,7 +76,7 @@ func newECHClient(router adapter.Router, serverAddress string, options option.Ou
 		return nil, E.New("missing server_name or insecure=true")
 		return nil, E.New("missing server_name or insecure=true")
 	}
 	}
 
 
-	var tlsConfig tls.Config
+	var tlsConfig cftls.Config
 	if options.DisableSNI {
 	if options.DisableSNI {
 		tlsConfig.ServerName = "127.0.0.1"
 		tlsConfig.ServerName = "127.0.0.1"
 	} else {
 	} else {
@@ -55,7 +86,7 @@ func newECHClient(router adapter.Router, serverAddress string, options option.Ou
 		tlsConfig.InsecureSkipVerify = options.Insecure
 		tlsConfig.InsecureSkipVerify = options.Insecure
 	} else if options.DisableSNI {
 	} else if options.DisableSNI {
 		tlsConfig.InsecureSkipVerify = true
 		tlsConfig.InsecureSkipVerify = true
-		tlsConfig.VerifyConnection = func(state tls.ConnectionState) error {
+		tlsConfig.VerifyConnection = func(state cftls.ConnectionState) error {
 			verifyOptions := x509.VerifyOptions{
 			verifyOptions := x509.VerifyOptions{
 				DNSName:       serverName,
 				DNSName:       serverName,
 				Intermediates: x509.NewCertPool(),
 				Intermediates: x509.NewCertPool(),
@@ -87,7 +118,7 @@ func newECHClient(router adapter.Router, serverAddress string, options option.Ou
 	if options.CipherSuites != nil {
 	if options.CipherSuites != nil {
 	find:
 	find:
 		for _, cipherSuite := range options.CipherSuites {
 		for _, cipherSuite := range options.CipherSuites {
-			for _, tlsCipherSuite := range tls.CipherSuites() {
+			for _, tlsCipherSuite := range cftls.CipherSuites() {
 				if cipherSuite == tlsCipherSuite.Name {
 				if cipherSuite == tlsCipherSuite.Name {
 					tlsConfig.CipherSuites = append(tlsConfig.CipherSuites, tlsCipherSuite.ID)
 					tlsConfig.CipherSuites = append(tlsConfig.CipherSuites, tlsCipherSuite.ID)
 					continue find
 					continue find
@@ -124,7 +155,7 @@ func newECHClient(router adapter.Router, serverAddress string, options option.Ou
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
-		clientConfig, err := tls.UnmarshalECHConfigs(clientConfigContent)
+		clientConfig, err := cftls.UnmarshalECHConfigs(clientConfigContent)
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
@@ -137,8 +168,8 @@ func newECHClient(router adapter.Router, serverAddress string, options option.Ou
 
 
 const typeHTTPS = 65
 const typeHTTPS = 65
 
 
-func fetchECHClientConfig(router adapter.Router) func(ctx context.Context, serverName string) ([]tls.ECHConfig, error) {
-	return func(ctx context.Context, serverName string) ([]tls.ECHConfig, error) {
+func fetchECHClientConfig(router adapter.Router) func(ctx context.Context, serverName string) ([]cftls.ECHConfig, error) {
+	return func(ctx context.Context, serverName string) ([]cftls.ECHConfig, error) {
 		message := &dnsmessage.Message{
 		message := &dnsmessage.Message{
 			Header: dnsmessage.Header{
 			Header: dnsmessage.Header{
 				RecursionDesired: true,
 				RecursionDesired: true,
@@ -176,7 +207,7 @@ func fetchECHClientConfig(router adapter.Router) func(ctx context.Context, serve
 						if err != nil {
 						if err != nil {
 							return nil, E.Cause(err, "decode ECH config")
 							return nil, E.Cause(err, "decode ECH config")
 						}
 						}
-						return tls.UnmarshalECHConfigs(echConfig)
+						return cftls.UnmarshalECHConfigs(echConfig)
 					}
 					}
 				}
 				}
 			default:
 			default:

+ 8 - 0
common/tls/std_client.go

@@ -99,6 +99,14 @@ func newStdClient(serverAddress string, options option.OutboundTLSOptions) (Conf
 	return &stdClientConfig{&tlsConfig}, nil
 	return &stdClientConfig{&tlsConfig}, nil
 }
 }
 
 
+func (s *stdClientConfig) NextProtos() []string {
+	return s.config.NextProtos
+}
+
+func (s *stdClientConfig) SetNextProtos(nextProto []string) {
+	s.config.NextProtos = nextProto
+}
+
 func (s *stdClientConfig) Config() (*STDConfig, error) {
 func (s *stdClientConfig) Config() (*STDConfig, error) {
 	return s.config, nil
 	return s.config, nil
 }
 }

+ 8 - 0
common/tls/std_server.go

@@ -26,6 +26,14 @@ type STDServerConfig struct {
 	watcher         *fsnotify.Watcher
 	watcher         *fsnotify.Watcher
 }
 }
 
 
+func (c *STDServerConfig) NextProtos() []string {
+	return c.config.NextProtos
+}
+
+func (c *STDServerConfig) SetNextProtos(nextProto []string) {
+	c.config.NextProtos = nextProto
+}
+
 func newSTDServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
 func newSTDServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) {
 	if !options.Enabled {
 	if !options.Enabled {
 		return nil, nil
 		return nil, nil

+ 26 - 0
common/tls/utls_client.go

@@ -22,6 +22,14 @@ type utlsClientConfig struct {
 	id     utls.ClientHelloID
 	id     utls.ClientHelloID
 }
 }
 
 
+func (e *utlsClientConfig) NextProtos() []string {
+	return e.config.NextProtos
+}
+
+func (e *utlsClientConfig) SetNextProtos(nextProto []string) {
+	e.config.NextProtos = nextProto
+}
+
 func (e *utlsClientConfig) Config() (*STDConfig, error) {
 func (e *utlsClientConfig) Config() (*STDConfig, error) {
 	return nil, E.New("unsupported usage for uTLS")
 	return nil, E.New("unsupported usage for uTLS")
 }
 }
@@ -38,6 +46,24 @@ func (c *utlsConnWrapper) HandshakeContext(ctx context.Context) error {
 	return c.Conn.Handshake()
 	return c.Conn.Handshake()
 }
 }
 
 
+func (c *utlsConnWrapper) ConnectionState() tls.ConnectionState {
+	state := c.Conn.ConnectionState()
+	return tls.ConnectionState{
+		Version:                     state.Version,
+		HandshakeComplete:           state.HandshakeComplete,
+		DidResume:                   state.DidResume,
+		CipherSuite:                 state.CipherSuite,
+		NegotiatedProtocol:          state.NegotiatedProtocol,
+		NegotiatedProtocolIsMutual:  state.NegotiatedProtocolIsMutual,
+		ServerName:                  state.ServerName,
+		PeerCertificates:            state.PeerCertificates,
+		VerifiedChains:              state.VerifiedChains,
+		SignedCertificateTimestamps: state.SignedCertificateTimestamps,
+		OCSPResponse:                state.OCSPResponse,
+		TLSUnique:                   state.TLSUnique,
+	}
+}
+
 func newUTLSClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
 func newUTLSClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
 	var serverName string
 	var serverName string
 	if options.ServerName != "" {
 	if options.ServerName != "" {

+ 8 - 8
test/go.mod

@@ -7,14 +7,14 @@ require github.com/sagernet/sing-box v0.0.0
 replace github.com/sagernet/sing-box => ../
 replace github.com/sagernet/sing-box => ../
 
 
 require (
 require (
-	github.com/docker/docker v20.10.17+incompatible
+	github.com/docker/docker v20.10.18+incompatible
 	github.com/docker/go-connections v0.4.0
 	github.com/docker/go-connections v0.4.0
-	github.com/gofrs/uuid v4.2.0+incompatible
-	github.com/sagernet/sing v0.0.0-20220905164441-f3d346256d4a
+	github.com/gofrs/uuid v4.3.0+incompatible
+	github.com/sagernet/sing v0.0.0-20220910144724-62c4ebdbcb3f
 	github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
 	github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
 	github.com/spyzhov/ajson v0.7.1
 	github.com/spyzhov/ajson v0.7.1
 	github.com/stretchr/testify v1.8.0
 	github.com/stretchr/testify v1.8.0
-	golang.org/x/net v0.0.0-20220907135653-1e95f45603a7
+	golang.org/x/net v0.0.0-20220909164309-bea034e7d591
 )
 )
 
 
 //replace github.com/sagernet/sing => ../../sing
 //replace github.com/sagernet/sing => ../../sing
@@ -66,12 +66,13 @@ require (
 	github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
 	github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
 	github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
 	github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
 	github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb // indirect
 	github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb // indirect
-	github.com/sagernet/sing-dns v0.0.0-20220903082137-b1102b8fc961 // indirect
+	github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666 // indirect
 	github.com/sagernet/sing-tun v0.0.0-20220909114108-a6b5a9289ecb // indirect
 	github.com/sagernet/sing-tun v0.0.0-20220909114108-a6b5a9289ecb // indirect
 	github.com/sagernet/sing-vmess v0.0.0-20220907073918-72d7fdf6825f // indirect
 	github.com/sagernet/sing-vmess v0.0.0-20220907073918-72d7fdf6825f // indirect
-	github.com/sagernet/smux v0.0.0-20220907034654-1acb8471c15a // indirect
+	github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 // indirect
 	github.com/sirupsen/logrus v1.8.1 // indirect
 	github.com/sirupsen/logrus v1.8.1 // indirect
 	github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
 	github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
+	go.etcd.io/bbolt v1.3.6 // indirect
 	go.uber.org/atomic v1.10.0 // indirect
 	go.uber.org/atomic v1.10.0 // indirect
 	go.uber.org/multierr v1.6.0 // indirect
 	go.uber.org/multierr v1.6.0 // indirect
 	go.uber.org/zap v1.22.0 // indirect
 	go.uber.org/zap v1.22.0 // indirect
@@ -79,7 +80,7 @@ require (
 	golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect
 	golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect
 	golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
 	golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
 	golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
 	golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
-	golang.org/x/sys v0.0.0-20220908164124-27713097b956 // indirect
+	golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2 // indirect
 	golang.org/x/text v0.3.7 // indirect
 	golang.org/x/text v0.3.7 // indirect
 	golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
 	golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
 	golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f // indirect
 	golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f // indirect
@@ -89,7 +90,6 @@ require (
 	google.golang.org/grpc v1.49.0 // indirect
 	google.golang.org/grpc v1.49.0 // indirect
 	google.golang.org/protobuf v1.28.1 // indirect
 	google.golang.org/protobuf v1.28.1 // indirect
 	gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
 	gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
-	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	gotest.tools/v3 v3.3.0 // indirect
 	gotest.tools/v3 v3.3.0 // indirect
 	gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c // indirect
 	gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c // indirect

+ 18 - 16
test/go.sum

@@ -32,8 +32,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
 github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
 github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
 github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
-github.com/docker/docker v20.10.17+incompatible h1:JYCuMrWaVNophQTOrMMoSwudOVEfcegoZZrleKc1xwE=
-github.com/docker/docker v20.10.17+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v20.10.18+incompatible h1:SN84VYXTBNGn92T/QwIRPlum9zfemfitN7pbsp26WSc=
+github.com/docker/docker v20.10.18+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
 github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
 github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
 github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
 github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
 github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
@@ -57,8 +57,8 @@ github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg=
 github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
 github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
 github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
 github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
 github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
 github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
-github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=
-github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
+github.com/gofrs/uuid v4.3.0+incompatible h1:CaSVZxm5B+7o45rtab4jC2G37WGYX1zQfuU2i6DSvnc=
+github.com/gofrs/uuid v4.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@@ -165,18 +165,18 @@ github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb h1:wc0yQ+SBn4TaTY
 github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb/go.mod h1:MIccjRKnPTjWwAOpl+AUGWOkzyTd9tERytudxu+1ra4=
 github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb/go.mod h1:MIccjRKnPTjWwAOpl+AUGWOkzyTd9tERytudxu+1ra4=
 github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
 github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
 github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
 github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
-github.com/sagernet/sing v0.0.0-20220905164441-f3d346256d4a h1:Bqt+eYP7vJocAgAVAXC0B0ZN0uMr6g6exAoF3Ado2pg=
-github.com/sagernet/sing v0.0.0-20220905164441-f3d346256d4a/go.mod h1:kZvzh1VDa/Dg/Bt5WaYKU0jl5ept8KKDpl3Ay4gRtRQ=
-github.com/sagernet/sing-dns v0.0.0-20220903082137-b1102b8fc961 h1:5JeqhvCGV6AQQiAO0V67Loh2eyO3JNjIQnvRF8NnTE0=
-github.com/sagernet/sing-dns v0.0.0-20220903082137-b1102b8fc961/go.mod h1:vKBBy4mNJRaFuJ8H6kYIOPofsZ1JT5mgdwIlebtvnZ4=
+github.com/sagernet/sing v0.0.0-20220910144724-62c4ebdbcb3f h1:w1TJq7Lw3It35tDyMsZLtYz4T2msf1UK9JxC85L5+sk=
+github.com/sagernet/sing v0.0.0-20220910144724-62c4ebdbcb3f/go.mod h1:kZvzh1VDa/Dg/Bt5WaYKU0jl5ept8KKDpl3Ay4gRtRQ=
+github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666 h1:XUTocA/Ek0dFxUX+xJCWMPPFZCn2GC/uLrBjTSr1vHY=
+github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666/go.mod h1:eDyH7AJmqBGjZQdQmpZIzlbTREudZuWDExMuGKgjRVM=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM=
 github.com/sagernet/sing-tun v0.0.0-20220909114108-a6b5a9289ecb h1:/swVU2mgwDwZ9l67v1Sim1ias/ZmriGTxQLnMakPhtQ=
 github.com/sagernet/sing-tun v0.0.0-20220909114108-a6b5a9289ecb h1:/swVU2mgwDwZ9l67v1Sim1ias/ZmriGTxQLnMakPhtQ=
 github.com/sagernet/sing-tun v0.0.0-20220909114108-a6b5a9289ecb/go.mod h1:5AhPUv9jWDQ3pv3Mj78SL/1TSjhoaj6WNASxRKLqXqM=
 github.com/sagernet/sing-tun v0.0.0-20220909114108-a6b5a9289ecb/go.mod h1:5AhPUv9jWDQ3pv3Mj78SL/1TSjhoaj6WNASxRKLqXqM=
 github.com/sagernet/sing-vmess v0.0.0-20220907073918-72d7fdf6825f h1:6l9aXZqAl1JqXJWi89KHpWnM/moQUPGG+XiwMc+yD0A=
 github.com/sagernet/sing-vmess v0.0.0-20220907073918-72d7fdf6825f h1:6l9aXZqAl1JqXJWi89KHpWnM/moQUPGG+XiwMc+yD0A=
 github.com/sagernet/sing-vmess v0.0.0-20220907073918-72d7fdf6825f/go.mod h1:u66Vv7NHXJWfeAmhh7JuJp/cwxmuQlM56QoZ7B7Mmd0=
 github.com/sagernet/sing-vmess v0.0.0-20220907073918-72d7fdf6825f/go.mod h1:u66Vv7NHXJWfeAmhh7JuJp/cwxmuQlM56QoZ7B7Mmd0=
-github.com/sagernet/smux v0.0.0-20220907034654-1acb8471c15a h1:GCNwsN8MEckpjGJjK3qjQBQ9qHsoXB9B/KHUWBvE1V4=
-github.com/sagernet/smux v0.0.0-20220907034654-1acb8471c15a/go.mod h1:yedWtra8nyGJ+SyI+ziwuaGMzBatbB10P1IOOZbbSK8=
+github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 h1:5VBIbVw9q7aKbrFdT83mjkyvQ+VaRsQ6yflTepfln38=
+github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195/go.mod h1:yedWtra8nyGJ+SyI+ziwuaGMzBatbB10P1IOOZbbSK8=
 github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
 github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
 github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
@@ -197,6 +197,8 @@ github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
+go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
 go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
 go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
 go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
 go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
 go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
 go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
@@ -251,8 +253,8 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx
 golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211111160137-58aab5ef257a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211111160137-58aab5ef257a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.0.0-20220907135653-1e95f45603a7 h1:1WGATo9HAhkWMbfyuVU0tEFP88OIkUvwaHFveQPvzCQ=
-golang.org/x/net v0.0.0-20220907135653-1e95f45603a7/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
+golang.org/x/net v0.0.0-20220909164309-bea034e7d591 h1:D0B/7al0LLrVC8aWF4+oxpv/m8bc7ViFfVS8/gXGdqI=
+golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -275,6 +277,7 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -290,8 +293,8 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220908164124-27713097b956 h1:XeJjHH1KiLpKGb6lvMiksZ9l0fVUh+AmGcm0nOMEBOY=
-golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2 h1:wM1k/lXfpc5HdkJJyW9GELpd8ERGdnh8sMGL6Gzq3Ho=
+golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -370,9 +373,8 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
 gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
-gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

+ 0 - 47
test/wireguard_test.go

@@ -2,7 +2,6 @@ package main
 
 
 import (
 import (
 	"net/netip"
 	"net/netip"
-	"os"
 	"testing"
 	"testing"
 	"time"
 	"time"
 
 
@@ -50,49 +49,3 @@ func TestWireGuard(t *testing.T) {
 	})
 	})
 	testSuitWg(t, clientPort, testPort)
 	testSuitWg(t, clientPort, testPort)
 }
 }
-
-func TestWireGuardSystem(t *testing.T) {
-	if os.Getuid() != 0 {
-		t.Skip("requires root")
-	}
-	startDockerContainer(t, DockerOptions{
-		Image: ImageBoringTun,
-		Cap:   []string{"MKNOD", "NET_ADMIN", "NET_RAW"},
-		Ports: []uint16{serverPort, testPort},
-		Bind: map[string]string{
-			"wireguard.conf": "/etc/wireguard/wg0.conf",
-		},
-		Cmd: []string{"wg0"},
-	})
-	time.Sleep(5 * time.Second)
-	startInstance(t, option.Options{
-		Inbounds: []option.Inbound{
-			{
-				Type: C.TypeMixed,
-				MixedOptions: option.HTTPMixedInboundOptions{
-					ListenOptions: option.ListenOptions{
-						Listen:     option.ListenAddress(netip.IPv4Unspecified()),
-						ListenPort: clientPort,
-					},
-				},
-			},
-		},
-		Outbounds: []option.Outbound{
-			{
-				Type: C.TypeWireGuard,
-				WireGuardOptions: option.WireGuardOutboundOptions{
-					InterfaceName: "wg",
-					ServerOptions: option.ServerOptions{
-						Server:     "127.0.0.1",
-						ServerPort: serverPort,
-					},
-					LocalAddress:  []option.ListenPrefix{option.ListenPrefix(netip.MustParsePrefix("10.0.0.2/32"))},
-					PrivateKey:    "qGnwlkZljMxeECW8fbwAWdvgntnbK7B8UmMFl3zM0mk=",
-					PeerPublicKey: "QsdcBm+oJw2oNv0cIFXLIq1E850lgTBonup4qnKEQBg=",
-				},
-			},
-		},
-	})
-	time.Sleep(10 * time.Second)
-	testSuitWg(t, clientPort, testPort)
-}

+ 22 - 10
transport/v2raygrpclite/client.go

@@ -13,6 +13,8 @@ import (
 	"github.com/sagernet/sing-box/option"
 	"github.com/sagernet/sing-box/option"
 	M "github.com/sagernet/sing/common/metadata"
 	M "github.com/sagernet/sing/common/metadata"
 	N "github.com/sagernet/sing/common/network"
 	N "github.com/sagernet/sing/common/network"
+
+	"golang.org/x/net/http2"
 )
 )
 
 
 var _ adapter.V2RayClientTransport = (*Client)(nil)
 var _ adapter.V2RayClientTransport = (*Client)(nil)
@@ -27,27 +29,37 @@ type Client struct {
 	ctx        context.Context
 	ctx        context.Context
 	dialer     N.Dialer
 	dialer     N.Dialer
 	serverAddr M.Socksaddr
 	serverAddr M.Socksaddr
-	transport  *http.Transport
+	transport  http.RoundTripper
 	options    option.V2RayGRPCOptions
 	options    option.V2RayGRPCOptions
 	url        *url.URL
 	url        *url.URL
 }
 }
 
 
 func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayGRPCOptions, tlsConfig tls.Config) adapter.V2RayClientTransport {
 func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayGRPCOptions, tlsConfig tls.Config) adapter.V2RayClientTransport {
-	return &Client{
-		ctx:        ctx,
-		dialer:     dialer,
-		serverAddr: serverAddr,
-		options:    options,
-		transport: &http.Transport{
-			DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
+	var transport http.RoundTripper
+	if tlsConfig == nil {
+		transport = &http.Transport{
+			DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
+				return dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
+			},
+		}
+	} else {
+		tlsConfig.SetNextProtos([]string{http2.NextProtoTLS})
+		transport = &http2.Transport{
+			DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.STDConfig) (net.Conn, error) {
 				conn, err := dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
 				conn, err := dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
 				if err != nil {
 				if err != nil {
 					return nil, err
 					return nil, err
 				}
 				}
 				return tls.ClientHandshake(ctx, conn, tlsConfig)
 				return tls.ClientHandshake(ctx, conn, tlsConfig)
 			},
 			},
-			ForceAttemptHTTP2: true,
-		},
+		}
+	}
+	return &Client{
+		ctx:        ctx,
+		dialer:     dialer,
+		serverAddr: serverAddr,
+		options:    options,
+		transport:  transport,
 		url: &url.URL{
 		url: &url.URL{
 			Scheme: "https",
 			Scheme: "https",
 			Host:   serverAddr.String(),
 			Host:   serverAddr.String(),

+ 6 - 0
transport/v2raygrpclite/server.go

@@ -17,6 +17,8 @@ import (
 	M "github.com/sagernet/sing/common/metadata"
 	M "github.com/sagernet/sing/common/metadata"
 	N "github.com/sagernet/sing/common/network"
 	N "github.com/sagernet/sing/common/network"
 	sHttp "github.com/sagernet/sing/protocol/http"
 	sHttp "github.com/sagernet/sing/protocol/http"
+
+	"golang.org/x/net/http2"
 )
 )
 
 
 var _ adapter.V2RayServerTransport = (*Server)(nil)
 var _ adapter.V2RayServerTransport = (*Server)(nil)
@@ -87,6 +89,10 @@ func (s *Server) Serve(listener net.Listener) error {
 	if s.httpServer.TLSConfig == nil {
 	if s.httpServer.TLSConfig == nil {
 		return s.httpServer.Serve(listener)
 		return s.httpServer.Serve(listener)
 	} else {
 	} else {
+		err := http2.ConfigureServer(s.httpServer, &http2.Server{})
+		if err != nil {
+			return err
+		}
 		return s.httpServer.ServeTLS(listener, "", "")
 		return s.httpServer.ServeTLS(listener, "", "")
 	}
 	}
 }
 }

+ 35 - 33
transport/v2rayhttp/client.go

@@ -16,6 +16,8 @@ import (
 	E "github.com/sagernet/sing/common/exceptions"
 	E "github.com/sagernet/sing/common/exceptions"
 	M "github.com/sagernet/sing/common/metadata"
 	M "github.com/sagernet/sing/common/metadata"
 	N "github.com/sagernet/sing/common/network"
 	N "github.com/sagernet/sing/common/network"
+
+	"golang.org/x/net/http2"
 )
 )
 
 
 var _ adapter.V2RayClientTransport = (*Client)(nil)
 var _ adapter.V2RayClientTransport = (*Client)(nil)
@@ -24,7 +26,7 @@ type Client struct {
 	ctx        context.Context
 	ctx        context.Context
 	dialer     N.Dialer
 	dialer     N.Dialer
 	serverAddr M.Socksaddr
 	serverAddr M.Socksaddr
-	client     *http.Client
+	transport  http.RoundTripper
 	http2      bool
 	http2      bool
 	url        *url.URL
 	url        *url.URL
 	host       []string
 	host       []string
@@ -33,6 +35,25 @@ type Client struct {
 }
 }
 
 
 func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayHTTPOptions, tlsConfig tls.Config) adapter.V2RayClientTransport {
 func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayHTTPOptions, tlsConfig tls.Config) adapter.V2RayClientTransport {
+	var transport http.RoundTripper
+	if tlsConfig == nil {
+		transport = &http.Transport{
+			DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
+				return dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
+			},
+		}
+	} else {
+		tlsConfig.SetNextProtos([]string{http2.NextProtoTLS})
+		transport = &http2.Transport{
+			DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.STDConfig) (net.Conn, error) {
+				conn, err := dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
+				if err != nil {
+					return nil, err
+				}
+				return tls.ClientHandshake(ctx, conn, tlsConfig)
+			},
+		}
+	}
 	client := &Client{
 	client := &Client{
 		ctx:        ctx,
 		ctx:        ctx,
 		dialer:     dialer,
 		dialer:     dialer,
@@ -40,27 +61,9 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
 		host:       options.Host,
 		host:       options.Host,
 		method:     options.Method,
 		method:     options.Method,
 		headers:    make(http.Header),
 		headers:    make(http.Header),
-		client:     &http.Client{},
+		transport:  transport,
 		http2:      tlsConfig != nil,
 		http2:      tlsConfig != nil,
 	}
 	}
-	if client.http2 {
-		client.client.Transport = &http.Transport{
-			DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
-				conn, err := dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
-				if err != nil {
-					return nil, err
-				}
-				return tls.ClientHandshake(ctx, conn, tlsConfig)
-			},
-			ForceAttemptHTTP2: true,
-		}
-	} else {
-		client.client.Transport = &http.Transport{
-			DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
-				return dialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
-			},
-		}
-	}
 	if client.method == "" {
 	if client.method == "" {
 		client.method = "PUT"
 		client.method = "PUT"
 	}
 	}
@@ -145,17 +148,16 @@ func (c *Client) dialHTTP2(ctx context.Context) (net.Conn, error) {
 	}
 	}
 	// Disable any compression method from server.
 	// Disable any compression method from server.
 	request.Header.Set("Accept-Encoding", "identity")
 	request.Header.Set("Accept-Encoding", "identity")
-	response, err := c.client.Do(request) // nolint: bodyclose
-	if err != nil {
-		pipeInWriter.Close()
-		return nil, err
-	}
-	if response.StatusCode != 200 {
-		pipeInWriter.Close()
-		return nil, E.New("unexpected status: ", response.StatusCode, " ", response.Status)
-	}
-	return &HTTPConn{
-		response.Body,
-		pipeInWriter,
-	}, nil
+	conn := newLateHTTPConn(pipeInWriter)
+	go func() {
+		response, err := c.transport.RoundTrip(request)
+		if err != nil {
+			conn.setup(nil, err)
+		} else if response.StatusCode != 200 {
+			conn.setup(nil, E.New("unexpected status: ", response.StatusCode, " ", response.Status))
+		} else {
+			conn.setup(response.Body, nil)
+		}
+	}()
+	return conn, nil
 }
 }

+ 28 - 0
transport/v2rayhttp/conn.go

@@ -14,9 +14,37 @@ import (
 type HTTPConn struct {
 type HTTPConn struct {
 	reader io.Reader
 	reader io.Reader
 	writer io.Writer
 	writer io.Writer
+	create chan struct{}
+	err    error
+}
+
+func newHTTPConn(reader io.Reader, writer io.Writer) HTTPConn {
+	return HTTPConn{
+		reader: reader,
+		writer: writer,
+	}
+}
+
+func newLateHTTPConn(writer io.Writer) *HTTPConn {
+	return &HTTPConn{
+		create: make(chan struct{}),
+		writer: writer,
+	}
+}
+
+func (c *HTTPConn) setup(reader io.Reader, err error) {
+	c.reader = reader
+	c.err = err
+	close(c.create)
 }
 }
 
 
 func (c *HTTPConn) Read(b []byte) (n int, err error) {
 func (c *HTTPConn) Read(b []byte) (n int, err error) {
+	if c.reader == nil {
+		<-c.create
+		if c.err != nil {
+			return 0, c.err
+		}
+	}
 	n, err = c.reader.Read(b)
 	n, err = c.reader.Read(b)
 	return n, baderror.WrapH2(err)
 	return n, baderror.WrapH2(err)
 }
 }

+ 7 - 4
transport/v2rayhttp/server.go

@@ -16,6 +16,8 @@ import (
 	M "github.com/sagernet/sing/common/metadata"
 	M "github.com/sagernet/sing/common/metadata"
 	N "github.com/sagernet/sing/common/network"
 	N "github.com/sagernet/sing/common/network"
 	sHttp "github.com/sagernet/sing/protocol/http"
 	sHttp "github.com/sagernet/sing/protocol/http"
+
+	"golang.org/x/net/http2"
 )
 )
 
 
 var _ adapter.V2RayServerTransport = (*Server)(nil)
 var _ adapter.V2RayServerTransport = (*Server)(nil)
@@ -110,10 +112,7 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
 		s.handler.NewConnection(request.Context(), conn, metadata)
 		s.handler.NewConnection(request.Context(), conn, metadata)
 	} else {
 	} else {
 		conn := &ServerHTTPConn{
 		conn := &ServerHTTPConn{
-			HTTPConn{
-				request.Body,
-				writer,
-			},
+			newHTTPConn(request.Body, writer),
 			writer.(http.Flusher),
 			writer.(http.Flusher),
 		}
 		}
 		s.handler.NewConnection(request.Context(), conn, metadata)
 		s.handler.NewConnection(request.Context(), conn, metadata)
@@ -128,6 +127,10 @@ func (s *Server) Serve(listener net.Listener) error {
 	if s.httpServer.TLSConfig == nil {
 	if s.httpServer.TLSConfig == nil {
 		return s.httpServer.Serve(listener)
 		return s.httpServer.Serve(listener)
 	} else {
 	} else {
+		err := http2.ConfigureServer(s.httpServer, &http2.Server{})
+		if err != nil {
+			return err
+		}
 		return s.httpServer.ServeTLS(listener, "", "")
 		return s.httpServer.ServeTLS(listener, "", "")
 	}
 	}
 }
 }