mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-25 01:51:29 +00:00
Improve tls writer
This commit is contained in:
parent
968430c338
commit
1173fdea64
206
common/badtls/badtls.go
Normal file
206
common/badtls/badtls.go
Normal file
|
@ -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()
|
||||
}
|
12
common/badtls/badtls_stub.go
Normal file
12
common/badtls/badtls_stub.go
Normal file
|
@ -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
|
||||
}
|
13
common/badtls/conn.go
Normal file
13
common/badtls/conn.go
Normal file
|
@ -0,0 +1,13 @@
|
|||
package badtls
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
)
|
||||
|
||||
type TLSConn interface {
|
||||
net.Conn
|
||||
HandshakeContext(ctx context.Context) error
|
||||
ConnectionState() tls.ConnectionState
|
||||
}
|
26
common/badtls/link.go
Normal file
26
common/badtls/link.go
Normal file
|
@ -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
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
2
go.mod
2
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
|
||||
|
|
4
go.sum
4
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=
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
16
test/go.mod
16
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
|
||||
|
|
32
test/go.sum
32
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=
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue