From 1173fdea64a415d39f545eca0779380603afc21d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Fri, 30 Sep 2022 11:27:18 +0800 Subject: [PATCH] Improve tls writer --- common/badtls/badtls.go | 206 +++++++++++++++++++++++++++++ common/badtls/badtls_stub.go | 12 ++ common/badtls/conn.go | 13 ++ common/badtls/link.go | 26 ++++ common/tls/client.go | 14 +- common/tls/server.go | 22 +++ go.mod | 2 +- go.sum | 4 +- inbound/http.go | 6 +- inbound/trojan.go | 6 +- inbound/vmess.go | 6 +- test/go.mod | 16 +-- test/go.sum | 32 ++--- transport/v2raywebsocket/client.go | 2 +- transport/v2raywebsocket/conn.go | 6 +- transport/v2raywebsocket/writer.go | 24 +++- 16 files changed, 358 insertions(+), 39 deletions(-) create mode 100644 common/badtls/badtls.go create mode 100644 common/badtls/badtls_stub.go create mode 100644 common/badtls/conn.go create mode 100644 common/badtls/link.go diff --git a/common/badtls/badtls.go b/common/badtls/badtls.go new file mode 100644 index 00000000..32e54a68 --- /dev/null +++ b/common/badtls/badtls.go @@ -0,0 +1,206 @@ +//go:build go1.19 && !go1.20 + +package badtls + +import ( + "crypto/cipher" + "crypto/rand" + "crypto/tls" + "encoding/binary" + "io" + "net" + "reflect" + "sync" + "sync/atomic" + "unsafe" + + "github.com/sagernet/sing/common" + "github.com/sagernet/sing/common/buf" + "github.com/sagernet/sing/common/bufio" + E "github.com/sagernet/sing/common/exceptions" + N "github.com/sagernet/sing/common/network" +) + +type Conn struct { + *tls.Conn + writer N.ExtendedWriter + activeCall *int32 + closeNotifySent *bool + version *uint16 + rand io.Reader + halfAccess *sync.Mutex + halfError *error + cipher cipher.AEAD + explicitNonceLen int + halfPtr uintptr + halfSeq []byte + halfScratchBuf []byte +} + +func Create(conn *tls.Conn) (TLSConn, error) { + if !handshakeComplete(conn) { + return nil, E.New("handshake not finished") + } + rawConn := reflect.Indirect(reflect.ValueOf(conn)) + rawActiveCall := rawConn.FieldByName("activeCall") + if !rawActiveCall.IsValid() || rawActiveCall.Kind() != reflect.Int32 { + return nil, E.New("badtls: invalid active call") + } + activeCall := (*int32)(unsafe.Pointer(rawActiveCall.UnsafeAddr())) + rawHalfConn := rawConn.FieldByName("out") + if !rawHalfConn.IsValid() || rawHalfConn.Kind() != reflect.Struct { + return nil, E.New("badtls: invalid half conn") + } + rawVersion := rawConn.FieldByName("vers") + if !rawVersion.IsValid() || rawVersion.Kind() != reflect.Uint16 { + return nil, E.New("badtls: invalid version") + } + version := (*uint16)(unsafe.Pointer(rawVersion.UnsafeAddr())) + rawCloseNotifySent := rawConn.FieldByName("closeNotifySent") + if !rawCloseNotifySent.IsValid() || rawCloseNotifySent.Kind() != reflect.Bool { + return nil, E.New("badtls: invalid notify") + } + closeNotifySent := (*bool)(unsafe.Pointer(rawCloseNotifySent.UnsafeAddr())) + rawConfig := reflect.Indirect(rawConn.FieldByName("config")) + if !rawConfig.IsValid() || rawConfig.Kind() != reflect.Struct { + return nil, E.New("badtls: bad config") + } + config := (*tls.Config)(unsafe.Pointer(rawConfig.UnsafeAddr())) + randReader := config.Rand + if randReader == nil { + randReader = rand.Reader + } + rawHalfMutex := rawHalfConn.FieldByName("Mutex") + if !rawHalfMutex.IsValid() || rawHalfMutex.Kind() != reflect.Struct { + return nil, E.New("badtls: invalid half mutex") + } + halfAccess := (*sync.Mutex)(unsafe.Pointer(rawHalfMutex.UnsafeAddr())) + rawHalfError := rawHalfConn.FieldByName("err") + if !rawHalfError.IsValid() || rawHalfError.Kind() != reflect.Interface { + return nil, E.New("badtls: invalid half error") + } + halfError := (*error)(unsafe.Pointer(rawHalfError.UnsafeAddr())) + rawHalfCipherInterface := rawHalfConn.FieldByName("cipher") + if !rawHalfCipherInterface.IsValid() || rawHalfCipherInterface.Kind() != reflect.Interface { + return nil, E.New("badtls: invalid cipher interface") + } + rawHalfCipher := rawHalfCipherInterface.Elem() + aeadCipher, loaded := valueInterface(rawHalfCipher, false).(cipher.AEAD) + if !loaded { + return nil, E.New("badtls: invalid AEAD cipher") + } + var explicitNonceLen int + switch cipherName := reflect.Indirect(rawHalfCipher).Type().String(); cipherName { + case "tls.prefixNonceAEAD": + explicitNonceLen = aeadCipher.NonceSize() + case "tls.xorNonceAEAD": + default: + return nil, E.New("badtls: unknown cipher type: ", cipherName) + } + rawHalfSeq := rawHalfConn.FieldByName("seq") + if !rawHalfSeq.IsValid() || rawHalfSeq.Kind() != reflect.Array { + return nil, E.New("badtls: invalid seq") + } + halfSeq := rawHalfSeq.Bytes() + rawHalfScratchBuf := rawHalfConn.FieldByName("scratchBuf") + if !rawHalfScratchBuf.IsValid() || rawHalfScratchBuf.Kind() != reflect.Array { + return nil, E.New("badtls: invalid scratchBuf") + } + halfScratchBuf := rawHalfScratchBuf.Bytes() + return &Conn{ + Conn: conn, + writer: bufio.NewExtendedWriter(conn.NetConn()), + activeCall: activeCall, + closeNotifySent: closeNotifySent, + version: version, + halfAccess: halfAccess, + halfError: halfError, + cipher: aeadCipher, + explicitNonceLen: explicitNonceLen, + rand: randReader, + halfPtr: rawHalfConn.UnsafeAddr(), + halfSeq: halfSeq, + halfScratchBuf: halfScratchBuf, + }, nil +} + +func (c *Conn) WriteBuffer(buffer *buf.Buffer) error { + if buffer.Len() > maxPlaintext { + defer buffer.Release() + return common.Error(c.Write(buffer.Bytes())) + } + for { + x := atomic.LoadInt32(c.activeCall) + if x&1 != 0 { + return net.ErrClosed + } + if atomic.CompareAndSwapInt32(c.activeCall, x, x+2) { + break + } + } + defer atomic.AddInt32(c.activeCall, -2) + c.halfAccess.Lock() + defer c.halfAccess.Unlock() + if err := *c.halfError; err != nil { + return err + } + if *c.closeNotifySent { + return errShutdown + } + dataLen := buffer.Len() + dataBytes := buffer.Bytes() + outBuf := buffer.ExtendHeader(recordHeaderLen + c.explicitNonceLen) + outBuf[0] = 23 + version := *c.version + if version == 0 { + version = tls.VersionTLS10 + } else if version == tls.VersionTLS13 { + version = tls.VersionTLS12 + } + binary.BigEndian.PutUint16(outBuf[1:], version) + var nonce []byte + if c.explicitNonceLen > 0 { + nonce = outBuf[5 : 5+c.explicitNonceLen] + if c.explicitNonceLen < 16 { + copy(nonce, c.halfSeq) + } else { + if _, err := io.ReadFull(c.rand, nonce); err != nil { + return err + } + } + } + if len(nonce) == 0 { + nonce = c.halfSeq + } + if *c.version == tls.VersionTLS13 { + buffer.FreeBytes()[0] = 23 + binary.BigEndian.PutUint16(outBuf[3:], uint16(dataLen+1+c.cipher.Overhead())) + c.cipher.Seal(outBuf, nonce, outBuf[recordHeaderLen:recordHeaderLen+c.explicitNonceLen+dataLen+1], outBuf[:recordHeaderLen]) + buffer.Extend(1 + c.cipher.Overhead()) + } else { + binary.BigEndian.PutUint16(outBuf[3:], uint16(dataLen)) + additionalData := append(c.halfScratchBuf[:0], c.halfSeq...) + additionalData = append(additionalData, outBuf[:recordHeaderLen]...) + c.cipher.Seal(outBuf, nonce, dataBytes, additionalData) + buffer.Extend(c.cipher.Overhead()) + binary.BigEndian.PutUint16(outBuf[3:], uint16(dataLen+c.explicitNonceLen+c.cipher.Overhead())) + } + incSeq(c.halfPtr) + return c.writer.WriteBuffer(buffer) +} + +func (c *Conn) FrontHeadroom() int { + return recordHeaderLen + c.explicitNonceLen +} + +func (c *Conn) RearHeadroom() int { + return 1 + c.cipher.Overhead() +} + +func (c *Conn) WriterMTU() int { + return maxPlaintext +} + +func (c *Conn) Upstream() any { + return c.NetConn() +} diff --git a/common/badtls/badtls_stub.go b/common/badtls/badtls_stub.go new file mode 100644 index 00000000..c44d8792 --- /dev/null +++ b/common/badtls/badtls_stub.go @@ -0,0 +1,12 @@ +//go:build !go1.19 || go1.20 + +package badtls + +import ( + "crypto/tls" + "os" +) + +func Create(conn *tls.Conn) (TLSConn, error) { + return nil, os.ErrInvalid +} diff --git a/common/badtls/conn.go b/common/badtls/conn.go new file mode 100644 index 00000000..235763dc --- /dev/null +++ b/common/badtls/conn.go @@ -0,0 +1,13 @@ +package badtls + +import ( + "context" + "crypto/tls" + "net" +) + +type TLSConn interface { + net.Conn + HandshakeContext(ctx context.Context) error + ConnectionState() tls.ConnectionState +} diff --git a/common/badtls/link.go b/common/badtls/link.go new file mode 100644 index 00000000..c86c7b49 --- /dev/null +++ b/common/badtls/link.go @@ -0,0 +1,26 @@ +//go:build go1.19 && !go.1.20 + +package badtls + +import ( + "crypto/tls" + "reflect" + _ "unsafe" +) + +const ( + maxPlaintext = 16384 // maximum plaintext payload length + recordHeaderLen = 5 // record header length +) + +//go:linkname errShutdown crypto/tls.errShutdown +var errShutdown error + +//go:linkname handshakeComplete crypto/tls.(*Conn).handshakeComplete +func handshakeComplete(conn *tls.Conn) bool + +//go:linkname incSeq crypto/tls.(*halfConn).incSeq +func incSeq(conn uintptr) + +//go:linkname valueInterface reflect.valueInterface +func valueInterface(v reflect.Value, safe bool) any diff --git a/common/tls/client.go b/common/tls/client.go index 1f28ba1a..28826124 100644 --- a/common/tls/client.go +++ b/common/tls/client.go @@ -2,10 +2,12 @@ package tls import ( "context" + "crypto/tls" "net" "os" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/common/badtls" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/option" M "github.com/sagernet/sing/common/metadata" @@ -35,7 +37,17 @@ func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, e ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout) defer cancel() err := tlsConn.HandshakeContext(ctx) - return tlsConn, err + if err != nil { + return nil, err + } + if stdConn, isSTD := tlsConn.(*tls.Conn); isSTD { + var badConn badtls.TLSConn + badConn, err = badtls.Create(stdConn) + if err == nil { + return badConn, nil + } + } + return tlsConn, nil } type Dialer struct { diff --git a/common/tls/server.go b/common/tls/server.go index a0268682..f8044199 100644 --- a/common/tls/server.go +++ b/common/tls/server.go @@ -2,7 +2,11 @@ package tls import ( "context" + "crypto/tls" + "net" + "github.com/sagernet/sing-box/common/badtls" + C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" ) @@ -10,3 +14,21 @@ import ( func NewServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) { return newSTDServer(ctx, logger, options) } + +func ServerHandshake(ctx context.Context, conn net.Conn, config ServerConfig) (Conn, error) { + tlsConn := config.Server(conn) + ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout) + defer cancel() + err := tlsConn.HandshakeContext(ctx) + if err != nil { + return nil, err + } + if stdConn, isSTD := tlsConn.(*tls.Conn); isSTD { + var badConn badtls.TLSConn + badConn, err = badtls.Create(stdConn) + if err == nil { + return badConn, nil + } + } + return tlsConn, nil +} diff --git a/go.mod b/go.mod index a2d322f3..ce5d1a18 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/pires/go-proxyproto v0.6.2 github.com/refraction-networking/utls v1.1.2 github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb - github.com/sagernet/sing v0.0.0-20220929000216-9a83e35b7186 + github.com/sagernet/sing v0.0.0-20221001030341-348376220066 github.com/sagernet/sing-dns v0.0.0-20220929010544-ee843807aae3 github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 github.com/sagernet/sing-tun v0.0.0-20220929163559-a93592a9b581 diff --git a/go.sum b/go.sum index 70873a4c..0e96c3bb 100644 --- a/go.sum +++ b/go.sum @@ -145,8 +145,8 @@ 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-20220929000216-9a83e35b7186 h1:ZDlgH6dTozS3ODaYq1GxCj+H8NvYESaex90iX72gadw= -github.com/sagernet/sing v0.0.0-20220929000216-9a83e35b7186/go.mod h1:zvgDYKI+vCAW9RyfyrKTgleI+DOa8lzHMPC7VZo3OL4= +github.com/sagernet/sing v0.0.0-20221001030341-348376220066 h1:kQSd+x9ZLBcjl2+VjCLlkxzlD8VO5Q9X3FHDb7OGoN4= +github.com/sagernet/sing v0.0.0-20221001030341-348376220066/go.mod h1:zvgDYKI+vCAW9RyfyrKTgleI+DOa8lzHMPC7VZo3OL4= github.com/sagernet/sing-dns v0.0.0-20220929010544-ee843807aae3 h1:AEdyJxEDFq38z0pBX/0MpikQapGMIch+1ADe9k1bJqU= github.com/sagernet/sing-dns v0.0.0-20220929010544-ee843807aae3/go.mod h1:SrvWLfOSlnFmH32CWXicfilAGgIXR0VjrH6yRbuXYww= github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4= diff --git a/inbound/http.go b/inbound/http.go index fdc5bd2b..e2963db0 100644 --- a/inbound/http.go +++ b/inbound/http.go @@ -72,8 +72,12 @@ func (h *HTTP) Close() error { } func (h *HTTP) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { + var err error if h.tlsConfig != nil { - conn = h.tlsConfig.Server(conn) + conn, err = tls.ServerHandshake(ctx, conn, h.tlsConfig) + if err != nil { + return err + } } return http.HandleConnection(ctx, conn, std_bufio.NewReader(conn), h.authenticator, h.upstreamUserHandler(metadata), adapter.UpstreamMetadata(metadata)) } diff --git a/inbound/trojan.go b/inbound/trojan.go index 49f1a58a..e1f47801 100644 --- a/inbound/trojan.go +++ b/inbound/trojan.go @@ -150,8 +150,12 @@ func (h *Trojan) newTransportConnection(ctx context.Context, conn net.Conn, meta } func (h *Trojan) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { + var err error if h.tlsConfig != nil && h.transport == nil { - conn = h.tlsConfig.Server(conn) + conn, err = tls.ServerHandshake(ctx, conn, h.tlsConfig) + if err != nil { + return err + } } return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata)) } diff --git a/inbound/vmess.go b/inbound/vmess.go index 5f310ff6..5e8d4e31 100644 --- a/inbound/vmess.go +++ b/inbound/vmess.go @@ -130,8 +130,12 @@ func (h *VMess) newTransportConnection(ctx context.Context, conn net.Conn, metad } func (h *VMess) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { + var err error if h.tlsConfig != nil && h.transport == nil { - conn = h.tlsConfig.Server(conn) + conn, err = tls.ServerHandshake(ctx, conn, h.tlsConfig) + if err != nil { + return err + } } return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata)) } diff --git a/test/go.mod b/test/go.mod index 8dacd8f9..4ad70b27 100644 --- a/test/go.mod +++ b/test/go.mod @@ -10,12 +10,12 @@ require ( github.com/docker/docker v20.10.18+incompatible github.com/docker/go-connections v0.4.0 github.com/gofrs/uuid v4.3.0+incompatible - github.com/sagernet/sing v0.0.0-20220921101604-86d7d510231f + github.com/sagernet/sing v0.0.0-20220930130214-cb9b17d6a4a7 github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 github.com/spyzhov/ajson v0.7.1 github.com/stretchr/testify v1.8.0 go.uber.org/goleak v1.2.0 - golang.org/x/net v0.0.0-20220909164309-bea034e7d591 + golang.org/x/net v0.0.0-20220927171203-f486391704dc ) //replace github.com/sagernet/sing => ../../sing @@ -67,9 +67,9 @@ 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-20220915084601-812e0864b45b // indirect - github.com/sagernet/sing-tun v0.0.0-20220922083325-80ee99472704 // indirect - github.com/sagernet/sing-vmess v0.0.0-20220923035311-d70afe4ab2c9 // indirect + github.com/sagernet/sing-dns v0.0.0-20220929010544-ee843807aae3 // indirect + github.com/sagernet/sing-tun v0.0.0-20220929163559-a93592a9b581 // indirect + github.com/sagernet/sing-vmess v0.0.0-20220925083655-063bc85ea685 // indirect github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 // indirect github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e // indirect github.com/sirupsen/logrus v1.9.0 // indirect @@ -78,11 +78,11 @@ require ( go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.22.0 // indirect - go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d // indirect - golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0 // indirect + go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab // indirect + golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be // 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-20220913120320-3275c407cedc // indirect + golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec // 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 diff --git a/test/go.sum b/test/go.sum index 9dc261b7..6a588505 100644 --- a/test/go.sum +++ b/test/go.sum @@ -165,16 +165,16 @@ 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-20220921101604-86d7d510231f h1:GX416thAwyc0vHBOal/qplvdhFgYO2dHD5GqADCJ0Ig= -github.com/sagernet/sing v0.0.0-20220921101604-86d7d510231f/go.mod h1:x3NHUeJBQwV75L51zwmLKQdLtRvR+M4PmXkfQtU1vIY= -github.com/sagernet/sing-dns v0.0.0-20220915084601-812e0864b45b h1:cXCMNJ9heZ+c6l+qUcku60x9KyXo4SOAaJfg/6spOmU= -github.com/sagernet/sing-dns v0.0.0-20220915084601-812e0864b45b/go.mod h1:SrvWLfOSlnFmH32CWXicfilAGgIXR0VjrH6yRbuXYww= +github.com/sagernet/sing v0.0.0-20220930130214-cb9b17d6a4a7 h1:VQqdVA6/ctKVOeKHKy9ZYMeB7TqxnP0jvK/Ig9Y+pG8= +github.com/sagernet/sing v0.0.0-20220930130214-cb9b17d6a4a7/go.mod h1:zvgDYKI+vCAW9RyfyrKTgleI+DOa8lzHMPC7VZo3OL4= +github.com/sagernet/sing-dns v0.0.0-20220929010544-ee843807aae3 h1:AEdyJxEDFq38z0pBX/0MpikQapGMIch+1ADe9k1bJqU= +github.com/sagernet/sing-dns v0.0.0-20220929010544-ee843807aae3/go.mod h1:SrvWLfOSlnFmH32CWXicfilAGgIXR0VjrH6yRbuXYww= 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-20220922083325-80ee99472704 h1:DOQQXQbB2gq4n2FuMHrL07HRs2naCCsuiu/9l1JFb9A= -github.com/sagernet/sing-tun v0.0.0-20220922083325-80ee99472704/go.mod h1:5AhPUv9jWDQ3pv3Mj78SL/1TSjhoaj6WNASxRKLqXqM= -github.com/sagernet/sing-vmess v0.0.0-20220923035311-d70afe4ab2c9 h1:w7JlEa4xHXuuPTL3Opb+Z5f/l66SYi54rSfobzuxSF8= -github.com/sagernet/sing-vmess v0.0.0-20220923035311-d70afe4ab2c9/go.mod h1:bwhAdSNET1X+j9DOXGj9NIQR39xgcWIk1rOQ9lLD+gM= +github.com/sagernet/sing-tun v0.0.0-20220929163559-a93592a9b581 h1:ghpmEsGVdFQys5jxy01aVQvhbbUT2c4kJRZXHZBNerw= +github.com/sagernet/sing-tun v0.0.0-20220929163559-a93592a9b581/go.mod h1:qbqV9lwcXJnj1Tw4we7oA6Z8zGE/kCXQBCzuhzRWVw8= +github.com/sagernet/sing-vmess v0.0.0-20220925083655-063bc85ea685 h1:AZzFNRR/ZwMTceUQ1b/mxx6oyKqmFymdMn/yleJmoVM= +github.com/sagernet/sing-vmess v0.0.0-20220925083655-063bc85ea685/go.mod h1:bwhAdSNET1X+j9DOXGj9NIQR39xgcWIk1rOQ9lLD+gM= 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/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs= @@ -213,16 +213,16 @@ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9i go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= go.uber.org/zap v1.22.0 h1:Zcye5DUgBloQ9BaT4qc9BnjOFog5TvBSAGkJ3Nf70c0= go.uber.org/zap v1.22.0/go.mod h1:H4siCOZOrAolnUPJEkfaSjDqyP+BDS0DdDWzwcgt3+U= -go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d h1:ggxwEf5eu0l8v+87VhX1czFh8zJul3hK16Gmruxn7hw= -go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc= +go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab h1:+yW1yrZ09EYNu1spCUOHBBNRbrLnfmutwyhbhCv3b6Q= +go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0 h1:a5Yg6ylndHHYJqIPrdq0AhvR6KTvDTAvgBtaidhEevY= -golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be h1:fmw3UbQh+nxngCAHrDCCztao/kbYFnWjoqop8dHx05A= +golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA= golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= @@ -257,8 +257,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-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/net v0.0.0-20220927171203-f486391704dc h1:FxpXZdoBqT8RjqTy6i1E8nXHhW21wK7ptQ/EPIGxzPQ= +golang.org/x/net v0.0.0-20220927171203-f486391704dc/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= @@ -298,8 +298,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/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-20220913120320-3275c407cedc h1:dpclq5m2YrqPGStKmtw7IcNbKLfbIqKXvNxDJKdIKYc= -golang.org/x/sys v0.0.0-20220913120320-3275c407cedc/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI= +golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/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= diff --git a/transport/v2raywebsocket/client.go b/transport/v2raywebsocket/client.go index f449ff53..f0f9bddf 100644 --- a/transport/v2raywebsocket/client.go +++ b/transport/v2raywebsocket/client.go @@ -74,7 +74,7 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) { if c.maxEarlyData <= 0 { conn, response, err := c.dialer.DialContext(ctx, c.uri, c.headers) if err == nil { - return &WebsocketConn{Conn: conn, Writer: &Writer{conn, false}}, nil + return &WebsocketConn{Conn: conn, Writer: NewWriter(conn, false)}, nil } return nil, wrapDialError(response, err) } else { diff --git a/transport/v2raywebsocket/conn.go b/transport/v2raywebsocket/conn.go index 455e1312..c0d0770e 100644 --- a/transport/v2raywebsocket/conn.go +++ b/transport/v2raywebsocket/conn.go @@ -26,7 +26,7 @@ func NewServerConn(wsConn *websocket.Conn, remoteAddr net.Addr) *WebsocketConn { return &WebsocketConn{ Conn: wsConn, remoteAddr: remoteAddr, - Writer: &Writer{wsConn, true}, + Writer: NewWriter(wsConn, true), } } @@ -117,7 +117,7 @@ func (c *EarlyWebsocketConn) Write(b []byte) (n int, err error) { if err != nil { return 0, wrapDialError(response, err) } - c.conn = &WebsocketConn{Conn: conn, Writer: &Writer{conn, false}} + c.conn = &WebsocketConn{Conn: conn, Writer: NewWriter(conn, false)} close(c.create) if len(lateData) > 0 { _, err = c.conn.Write(lateData) @@ -160,7 +160,7 @@ func (c *EarlyWebsocketConn) WriteBuffer(buffer *buf.Buffer) error { if err != nil { return wrapDialError(response, err) } - c.conn = &WebsocketConn{Conn: conn, Writer: &Writer{conn, false}} + c.conn = &WebsocketConn{Conn: conn, Writer: NewWriter(conn, false)} close(c.create) if len(lateData) > 0 { _, err = c.conn.Write(lateData) diff --git a/transport/v2raywebsocket/writer.go b/transport/v2raywebsocket/writer.go index ba0b145e..0d2d45ac 100644 --- a/transport/v2raywebsocket/writer.go +++ b/transport/v2raywebsocket/writer.go @@ -4,8 +4,9 @@ import ( "encoding/binary" "math/rand" - "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/buf" + "github.com/sagernet/sing/common/bufio" + N "github.com/sagernet/sing/common/network" "github.com/sagernet/websocket" ) @@ -13,9 +14,18 @@ const frontHeadroom = 14 type Writer struct { *websocket.Conn + writer N.ExtendedWriter isServer bool } +func NewWriter(conn *websocket.Conn, isServer bool) *Writer { + return &Writer{ + conn, + bufio.NewExtendedWriter(conn.NetConn()), + isServer, + } +} + func (w *Writer) Write(p []byte) (n int, err error) { err = w.Conn.WriteMessage(websocket.BinaryMessage, p) if err != nil { @@ -25,8 +35,6 @@ func (w *Writer) Write(p []byte) (n int, err error) { } func (w *Writer) WriteBuffer(buffer *buf.Buffer) error { - defer buffer.Release() - var payloadBitLength int dataLen := buffer.Len() data := buffer.Bytes() @@ -69,5 +77,13 @@ func (w *Writer) WriteBuffer(buffer *buf.Buffer) error { maskBytes(*(*[4]byte)(header[1+payloadBitLength:]), 0, data) } - return common.Error(w.Conn.NetConn().Write(buffer.Bytes())) + return w.writer.WriteBuffer(buffer) +} + +func (w *Writer) Upstream() any { + return w.Conn.NetConn() +} + +func (w *Writer) FrontHeadroom() int { + return frontHeadroom }