diff --git a/common/tls/client.go b/common/tls/client.go index cf61566e..1f28ba1a 100644 --- a/common/tls/client.go +++ b/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) ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout) defer cancel() diff --git a/common/tls/config.go b/common/tls/config.go index 68c1a951..8777c82f 100644 --- a/common/tls/config.go +++ b/common/tls/config.go @@ -15,6 +15,8 @@ type ( ) type Config interface { + NextProtos() []string + SetNextProtos(nextProto []string) Config() (*STDConfig, error) Client(conn net.Conn) Conn } @@ -28,6 +30,7 @@ type ServerConfig interface { type Conn interface { net.Conn HandshakeContext(ctx context.Context) error + ConnectionState() tls.ConnectionState } func ParseTLSVersion(version string) (uint16, error) { diff --git a/common/tls/ech_client.go b/common/tls/ech_client.go index 07320287..e3d1bb5d 100644 --- a/common/tls/ech_client.go +++ b/common/tls/ech_client.go @@ -4,6 +4,7 @@ package tls import ( "context" + "crypto/tls" "crypto/x509" "encoding/base64" "net" @@ -12,7 +13,7 @@ import ( "github.com/sagernet/sing-box/adapter" "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" E "github.com/sagernet/sing/common/exceptions" @@ -21,7 +22,15 @@ import ( ) 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) { @@ -29,7 +38,29 @@ func (e *echClientConfig) Config() (*STDConfig, error) { } 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) { @@ -45,7 +76,7 @@ func newECHClient(router adapter.Router, serverAddress string, options option.Ou return nil, E.New("missing server_name or insecure=true") } - var tlsConfig tls.Config + var tlsConfig cftls.Config if options.DisableSNI { tlsConfig.ServerName = "127.0.0.1" } else { @@ -55,7 +86,7 @@ func newECHClient(router adapter.Router, serverAddress string, options option.Ou tlsConfig.InsecureSkipVerify = options.Insecure } else if options.DisableSNI { tlsConfig.InsecureSkipVerify = true - tlsConfig.VerifyConnection = func(state tls.ConnectionState) error { + tlsConfig.VerifyConnection = func(state cftls.ConnectionState) error { verifyOptions := x509.VerifyOptions{ DNSName: serverName, Intermediates: x509.NewCertPool(), @@ -87,7 +118,7 @@ func newECHClient(router adapter.Router, serverAddress string, options option.Ou if options.CipherSuites != nil { find: for _, cipherSuite := range options.CipherSuites { - for _, tlsCipherSuite := range tls.CipherSuites() { + for _, tlsCipherSuite := range cftls.CipherSuites() { if cipherSuite == tlsCipherSuite.Name { tlsConfig.CipherSuites = append(tlsConfig.CipherSuites, tlsCipherSuite.ID) continue find @@ -124,7 +155,7 @@ func newECHClient(router adapter.Router, serverAddress string, options option.Ou if err != nil { return nil, err } - clientConfig, err := tls.UnmarshalECHConfigs(clientConfigContent) + clientConfig, err := cftls.UnmarshalECHConfigs(clientConfigContent) if err != nil { return nil, err } @@ -137,8 +168,8 @@ func newECHClient(router adapter.Router, serverAddress string, options option.Ou 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{ Header: dnsmessage.Header{ RecursionDesired: true, @@ -176,7 +207,7 @@ func fetchECHClientConfig(router adapter.Router) func(ctx context.Context, serve if err != nil { return nil, E.Cause(err, "decode ECH config") } - return tls.UnmarshalECHConfigs(echConfig) + return cftls.UnmarshalECHConfigs(echConfig) } } default: diff --git a/common/tls/std_client.go b/common/tls/std_client.go index de2076ae..1a7c57e1 100644 --- a/common/tls/std_client.go +++ b/common/tls/std_client.go @@ -99,6 +99,14 @@ func newStdClient(serverAddress string, options option.OutboundTLSOptions) (Conf 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) { return s.config, nil } diff --git a/common/tls/std_server.go b/common/tls/std_server.go index b3cbbc74..22f1924f 100644 --- a/common/tls/std_server.go +++ b/common/tls/std_server.go @@ -26,6 +26,14 @@ type STDServerConfig struct { 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) { if !options.Enabled { return nil, nil diff --git a/common/tls/utls_client.go b/common/tls/utls_client.go index 435e1977..b3b91970 100644 --- a/common/tls/utls_client.go +++ b/common/tls/utls_client.go @@ -22,6 +22,14 @@ type utlsClientConfig struct { 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) { return nil, E.New("unsupported usage for uTLS") } @@ -38,6 +46,24 @@ func (c *utlsConnWrapper) HandshakeContext(ctx context.Context) error { 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) { var serverName string if options.ServerName != "" { diff --git a/test/go.mod b/test/go.mod index 256e7165..f77a2434 100644 --- a/test/go.mod +++ b/test/go.mod @@ -7,14 +7,14 @@ require github.com/sagernet/sing-box v0.0.0 replace github.com/sagernet/sing-box => ../ 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/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/spyzhov/ajson v0.7.1 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 @@ -66,12 +66,13 @@ require ( 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/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-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/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/multierr v1.6.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/exp v0.0.0-20220722155223-a9213eeb770e // 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/time v0.0.0-20191024005414-555d28b269f0 // 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/protobuf v1.28.1 // 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 gotest.tools/v3 v3.3.0 // indirect gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c // indirect diff --git a/test/go.sum b/test/go.sum index 18aa9d7b..71369609 100644 --- a/test/go.sum +++ b/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/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/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/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= 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-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/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/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 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/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-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/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/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/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.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= 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.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 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.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 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-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-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-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 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-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-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-20201119102817-f84b799fce68/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-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-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-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= 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.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.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= 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-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/test/wireguard_test.go b/test/wireguard_test.go index 928d696a..fdffe0b8 100644 --- a/test/wireguard_test.go +++ b/test/wireguard_test.go @@ -2,7 +2,6 @@ package main import ( "net/netip" - "os" "testing" "time" @@ -50,49 +49,3 @@ func TestWireGuard(t *testing.T) { }) 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) -} diff --git a/transport/v2raygrpclite/client.go b/transport/v2raygrpclite/client.go index 7205e9c6..313a575a 100644 --- a/transport/v2raygrpclite/client.go +++ b/transport/v2raygrpclite/client.go @@ -13,6 +13,8 @@ import ( "github.com/sagernet/sing-box/option" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" + + "golang.org/x/net/http2" ) var _ adapter.V2RayClientTransport = (*Client)(nil) @@ -27,27 +29,37 @@ type Client struct { ctx context.Context dialer N.Dialer serverAddr M.Socksaddr - transport *http.Transport + transport http.RoundTripper options option.V2RayGRPCOptions url *url.URL } 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)) if err != nil { return nil, err } return tls.ClientHandshake(ctx, conn, tlsConfig) }, - ForceAttemptHTTP2: true, - }, + } + } + return &Client{ + ctx: ctx, + dialer: dialer, + serverAddr: serverAddr, + options: options, + transport: transport, url: &url.URL{ Scheme: "https", Host: serverAddr.String(), diff --git a/transport/v2raygrpclite/server.go b/transport/v2raygrpclite/server.go index 8319baca..c90aa43a 100644 --- a/transport/v2raygrpclite/server.go +++ b/transport/v2raygrpclite/server.go @@ -17,6 +17,8 @@ import ( M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" sHttp "github.com/sagernet/sing/protocol/http" + + "golang.org/x/net/http2" ) var _ adapter.V2RayServerTransport = (*Server)(nil) @@ -87,6 +89,10 @@ func (s *Server) Serve(listener net.Listener) error { if s.httpServer.TLSConfig == nil { return s.httpServer.Serve(listener) } else { + err := http2.ConfigureServer(s.httpServer, &http2.Server{}) + if err != nil { + return err + } return s.httpServer.ServeTLS(listener, "", "") } } diff --git a/transport/v2rayhttp/client.go b/transport/v2rayhttp/client.go index 45ee62ab..3f82c89e 100644 --- a/transport/v2rayhttp/client.go +++ b/transport/v2rayhttp/client.go @@ -16,6 +16,8 @@ import ( E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" + + "golang.org/x/net/http2" ) var _ adapter.V2RayClientTransport = (*Client)(nil) @@ -24,7 +26,7 @@ type Client struct { ctx context.Context dialer N.Dialer serverAddr M.Socksaddr - client *http.Client + transport http.RoundTripper http2 bool url *url.URL 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 { + 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{ ctx: ctx, dialer: dialer, @@ -40,27 +61,9 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt host: options.Host, method: options.Method, headers: make(http.Header), - client: &http.Client{}, + transport: transport, 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 == "" { client.method = "PUT" } @@ -145,17 +148,16 @@ func (c *Client) dialHTTP2(ctx context.Context) (net.Conn, error) { } // Disable any compression method from server. 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 } diff --git a/transport/v2rayhttp/conn.go b/transport/v2rayhttp/conn.go index e99e48b8..9bd1a5de 100644 --- a/transport/v2rayhttp/conn.go +++ b/transport/v2rayhttp/conn.go @@ -14,9 +14,37 @@ import ( type HTTPConn struct { reader io.Reader 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) { + if c.reader == nil { + <-c.create + if c.err != nil { + return 0, c.err + } + } n, err = c.reader.Read(b) return n, baderror.WrapH2(err) } diff --git a/transport/v2rayhttp/server.go b/transport/v2rayhttp/server.go index 8d838349..cba5c57c 100644 --- a/transport/v2rayhttp/server.go +++ b/transport/v2rayhttp/server.go @@ -16,6 +16,8 @@ import ( M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" sHttp "github.com/sagernet/sing/protocol/http" + + "golang.org/x/net/http2" ) 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) } else { conn := &ServerHTTPConn{ - HTTPConn{ - request.Body, - writer, - }, + newHTTPConn(request.Body, writer), writer.(http.Flusher), } s.handler.NewConnection(request.Context(), conn, metadata) @@ -128,6 +127,10 @@ func (s *Server) Serve(listener net.Listener) error { if s.httpServer.TLSConfig == nil { return s.httpServer.Serve(listener) } else { + err := http2.ConfigureServer(s.httpServer, &http2.Server{}) + if err != nil { + return err + } return s.httpServer.ServeTLS(listener, "", "") } }