Improve read wait interface &

Refactor Authenticator interface to struct &
Update smux &
Update gVisor to 20231204.0 &
Update quic-go to v0.40.1 &
Update wireguard-go &
Add GSO support for TUN/WireGuard &
Fix router pre-start &
Fix bind forwarder to interface for systems stack
This commit is contained in:
世界 2023-12-20 20:00:00 +08:00
parent 35fd9de3ff
commit 89c723e3e4
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
48 changed files with 902 additions and 658 deletions

View file

@ -17,6 +17,7 @@ import (
type Router interface { type Router interface {
Service Service
PreStarter
PostStarter PostStarter
Outbounds() []Outbound Outbounds() []Outbound

9
box.go
View file

@ -259,6 +259,10 @@ func (s *Box) preStart() error {
} }
} }
} }
err = s.router.PreStart()
if err != nil {
return E.Cause(err, "pre-start router")
}
err = s.startOutbounds() err = s.startOutbounds()
if err != nil { if err != nil {
return err return err
@ -313,10 +317,7 @@ func (s *Box) postStart() error {
} }
} }
} }
err := s.router.PostStart()
if err != nil {
return E.Cause(err, "post-start router")
}
return s.router.PostStart() return s.router.PostStart()
} }

View file

@ -1,233 +0,0 @@
//go:build go1.20 && !go1.21
package badtls
import (
"crypto/cipher"
"crypto/rand"
"crypto/tls"
"encoding/binary"
"io"
"net"
"reflect"
"sync"
"sync/atomic"
"unsafe"
"github.com/sagernet/sing-box/log"
"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"
aTLS "github.com/sagernet/sing/common/tls"
)
type Conn struct {
*tls.Conn
writer N.ExtendedWriter
isHandshakeComplete *atomic.Bool
activeCall *atomic.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 TryCreate(conn aTLS.Conn) aTLS.Conn {
tlsConn, ok := conn.(*tls.Conn)
if !ok {
return conn
}
badConn, err := Create(tlsConn)
if err != nil {
log.Warn("initialize badtls: ", err)
return conn
}
return badConn
}
func Create(conn *tls.Conn) (aTLS.Conn, error) {
rawConn := reflect.Indirect(reflect.ValueOf(conn))
rawIsHandshakeComplete := rawConn.FieldByName("isHandshakeComplete")
if !rawIsHandshakeComplete.IsValid() || rawIsHandshakeComplete.Kind() != reflect.Struct {
return nil, E.New("badtls: invalid isHandshakeComplete")
}
isHandshakeComplete := (*atomic.Bool)(unsafe.Pointer(rawIsHandshakeComplete.UnsafeAddr()))
if !isHandshakeComplete.Load() {
return nil, E.New("handshake not finished")
}
rawActiveCall := rawConn.FieldByName("activeCall")
if !rawActiveCall.IsValid() || rawActiveCall.Kind() != reflect.Struct {
return nil, E.New("badtls: invalid active call")
}
activeCall := (*atomic.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()),
isHandshakeComplete: isHandshakeComplete,
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 := c.activeCall.Load()
if x&1 != 0 {
return net.ErrClosed
}
if c.activeCall.CompareAndSwap(x, x+2) {
break
}
}
defer c.activeCall.Add(-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)
log.Trace("badtls write ", buffer.Len())
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.Conn
}
func (c *Conn) UpstreamWriter() any {
return c.NetConn()
}

View file

@ -1,14 +0,0 @@
//go:build !go1.19 || go1.21
package badtls
import (
"crypto/tls"
"os"
aTLS "github.com/sagernet/sing/common/tls"
)
func Create(conn *tls.Conn) (aTLS.Conn, error) {
return nil, os.ErrInvalid
}

View file

@ -1,22 +0,0 @@
//go:build go1.20 && !go.1.21
package badtls
import (
"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 incSeq crypto/tls.(*halfConn).incSeq
func incSeq(conn uintptr)
//go:linkname valueInterface reflect.valueInterface
func valueInterface(v reflect.Value, safe bool) any

115
common/badtls/read_wait.go Normal file
View file

@ -0,0 +1,115 @@
//go:build go1.21 && !without_badtls
package badtls
import (
"bytes"
"os"
"reflect"
"sync"
"unsafe"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/tls"
)
var _ N.ReadWaiter = (*ReadWaitConn)(nil)
type ReadWaitConn struct {
*tls.STDConn
halfAccess *sync.Mutex
rawInput *bytes.Buffer
input *bytes.Reader
hand *bytes.Buffer
readWaitOptions N.ReadWaitOptions
}
func NewReadWaitConn(conn tls.Conn) (tls.Conn, error) {
stdConn, isSTDConn := conn.(*tls.STDConn)
if !isSTDConn {
return nil, os.ErrInvalid
}
rawConn := reflect.Indirect(reflect.ValueOf(stdConn))
rawHalfConn := rawConn.FieldByName("in")
if !rawHalfConn.IsValid() || rawHalfConn.Kind() != reflect.Struct {
return nil, E.New("badtls: invalid half conn")
}
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()))
rawRawInput := rawConn.FieldByName("rawInput")
if !rawRawInput.IsValid() || rawRawInput.Kind() != reflect.Struct {
return nil, E.New("badtls: invalid raw input")
}
rawInput := (*bytes.Buffer)(unsafe.Pointer(rawRawInput.UnsafeAddr()))
rawInput0 := rawConn.FieldByName("input")
if !rawInput0.IsValid() || rawInput0.Kind() != reflect.Struct {
return nil, E.New("badtls: invalid input")
}
input := (*bytes.Reader)(unsafe.Pointer(rawInput0.UnsafeAddr()))
rawHand := rawConn.FieldByName("hand")
if !rawHand.IsValid() || rawHand.Kind() != reflect.Struct {
return nil, E.New("badtls: invalid hand")
}
hand := (*bytes.Buffer)(unsafe.Pointer(rawHand.UnsafeAddr()))
return &ReadWaitConn{
STDConn: stdConn,
halfAccess: halfAccess,
rawInput: rawInput,
input: input,
hand: hand,
}, nil
}
func (c *ReadWaitConn) InitializeReadWaiter(options N.ReadWaitOptions) (needCopy bool) {
c.readWaitOptions = options
return false
}
func (c *ReadWaitConn) WaitReadBuffer() (buffer *buf.Buffer, err error) {
err = c.Handshake()
if err != nil {
return
}
c.halfAccess.Lock()
defer c.halfAccess.Unlock()
for c.input.Len() == 0 {
err = tlsReadRecord(c.STDConn)
if err != nil {
return
}
for c.hand.Len() > 0 {
err = tlsHandlePostHandshakeMessage(c.STDConn)
if err != nil {
return
}
}
}
buffer = c.readWaitOptions.NewBuffer()
n, err := c.input.Read(buffer.FreeBytes())
if err != nil {
buffer.Release()
return
}
buffer.Truncate(n)
if n != 0 && c.input.Len() == 0 && c.rawInput.Len() > 0 &&
// recordType(c.rawInput.Bytes()[0]) == recordTypeAlert {
c.rawInput.Bytes()[0] == 21 {
_ = tlsReadRecord(c.STDConn)
// return n, err // will be io.EOF on closeNotify
}
c.readWaitOptions.PostReturn(buffer)
return
}
//go:linkname tlsReadRecord crypto/tls.(*Conn).readRecord
func tlsReadRecord(c *tls.STDConn) error
//go:linkname tlsHandlePostHandshakeMessage crypto/tls.(*Conn).handlePostHandshakeMessage
func tlsHandlePostHandshakeMessage(c *tls.STDConn) error

View file

@ -0,0 +1,13 @@
//go:build !go1.21 || without_badtls
package badtls
import (
"os"
"github.com/sagernet/sing/common/tls"
)
func NewReadWaitConn(conn tls.Conn) (tls.Conn, error) {
return nil, os.ErrInvalid
}

View file

@ -15,14 +15,17 @@ import (
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
) )
var _ WireGuardListener = (*DefaultDialer)(nil)
type DefaultDialer struct { type DefaultDialer struct {
dialer4 tcpDialer dialer4 tcpDialer
dialer6 tcpDialer dialer6 tcpDialer
udpDialer4 net.Dialer udpDialer4 net.Dialer
udpDialer6 net.Dialer udpDialer6 net.Dialer
udpListener net.ListenConfig udpListener net.ListenConfig
udpAddr4 string udpAddr4 string
udpAddr6 string udpAddr6 string
isWireGuardListener bool
} }
func NewDefault(router adapter.Router, options option.DialerOptions) (*DefaultDialer, error) { func NewDefault(router adapter.Router, options option.DialerOptions) (*DefaultDialer, error) {
@ -98,6 +101,11 @@ func NewDefault(router adapter.Router, options option.DialerOptions) (*DefaultDi
} }
setMultiPathTCP(&dialer4) setMultiPathTCP(&dialer4)
} }
if options.IsWireGuardListener {
for _, controlFn := range wgControlFns {
listener.Control = control.Append(listener.Control, controlFn)
}
}
tcpDialer4, err := newTCPDialer(dialer4, options.TCPFastOpen) tcpDialer4, err := newTCPDialer(dialer4, options.TCPFastOpen)
if err != nil { if err != nil {
return nil, err return nil, err
@ -114,6 +122,7 @@ func NewDefault(router adapter.Router, options option.DialerOptions) (*DefaultDi
listener, listener,
udpAddr4, udpAddr4,
udpAddr6, udpAddr6,
options.IsWireGuardListener,
}, nil }, nil
} }
@ -146,6 +155,10 @@ func (d *DefaultDialer) ListenPacket(ctx context.Context, destination M.Socksadd
} }
} }
func (d *DefaultDialer) ListenPacketCompat(network, address string) (net.PacketConn, error) {
return trackPacketConn(d.udpListener.ListenPacket(context.Background(), network, address))
}
func trackConn(conn net.Conn, err error) (net.Conn, error) { func trackConn(conn net.Conn, err error) (net.Conn, error) {
if !conntrack.Enabled || err != nil { if !conntrack.Enabled || err != nil {
return conn, err return conn, err

View file

@ -6,15 +6,13 @@ 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-dns" "github.com/sagernet/sing-dns"
"github.com/sagernet/sing/common"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
) )
func MustNew(router adapter.Router, options option.DialerOptions) N.Dialer {
return common.Must1(New(router, options))
}
func New(router adapter.Router, options option.DialerOptions) (N.Dialer, error) { func New(router adapter.Router, options option.DialerOptions) (N.Dialer, error) {
if options.IsWireGuardListener {
return NewDefault(router, options)
}
var ( var (
dialer N.Dialer dialer N.Dialer
err error err error

View file

@ -0,0 +1,9 @@
package dialer
import (
"net"
)
type WireGuardListener interface {
ListenPacketCompat(network, address string) (net.PacketConn, error)
}

View file

@ -0,0 +1,11 @@
//go:build with_wireguard
package dialer
import (
"github.com/sagernet/wireguard-go/conn"
)
var _ WireGuardListener = (conn.Listener)(nil)
var wgControlFns = conn.ControlFns

View file

@ -0,0 +1,9 @@
//go:build !with_wireguard
package dialer
import (
"github.com/sagernet/sing/common/control"
)
var wgControlFns []control.Func

View file

@ -6,6 +6,7 @@ import (
"os" "os"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/badtls"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"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"
@ -42,7 +43,17 @@ func NewClient(ctx context.Context, serverAddress string, options option.Outboun
func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, error) { func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, error) {
ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout) ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout)
defer cancel() defer cancel()
return aTLS.ClientHandshake(ctx, conn, config) tlsConn, err := aTLS.ClientHandshake(ctx, conn, config)
if err != nil {
return nil, err
}
readWaitConn, err := badtls.NewReadWaitConn(tlsConn)
if err == nil {
return readWaitConn, nil
} else if err != os.ErrInvalid {
return nil, err
}
return tlsConn, nil
} }
type Dialer struct { type Dialer struct {

View file

@ -3,7 +3,9 @@ package tls
import ( import (
"context" "context"
"net" "net"
"os"
"github.com/sagernet/sing-box/common/badtls"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
@ -26,5 +28,15 @@ func NewServer(ctx context.Context, logger log.Logger, options option.InboundTLS
func ServerHandshake(ctx context.Context, conn net.Conn, config ServerConfig) (Conn, error) { func ServerHandshake(ctx context.Context, conn net.Conn, config ServerConfig) (Conn, error) {
ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout) ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout)
defer cancel() defer cancel()
return aTLS.ServerHandshake(ctx, conn, config) tlsConn, err := aTLS.ServerHandshake(ctx, conn, config)
if err != nil {
return nil, err
}
readWaitConn, err := badtls.NewReadWaitConn(tlsConn)
if err == nil {
return readWaitConn, nil
} else if err != os.ErrInvalid {
return nil, err
}
return tlsConn, nil
} }

View file

@ -18,6 +18,7 @@ SFA provides an unprivileged TUN implementation through Android VpnService.
| `inet4_address` | :material-check: | / | | `inet4_address` | :material-check: | / |
| `inet6_address` | :material-check: | / | | `inet6_address` | :material-check: | / |
| `mtu` | :material-check: | / | | `mtu` | :material-check: | / |
| `gso` | :material-close: | No permission |
| `auto_route` | :material-check: | / | | `auto_route` | :material-check: | / |
| `strict_route` | :material-close: | Not implemented | | `strict_route` | :material-close: | Not implemented |
| `inet4_route_address` | :material-check: | / | | `inet4_route_address` | :material-check: | / |

View file

@ -14,28 +14,29 @@ SFI/SFM/SFT allows you to run sing-box through NetworkExtension with Application
SFI/SFM/SFT provides an unprivileged TUN implementation through NetworkExtension. SFI/SFM/SFT provides an unprivileged TUN implementation through NetworkExtension.
| TUN inbound option | Available | Note | | TUN inbound option | Available | Note |
|-------------------------------|-----------|-------------------| |-------------------------------|-------------------|-------------------|
| `interface_name` | ✖️ | Managed by Darwin | | `interface_name` | :material-close: | Managed by Darwin |
| `inet4_address` | ✔️ | / | | `inet4_address` | :material-check: | / |
| `inet6_address` | ✔️ | / | | `inet6_address` | :material-check: | / |
| `mtu` | ✔️ | / | | `mtu` | :material-check: | / |
| `auto_route` | ✔️ | / | | `gso` | :material-close: | Not implemented |
| `strict_route` | ✖️ | Not implemented | | `auto_route` | :material-check: | / |
| `inet4_route_address` | ✔️ | / | | `strict_route` | :material-close: | Not implemented |
| `inet6_route_address` | ✔️ | / | | `inet4_route_address` | :material-check: | / |
| `inet4_route_exclude_address` | ✔️ | / | | `inet6_route_address` | :material-check: | / |
| `inet6_route_exclude_address` | ✔️ | / | | `inet4_route_exclude_address` | :material-check: | / |
| `endpoint_independent_nat` | ✔️ | / | | `inet6_route_exclude_address` | :material-check: | / |
| `stack` | ✔️ | / | | `endpoint_independent_nat` | :material-check: | / |
| `include_interface` | ✖️ | Not implemented | | `stack` | :material-check: | / |
| `exclude_interface` | ✖️ | Not implemented | | `include_interface` | :material-close: | Not implemented |
| `include_uid` | ✖️ | Not implemented | | `exclude_interface` | :material-close: | Not implemented |
| `exclude_uid` | ✖️ | Not implemented | | `include_uid` | :material-close: | Not implemented |
| `include_android_user` | ✖️ | Not implemented | | `exclude_uid` | :material-close: | Not implemented |
| `include_package` | ✖️ | Not implemented | | `include_android_user` | :material-close: | Not implemented |
| `exclude_package` | ✖️ | Not implemented | | `include_package` | :material-close: | Not implemented |
| `platform` | ✔️ | / | | `exclude_package` | :material-close: | Not implemented |
| `platform` | :material-check: | / |
| Route/DNS rule option | Available | Note | | Route/DNS rule option | Available | Note |
|-----------------------|------------------|-----------------------| |-----------------------|------------------|-----------------------|

View file

@ -1,3 +1,12 @@
---
icon: material/alert-decagram
---
!!! quote "Changes in sing-box 1.8.0"
:material-plus: [gso](#gso)
:material-alert-decagram: [stack](#stack)
!!! quote "" !!! quote ""
Only supported on Linux, Windows and macOS. Only supported on Linux, Windows and macOS.
@ -12,6 +21,7 @@
"inet4_address": "172.19.0.1/30", "inet4_address": "172.19.0.1/30",
"inet6_address": "fdfe:dcba:9876::1/126", "inet6_address": "fdfe:dcba:9876::1/126",
"mtu": 9000, "mtu": 9000,
"gso": false,
"auto_route": true, "auto_route": true,
"strict_route": true, "strict_route": true,
"inet4_route_address": [ "inet4_route_address": [
@ -99,6 +109,16 @@ IPv6 prefix for the tun interface.
The maximum transmission unit. The maximum transmission unit.
#### gso
!!! question "Since sing-box 1.8.0"
!!! quote ""
Only supported on Linux.
Enable generic segmentation offload.
#### auto_route #### auto_route
Set the default route to the Tun. Set the default route to the Tun.
@ -161,18 +181,19 @@ UDP NAT expiration time in seconds, default is 300 (5 minutes).
#### stack #### stack
!!! quote "Changes in sing-box 1.8.0"
:material-delete-alert: The legacy LWIP stack has been deprecated and removed.
TCP/IP stack. TCP/IP stack.
| Stack | Description | Status | | Stack | Description |
|--------|----------------------------------------------------------------------------------|-------------------| |----------|-------------------------------------------------------------------------------------------------------|
| system | Sometimes better performance | recommended | | `system` | Perform L3 to L4 translation using the system network stack |
| gVisor | Better compatibility, based on [google/gvisor](https://github.com/google/gvisor) | recommended | | `gvisor` | Perform L3 to L4 translation using [gVisor](https://github.com/google/gvisor)'s virtual network stack |
| mixed | Mixed `system` TCP stack and `gVisor` UDP stack | recommended | | `mixed` | Mixed `system` TCP stack and `gvisor` UDP stack |
| LWIP | Based on [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | upstream archived |
!!! warning "" Defaults to the `mixed` stack if the gVisor build tag is enabled, otherwise defaults to the `system` stack.
LWIP stacks is not included by default, see [Installation](/installation/build-from-source/#build-tags).
#### include_interface #### include_interface
@ -218,10 +239,10 @@ Exclude users in route, but in range.
Limit android users in route. Limit android users in route.
| Common user | ID | | Common user | ID |
|--------------|-----| |--------------|----|
| Main | 0 | | Main | 0 |
| Work Profile | 10 | | Work Profile | 10 |
#### include_package #### include_package

View file

@ -1,3 +1,12 @@
---
icon: material/alert-decagram
---
!!! quote "sing-box 1.8.0 中的更改"
:material-plus: [gso](#gso)
:material-alert-decagram: [stack](#stack)
!!! quote "" !!! quote ""
仅支持 Linux、Windows 和 macOS。 仅支持 Linux、Windows 和 macOS。
@ -12,6 +21,7 @@
"inet4_address": "172.19.0.1/30", "inet4_address": "172.19.0.1/30",
"inet6_address": "fdfe:dcba:9876::1/126", "inet6_address": "fdfe:dcba:9876::1/126",
"mtu": 9000, "mtu": 9000,
"gso": false,
"auto_route": true, "auto_route": true,
"strict_route": true, "strict_route": true,
"inet4_route_address": [ "inet4_route_address": [
@ -99,6 +109,16 @@ tun 接口的 IPv6 前缀。
最大传输单元。 最大传输单元。
#### gso
!!! question "自 sing-box 1.8.0 起"
!!! quote ""
仅支持 Linux。
启用通用分段卸载。
#### auto_route #### auto_route
设置到 Tun 的默认路由。 设置到 Tun 的默认路由。
@ -158,17 +178,19 @@ UDP NAT 过期时间,以秒为单位,默认为 3005 分钟)。
#### stack #### stack
!!! quote "sing-box 1.8.0 中的更改"
:material-delete-alert: 旧的 LWIP 栈已被弃用并移除。
TCP/IP 栈。 TCP/IP 栈。
| 栈 | 描述 | 状态 | | 栈 | 描述 |
|-------------|--------------------------------------------------------------------------|-------| |--------|------------------------------------------------------------------|
| system (默认) | 有时性能更好 | 推荐 | | system | 基于系统网络栈执行 L3 到 L4 转换 |
| gVisor | 兼容性较好,基于 [google/gvisor](https://github.com/google/gvisor) | 推荐 | | gVisor | 基于 [gVisor](https://github.com/google/gvisor) 虚拟网络栈执行 L3 到 L4 转换 |
| LWIP | 基于 [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | 上游已存档 | | mixed | 混合 `system` TCP 栈与 `gvisor` UDP 栈 |
!!! warning "" 默认使用 `mixed` 栈如果 gVisor 构建标记已启用,否则默认使用 `system` 栈。
默认安装不包含 LWIP 栈,参阅 [安装](/zh/installation/build-from-source/#_5)。
#### include_interface #### include_interface
@ -215,8 +237,8 @@ TCP/IP 栈。
限制被路由的 Android 用户。 限制被路由的 Android 用户。
| 常用用户 | ID | | 常用用户 | ID |
|--|-----| |------|----|
| 您 | 0 | | 您 | 0 |
| 工作资料 | 10 | | 工作资料 | 10 |
#### include_package #### include_package

View file

@ -1,3 +1,11 @@
---
icon: material/new-box
---
!!! quote "Changes in sing-box 1.8.0"
:material-plus: [gso](#gso)
### Structure ### Structure
```json ```json
@ -8,6 +16,7 @@
"server": "127.0.0.1", "server": "127.0.0.1",
"server_port": 1080, "server_port": 1080,
"system_interface": false, "system_interface": false,
"gso": false,
"interface_name": "wg0", "interface_name": "wg0",
"local_address": [ "local_address": [
"10.0.0.2/32" "10.0.0.2/32"
@ -52,15 +61,25 @@ The server port.
#### system_interface #### system_interface
Use system tun support. Use system interface.
Requires privilege and cannot conflict with system interfaces. Requires privilege and cannot conflict with exists system interfaces.
Forced if gVisor not included in the build. Forced if gVisor not included in the build.
#### interface_name #### interface_name
Custom device name when `system_interface` enabled. Custom interface name for system interface.
#### gso
!!! question "Since sing-box 1.8.0"
!!! quote ""
Only supported on Linux.
Try to enable generic segmentation offload.
#### local_address #### local_address

View file

@ -1,3 +1,11 @@
---
icon: material/new-box
---
!!! quote "sing-box 1.8.0 中的更改"
:material-plus: [gso](#gso)
### 结构 ### 结构
```json ```json
@ -8,6 +16,7 @@
"server": "127.0.0.1", "server": "127.0.0.1",
"server_port": 1080, "server_port": 1080,
"system_interface": false, "system_interface": false,
"gso": false,
"interface_name": "wg0", "interface_name": "wg0",
"local_address": [ "local_address": [
"10.0.0.2/32" "10.0.0.2/32"
@ -40,15 +49,25 @@
#### system_interface #### system_interface
使用系统 tun 支持 使用系统设备
需要特权且不能与系统接口冲突。 需要特权且不能与已有系统接口冲突。
如果 gVisor 未包含在构建中,则强制执行。 如果 gVisor 未包含在构建中,则强制执行。
#### interface_name #### interface_name
启用 `system_interface` 时的自定义设备名称。 为系统接口自定义设备名称。
#### gso
!!! question "自 sing-box 1.8.0 起"
!!! quote ""
仅支持 Linux。
尝试启用通用分段卸载。
#### local_address #### local_address

View file

@ -55,18 +55,17 @@ go build -tags "tag_a tag_b" ./cmd/sing-box
| Build Tag | Enabled by default | Description | | Build Tag | Enabled by default | Description |
|------------------------------------|--------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |------------------------------------|--------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `with_quic` | ✔ | Build with QUIC support, see [QUIC and HTTP3 DNS transports](/configuration/dns/server/), [Naive inbound](/configuration/inbound/naive/), [Hysteria Inbound](/configuration/inbound/hysteria/), [Hysteria Outbound](/configuration/outbound/hysteria/) and [V2Ray Transport#QUIC](/configuration/shared/v2ray-transport#quic). | | `with_quic` | :material-check: | Build with QUIC support, see [QUIC and HTTP3 DNS transports](/configuration/dns/server/), [Naive inbound](/configuration/inbound/naive/), [Hysteria Inbound](/configuration/inbound/hysteria/), [Hysteria Outbound](/configuration/outbound/hysteria/) and [V2Ray Transport#QUIC](/configuration/shared/v2ray-transport#quic). |
| `with_grpc` | ✖️ | Build with standard gRPC support, see [V2Ray Transport#gRPC](/configuration/shared/v2ray-transport#grpc). | | `with_grpc` | :material-close: | Build with standard gRPC support, see [V2Ray Transport#gRPC](/configuration/shared/v2ray-transport#grpc). |
| `with_dhcp` | ✔ | Build with DHCP support, see [DHCP DNS transport](/configuration/dns/server/). | | `with_dhcp` | :material-check: | Build with DHCP support, see [DHCP DNS transport](/configuration/dns/server/). |
| `with_wireguard` | ✔ | Build with WireGuard support, see [WireGuard outbound](/configuration/outbound/wireguard/). | | `with_wireguard` | :material-check: | Build with WireGuard support, see [WireGuard outbound](/configuration/outbound/wireguard/). |
| `with_ech` | ✔ | Build with TLS ECH extension support for TLS outbound, see [TLS](/configuration/shared/tls#ech). | | `with_ech` | :material-check: | Build with TLS ECH extension support for TLS outbound, see [TLS](/configuration/shared/tls#ech). |
| `with_utls` | ✔ | Build with [uTLS](https://github.com/refraction-networking/utls) support for TLS outbound, see [TLS](/configuration/shared/tls#utls). | | `with_utls` | :material-check: | Build with [uTLS](https://github.com/refraction-networking/utls) support for TLS outbound, see [TLS](/configuration/shared/tls#utls). |
| `with_reality_server` | ✔ | Build with reality TLS server support, see [TLS](/configuration/shared/tls/). | | `with_reality_server` | :material-check: | Build with reality TLS server support, see [TLS](/configuration/shared/tls/). |
| `with_acme` | ✔ | Build with ACME TLS certificate issuer support, see [TLS](/configuration/shared/tls/). | | `with_acme` | :material-check: | Build with ACME TLS certificate issuer support, see [TLS](/configuration/shared/tls/). |
| `with_clash_api` | ✔ | Build with Clash API support, see [Experimental](/configuration/experimental#clash-api-fields). | | `with_clash_api` | :material-check: | Build with Clash API support, see [Experimental](/configuration/experimental#clash-api-fields). |
| `with_v2ray_api` | ✖️ | Build with V2Ray API support, see [Experimental](/configuration/experimental#v2ray-api-fields). | | `with_v2ray_api` | :material-close: | Build with V2Ray API support, see [Experimental](/configuration/experimental#v2ray-api-fields). |
| `with_gvisor` | ✔ | Build with gVisor support, see [Tun inbound](/configuration/inbound/tun#stack) and [WireGuard outbound](/configuration/outbound/wireguard#system_interface). | | `with_gvisor` | :material-check: | Build with gVisor support, see [Tun inbound](/configuration/inbound/tun#stack) and [WireGuard outbound](/configuration/outbound/wireguard#system_interface). |
| `with_embedded_tor` (CGO required) | ✖️ | Build with embedded Tor support, see [Tor outbound](/configuration/outbound/tor/). | | `with_embedded_tor` (CGO required) | :material-close: | Build with embedded Tor support, see [Tor outbound](/configuration/outbound/tor/). |
| `with_lwip` (CGO required) | ✖️ | Build with LWIP Tun stack support, see [Tun inbound](/configuration/inbound/tun#stack). |
It is not recommended to change the default build tag list unless you really know what you are adding. It is not recommended to change the default build tag list unless you really know what you are adding.

View file

@ -53,21 +53,19 @@ go build -tags "tag_a tag_b" ./cmd/sing-box
## :material-folder-settings: 构建标记 ## :material-folder-settings: 构建标记
| 构建标记 | 默认启动 | 说明 | | 构建标记 | 默认启动 | 说明 |
|------------------------------------|------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |------------------------------------|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `with_quic` | ✔ | Build with QUIC support, see [QUIC and HTTP3 DNS transports](/configuration/dns/server/), [Naive inbound](/configuration/inbound/naive/), [Hysteria Inbound](/configuration/inbound/hysteria/), [Hysteria Outbound](/configuration/outbound/hysteria/) and [V2Ray Transport#QUIC](/configuration/shared/v2ray-transport#quic). | | `with_quic` | :material-check: | Build with QUIC support, see [QUIC and HTTP3 DNS transports](/configuration/dns/server/), [Naive inbound](/configuration/inbound/naive/), [Hysteria Inbound](/configuration/inbound/hysteria/), [Hysteria Outbound](/configuration/outbound/hysteria/) and [V2Ray Transport#QUIC](/configuration/shared/v2ray-transport#quic). |
| `with_grpc` | ✖️ | Build with standard gRPC support, see [V2Ray Transport#gRPC](/configuration/shared/v2ray-transport#grpc). | | `with_grpc` | :material-close: | Build with standard gRPC support, see [V2Ray Transport#gRPC](/configuration/shared/v2ray-transport#grpc). |
| `with_dhcp` | ✔ | Build with DHCP support, see [DHCP DNS transport](/configuration/dns/server/). | | `with_dhcp` | :material-check: | Build with DHCP support, see [DHCP DNS transport](/configuration/dns/server/). |
| `with_wireguard` | ✔ | Build with WireGuard support, see [WireGuard outbound](/configuration/outbound/wireguard/). | | `with_wireguard` | :material-check: | Build with WireGuard support, see [WireGuard outbound](/configuration/outbound/wireguard/). |
| `with_ech` | ✔ | Build with TLS ECH extension support for TLS outbound, see [TLS](/configuration/shared/tls#ech). | | `with_ech` | :material-check: | Build with TLS ECH extension support for TLS outbound, see [TLS](/configuration/shared/tls#ech). |
| `with_utls` | ✔ | Build with [uTLS](https://github.com/refraction-networking/utls) support for TLS outbound, see [TLS](/configuration/shared/tls#utls). | | `with_utls` | :material-check: | Build with [uTLS](https://github.com/refraction-networking/utls) support for TLS outbound, see [TLS](/configuration/shared/tls#utls). |
| `with_reality_server` | ✔ | Build with reality TLS server support, see [TLS](/configuration/shared/tls/). | | `with_reality_server` | :material-check: | Build with reality TLS server support, see [TLS](/configuration/shared/tls/). |
| `with_acme` | ✔ | Build with ACME TLS certificate issuer support, see [TLS](/configuration/shared/tls/). | | `with_acme` | :material-check: | Build with ACME TLS certificate issuer support, see [TLS](/configuration/shared/tls/). |
| `with_clash_api` | ✔ | Build with Clash API support, see [Experimental](/configuration/experimental#clash-api-fields). | | `with_clash_api` | :material-check: | Build with Clash API support, see [Experimental](/configuration/experimental#clash-api-fields). |
| `with_v2ray_api` | ✖️ | Build with V2Ray API support, see [Experimental](/configuration/experimental#v2ray-api-fields). | | `with_v2ray_api` | :material-close: | Build with V2Ray API support, see [Experimental](/configuration/experimental#v2ray-api-fields). |
| `with_gvisor` | ✔ | Build with gVisor support, see [Tun inbound](/configuration/inbound/tun#stack) and [WireGuard outbound](/configuration/outbound/wireguard#system_interface). | | `with_gvisor` | :material-check: | Build with gVisor support, see [Tun inbound](/configuration/inbound/tun#stack) and [WireGuard outbound](/configuration/outbound/wireguard#system_interface). |
| `with_embedded_tor` (CGO required) | ✖️ | Build with embedded Tor support, see [Tor outbound](/configuration/outbound/tor/). | | `with_embedded_tor` (CGO required) | :material-close: | Build with embedded Tor support, see [Tor outbound](/configuration/outbound/tor/). |
| `with_lwip` (CGO required) | ✖️ | Build with LWIP Tun stack support, see [Tun inbound](/configuration/inbound/tun#stack). |
除非您确实知道您正在启用什么,否则不建议更改默认构建标签列表。 除非您确实知道您正在启用什么,否则不建议更改默认构建标签列表。

23
go.mod
View file

@ -23,22 +23,22 @@ require (
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1
github.com/sagernet/gomobile v0.1.1 github.com/sagernet/gomobile v0.1.1
github.com/sagernet/gvisor v0.0.0-20231119034329-07cfb6aaf930 github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e
github.com/sagernet/quic-go v0.40.0 github.com/sagernet/quic-go v0.40.1-beta.2
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
github.com/sagernet/sing v0.3.0-rc.4 github.com/sagernet/sing v0.3.0-rc.7
github.com/sagernet/sing-dns v0.1.12 github.com/sagernet/sing-dns v0.1.12
github.com/sagernet/sing-mux v0.1.7 github.com/sagernet/sing-mux v0.1.8-rc.1
github.com/sagernet/sing-quic v0.1.6 github.com/sagernet/sing-quic v0.1.7-rc.2
github.com/sagernet/sing-shadowsocks v0.2.6 github.com/sagernet/sing-shadowsocks v0.2.6
github.com/sagernet/sing-shadowsocks2 v0.1.5 github.com/sagernet/sing-shadowsocks2 v0.1.6-rc.1
github.com/sagernet/sing-shadowtls v0.1.4 github.com/sagernet/sing-shadowtls v0.1.4
github.com/sagernet/sing-tun v0.1.24 github.com/sagernet/sing-tun v0.2.0-rc.1
github.com/sagernet/sing-vmess v0.1.8 github.com/sagernet/sing-vmess v0.1.8
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6 github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6
github.com/sagernet/utls v1.5.4 github.com/sagernet/utls v1.5.4
github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
github.com/spf13/cobra v1.8.0 github.com/spf13/cobra v1.8.0
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.8.4
@ -79,7 +79,6 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect github.com/quic-go/qtls-go1-20 v0.4.1 // 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/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
@ -87,10 +86,10 @@ require (
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
github.com/zeebo/blake3 v0.2.3 // indirect github.com/zeebo/blake3 v0.2.3 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
golang.org/x/exp v0.0.0-20231219180239-dc181d75b848 // indirect golang.org/x/exp v0.0.0-20231226003508-02704c960a9b // indirect
golang.org/x/mod v0.14.0 // indirect golang.org/x/mod v0.14.0 // indirect
golang.org/x/text v0.14.0 // indirect golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.4.0 // indirect golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.16.0 // indirect golang.org/x/tools v0.16.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect

51
go.sum
View file

@ -98,46 +98,43 @@ github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkk
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM= github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM=
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 h1:YbmpqPQEMdlk9oFSKYWRqVuu9qzNiOayIonKmv1gCXY= github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 h1:YbmpqPQEMdlk9oFSKYWRqVuu9qzNiOayIonKmv1gCXY=
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1/go.mod h1:J2yAxTFPDjrDPhuAi9aWFz2L3ox9it4qAluBBbN0H5k= github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1/go.mod h1:J2yAxTFPDjrDPhuAi9aWFz2L3ox9it4qAluBBbN0H5k=
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
github.com/sagernet/gomobile v0.1.1 h1:3vihRGyUfFTToHMeeak0UK6/ldt2MV2bcWKFi2VyECU= github.com/sagernet/gomobile v0.1.1 h1:3vihRGyUfFTToHMeeak0UK6/ldt2MV2bcWKFi2VyECU=
github.com/sagernet/gomobile v0.1.1/go.mod h1:Pqq2+ZVvs10U7xK+UwJgwYWUykewi8H6vlslAO73n9E= github.com/sagernet/gomobile v0.1.1/go.mod h1:Pqq2+ZVvs10U7xK+UwJgwYWUykewi8H6vlslAO73n9E=
github.com/sagernet/gvisor v0.0.0-20231119034329-07cfb6aaf930 h1:dSPgjIw0CT6ISLeEh8Q20dZMBMFCcEceo23+LncRcNQ= github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e h1:DOkjByVeAR56dkszjnMZke4wr7yM/1xHaJF3G9olkEE=
github.com/sagernet/gvisor v0.0.0-20231119034329-07cfb6aaf930/go.mod h1:JpKHkOYgh4wLwrX2BhH3ZIvCvazCkTnPeEcmigZJfHY= github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e/go.mod h1:fLxq/gtp0qzkaEwywlRRiGmjOK5ES/xUzyIKIFP2Asw=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/quic-go v0.40.0 h1:DvQNPb72lzvNQDe9tcUyHTw8eRv6PLtM2mNYmdlzUMo= github.com/sagernet/quic-go v0.40.1-beta.2 h1:USRwm36XuAFdcrmv4vDRD+YUOO08DfvLNruXThrVHZU=
github.com/sagernet/quic-go v0.40.0/go.mod h1:VqtdhlbkeeG5Okhb3eDMb/9o0EoglReHunNT9ukrJAI= github.com/sagernet/quic-go v0.40.1-beta.2/go.mod h1:CcKTpzTAISxrM4PA5M20/wYuz9Tj6Tx4DwGbNl9UQrU=
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc= github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= github.com/sagernet/sing v0.3.0-rc.7 h1:FmnzFRYC6usVgWf112cUxiexwvL+iAurKmCL4Axa9+A=
github.com/sagernet/sing v0.3.0-rc.4 h1:1Til9jN0AnTPB9iiX/MbFrocbRCOXDsdZ/io1IjVWkg= github.com/sagernet/sing v0.3.0-rc.7/go.mod h1:9pfuAH6mZfgnz/YjP6xu5sxx882rfyjpcrTdUpd6w3g=
github.com/sagernet/sing v0.3.0-rc.4/go.mod h1:Ce5LNojQOgOiWhiD8pPD6E9H7e2KgtOe3Zxx4Ou5u80=
github.com/sagernet/sing-dns v0.1.12 h1:1HqZ+ln+Rezx/aJMStaS0d7oPeX2EobSV1NT537kyj4= github.com/sagernet/sing-dns v0.1.12 h1:1HqZ+ln+Rezx/aJMStaS0d7oPeX2EobSV1NT537kyj4=
github.com/sagernet/sing-dns v0.1.12/go.mod h1:rx/DTOisneQpCgNQ4jbFU/JNEtnz0lYcHXenlVzpjEU= github.com/sagernet/sing-dns v0.1.12/go.mod h1:rx/DTOisneQpCgNQ4jbFU/JNEtnz0lYcHXenlVzpjEU=
github.com/sagernet/sing-mux v0.1.7 h1:+48spVReBwIrv6ZdUujiRFCCnblZFwxmbPgrs5zezlI= github.com/sagernet/sing-mux v0.1.8-rc.1 h1:5dsZgWmNr9W6JzQj4fb3xX2pMP0OyJH6kVtlqc2kFKA=
github.com/sagernet/sing-mux v0.1.7/go.mod h1:UmcVSPrVjsOGe95jDXmGgOyKKIXOcjz6FKbFy+0LeDU= github.com/sagernet/sing-mux v0.1.8-rc.1/go.mod h1:KK5zCbNujj5kn36G+wLFROOXyJhaaXLyaZWY2w7kBNQ=
github.com/sagernet/sing-quic v0.1.6 h1:yNkZiNOlmEGpS+A7I4/Zavhe/fRrLz7yCO/dVMZzt+k= github.com/sagernet/sing-quic v0.1.7-rc.2 h1:rCWhtvzQwgkWbX4sVHYdNwzyPweoUPEgBCBatywHjMs=
github.com/sagernet/sing-quic v0.1.6/go.mod h1:g1Ogcy2KSwKvC7eDXEUu9AnHbjotC+2xsSP+A1i/VOA= github.com/sagernet/sing-quic v0.1.7-rc.2/go.mod h1:IbKCPWXP13zd3cdu0rirtYjkMlquc5zWtc3avfSUGAw=
github.com/sagernet/sing-shadowsocks v0.2.6 h1:xr7ylAS/q1cQYS8oxKKajhuQcchd5VJJ4K4UZrrpp0s= github.com/sagernet/sing-shadowsocks v0.2.6 h1:xr7ylAS/q1cQYS8oxKKajhuQcchd5VJJ4K4UZrrpp0s=
github.com/sagernet/sing-shadowsocks v0.2.6/go.mod h1:j2YZBIpWIuElPFL/5sJAj470bcn/3QQ5lxZUNKLDNAM= github.com/sagernet/sing-shadowsocks v0.2.6/go.mod h1:j2YZBIpWIuElPFL/5sJAj470bcn/3QQ5lxZUNKLDNAM=
github.com/sagernet/sing-shadowsocks2 v0.1.5 h1:JDeAJ4ZWlYZ7F6qEVdDKPhQEangxKw/JtmU+i/YfCYE= github.com/sagernet/sing-shadowsocks2 v0.1.6-rc.1 h1:E+8OyyVg0YfFNUmxMx9jYBEhjLYMQSAMzJrUmE934bo=
github.com/sagernet/sing-shadowsocks2 v0.1.5/go.mod h1:KF65y8lI5PGHyMgRZGYXYsH9ilgRc/yr+NYbSNGuBm4= github.com/sagernet/sing-shadowsocks2 v0.1.6-rc.1/go.mod h1:wFkU7sKxyZADS/idtJqBhtc+QBf5iwX9nZO7ymcn6MM=
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k= github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4= github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
github.com/sagernet/sing-tun v0.1.24 h1:cxn8lr8uHMLB1tLU0SzBPE1Q04pG0Fb71GyeeCuic5Q= github.com/sagernet/sing-tun v0.2.0-rc.1 h1:CnlxRgrJKAMKYNuJOcKie6TjRz8wremEq1wndLup7cA=
github.com/sagernet/sing-tun v0.1.24/go.mod h1:Mnd7+8iGNb9uGnMAh3bp0ZA+nPFBZNaMHZPMEGdAQJM= github.com/sagernet/sing-tun v0.2.0-rc.1/go.mod h1:hpbL9jNAbYT9G2EHCpCXVIgSrM/2Wgnrm/Hped+8zdY=
github.com/sagernet/sing-vmess v0.1.8 h1:XVWad1RpTy9b5tPxdm5MCU8cGfrTGdR8qCq6HV2aCNc= github.com/sagernet/sing-vmess v0.1.8 h1:XVWad1RpTy9b5tPxdm5MCU8cGfrTGdR8qCq6HV2aCNc=
github.com/sagernet/sing-vmess v0.1.8/go.mod h1:vhx32UNzTDUkNwOyIjcZQohre1CaytquC5mPplId8uA= github.com/sagernet/sing-vmess v0.1.8/go.mod h1:vhx32UNzTDUkNwOyIjcZQohre1CaytquC5mPplId8uA=
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37/go.mod h1:3skNSftZDJWTGVtVaM2jfbce8qHnmH/AGDRe62iNOg0= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo=
github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6 h1:z3SJQhVyU63FT26Wn/UByW6b7q8QKB0ZkPqsyqcz2PI= github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6 h1:z3SJQhVyU63FT26Wn/UByW6b7q8QKB0ZkPqsyqcz2PI=
github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6/go.mod h1:73xRZuxwkFk4aiLw28hG8W6o9cr2UPrGL9pdY2UTbvY= github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6/go.mod h1:73xRZuxwkFk4aiLw28hG8W6o9cr2UPrGL9pdY2UTbvY=
github.com/sagernet/utls v1.5.4 h1:KmsEGbB2dKUtCNC+44NwAdNAqnqQ6GA4pTO0Yik56co= github.com/sagernet/utls v1.5.4 h1:KmsEGbB2dKUtCNC+44NwAdNAqnqQ6GA4pTO0Yik56co=
github.com/sagernet/utls v1.5.4/go.mod h1:CTGxPWExIloRipK3XFpYv0OVyhO8kk3XCGW/ieyTh1s= github.com/sagernet/utls v1.5.4/go.mod h1:CTGxPWExIloRipK3XFpYv0OVyhO8kk3XCGW/ieyTh1s=
github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f h1:Kvo8w8Y9lzFGB/7z09MJ3TR99TFtfI/IuY87Ygcycho= github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8 h1:R0OMYAScomNAVpTfbHFpxqJpvwuhxSRi+g6z7gZhABs=
github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f/go.mod h1:mySs0abhpc/gLlvhoq7HP1RzOaRmIXVeZGCh++zoApk= github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8/go.mod h1:K4J7/npM+VAMUeUmTa2JaA02JmyheP0GpRBOUvn3ecc=
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc= github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc=
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA= github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA=
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh8n+csdOuDfP+NuykE0U6AeYSJJHKDgSg= github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh8n+csdOuDfP+NuykE0U6AeYSJJHKDgSg=
@ -173,8 +170,8 @@ golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaE
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/exp v0.0.0-20231219180239-dc181d75b848 h1:+iq7lrkxmFNBM7xx+Rae2W6uyPfhPeDWD+n+JgppptE= golang.org/x/exp v0.0.0-20231226003508-02704c960a9b h1:kLiC65FbiHWFAOu+lxwNPujcsl8VYyTYYEZnsOO1WK4=
golang.org/x/exp v0.0.0-20231219180239-dc181d75b848/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/exp v0.0.0-20231226003508-02704c960a9b/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
@ -187,10 +184,10 @@ golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7w
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-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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=
@ -199,8 +196,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.4.0 h1:Z81tqI5ddIoXDPvVQ7/7CC9TnLM7ubaFG2qXYd5BbYY= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.4.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM=
golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=

View file

@ -26,7 +26,7 @@ var (
type HTTP struct { type HTTP struct {
myInboundAdapter myInboundAdapter
authenticator auth.Authenticator authenticator *auth.Authenticator
tlsConfig tls.ServerConfig tlsConfig tls.ServerConfig
} }

View file

@ -29,7 +29,7 @@ var (
type Mixed struct { type Mixed struct {
myInboundAdapter myInboundAdapter
authenticator auth.Authenticator authenticator *auth.Authenticator
} }
func NewMixed(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPMixedInboundOptions) *Mixed { func NewMixed(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPMixedInboundOptions) *Mixed {

View file

@ -32,7 +32,7 @@ var _ adapter.Inbound = (*Naive)(nil)
type Naive struct { type Naive struct {
myInboundAdapter myInboundAdapter
authenticator auth.Authenticator authenticator *auth.Authenticator
tlsConfig tls.ServerConfig tlsConfig tls.ServerConfig
httpServer *http.Server httpServer *http.Server
h3Server any h3Server any

View file

@ -22,7 +22,7 @@ var (
type Socks struct { type Socks struct {
myInboundAdapter myInboundAdapter
authenticator auth.Authenticator authenticator *auth.Authenticator
} }
func NewSocks(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SocksInboundOptions) *Socks { func NewSocks(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SocksInboundOptions) *Socks {

View file

@ -75,6 +75,7 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
tunOptions: tun.Options{ tunOptions: tun.Options{
Name: options.InterfaceName, Name: options.InterfaceName,
MTU: tunMTU, MTU: tunMTU,
GSO: options.GSO,
Inet4Address: options.Inet4Address, Inet4Address: options.Inet4Address,
Inet6Address: options.Inet6Address, Inet6Address: options.Inet6Address,
AutoRoute: options.AutoRoute, AutoRoute: options.AutoRoute,
@ -168,10 +169,7 @@ func (t *Tun) Start() error {
t.tunStack, err = tun.NewStack(t.stack, tun.StackOptions{ t.tunStack, err = tun.NewStack(t.stack, tun.StackOptions{
Context: t.ctx, Context: t.ctx,
Tun: tunInterface, Tun: tunInterface,
MTU: t.tunOptions.MTU, TunOptions: t.tunOptions,
Name: t.tunOptions.Name,
Inet4Address: t.tunOptions.Inet4Address,
Inet6Address: t.tunOptions.Inet6Address,
EndpointIndependentNat: t.endpointIndependentNat, EndpointIndependentNat: t.endpointIndependentNat,
UDPTimeout: t.udpTimeout, UDPTimeout: t.udpTimeout,
Handler: t, Handler: t,

View file

@ -108,20 +108,21 @@ type DialerOptionsWrapper interface {
} }
type DialerOptions struct { type DialerOptions struct {
Detour string `json:"detour,omitempty"` Detour string `json:"detour,omitempty"`
BindInterface string `json:"bind_interface,omitempty"` BindInterface string `json:"bind_interface,omitempty"`
Inet4BindAddress *ListenAddress `json:"inet4_bind_address,omitempty"` Inet4BindAddress *ListenAddress `json:"inet4_bind_address,omitempty"`
Inet6BindAddress *ListenAddress `json:"inet6_bind_address,omitempty"` Inet6BindAddress *ListenAddress `json:"inet6_bind_address,omitempty"`
ProtectPath string `json:"protect_path,omitempty"` ProtectPath string `json:"protect_path,omitempty"`
RoutingMark int `json:"routing_mark,omitempty"` RoutingMark int `json:"routing_mark,omitempty"`
ReuseAddr bool `json:"reuse_addr,omitempty"` ReuseAddr bool `json:"reuse_addr,omitempty"`
ConnectTimeout Duration `json:"connect_timeout,omitempty"` ConnectTimeout Duration `json:"connect_timeout,omitempty"`
TCPFastOpen bool `json:"tcp_fast_open,omitempty"` TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
TCPMultiPath bool `json:"tcp_multi_path,omitempty"` TCPMultiPath bool `json:"tcp_multi_path,omitempty"`
UDPFragment *bool `json:"udp_fragment,omitempty"` UDPFragment *bool `json:"udp_fragment,omitempty"`
UDPFragmentDefault bool `json:"-"` UDPFragmentDefault bool `json:"-"`
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"` DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
FallbackDelay Duration `json:"fallback_delay,omitempty"` FallbackDelay Duration `json:"fallback_delay,omitempty"`
IsWireGuardListener bool `json:"-"`
} }
func (o *DialerOptions) TakeDialerOptions() DialerOptions { func (o *DialerOptions) TakeDialerOptions() DialerOptions {

View file

@ -5,6 +5,7 @@ import "net/netip"
type TunInboundOptions struct { type TunInboundOptions struct {
InterfaceName string `json:"interface_name,omitempty"` InterfaceName string `json:"interface_name,omitempty"`
MTU uint32 `json:"mtu,omitempty"` MTU uint32 `json:"mtu,omitempty"`
GSO bool `json:"gso,omitempty"`
Inet4Address Listable[netip.Prefix] `json:"inet4_address,omitempty"` Inet4Address Listable[netip.Prefix] `json:"inet4_address,omitempty"`
Inet6Address Listable[netip.Prefix] `json:"inet6_address,omitempty"` Inet6Address Listable[netip.Prefix] `json:"inet6_address,omitempty"`
AutoRoute bool `json:"auto_route,omitempty"` AutoRoute bool `json:"auto_route,omitempty"`

View file

@ -5,6 +5,7 @@ import "net/netip"
type WireGuardOutboundOptions struct { type WireGuardOutboundOptions struct {
DialerOptions DialerOptions
SystemInterface bool `json:"system_interface,omitempty"` SystemInterface bool `json:"system_interface,omitempty"`
GSO bool `json:"gso,omitempty"`
InterfaceName string `json:"interface_name,omitempty"` InterfaceName string `json:"interface_name,omitempty"`
LocalAddress Listable[netip.Prefix] `json:"local_address"` LocalAddress Listable[netip.Prefix] `json:"local_address"`
PrivateKey string `json:"private_key"` PrivateKey string `json:"private_key"`

View file

@ -111,6 +111,9 @@ func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metada
} }
} }
if readWaiter, created := bufio.CreatePacketReadWaiter(reader); created { if readWaiter, created := bufio.CreatePacketReadWaiter(reader); created {
readWaiter.InitializeReadWaiter(N.ReadWaitOptions{
MTU: dns.FixedPacketSize,
})
return d.newPacketConnection(ctx, conn, readWaiter, counters, cachedPackets, metadata) return d.newPacketConnection(ctx, conn, readWaiter, counters, cachedPackets, metadata)
} }
break break
@ -193,15 +196,13 @@ func (d *DNS) newPacketConnection(ctx context.Context, conn N.PacketConn, readWa
timeout := canceler.New(fastClose, cancel, C.DNSTimeout) timeout := canceler.New(fastClose, cancel, C.DNSTimeout)
var group task.Group var group task.Group
group.Append0(func(ctx context.Context) error { group.Append0(func(ctx context.Context) error {
var buffer *buf.Buffer
readWaiter.InitializeReadWaiter(func() *buf.Buffer {
return buf.NewSize(dns.FixedPacketSize)
})
defer readWaiter.InitializeReadWaiter(nil)
for { for {
var message mDNS.Msg var (
var destination M.Socksaddr message mDNS.Msg
var err error destination M.Socksaddr
err error
buffer *buf.Buffer
)
if len(cached) > 0 { if len(cached) > 0 {
packet := cached[0] packet := cached[0]
cached = cached[1:] cached = cached[1:]
@ -216,9 +217,8 @@ func (d *DNS) newPacketConnection(ctx context.Context, conn N.PacketConn, readWa
} }
destination = packet.Destination destination = packet.Destination
} else { } else {
destination, err = readWaiter.WaitReadPacket() buffer, destination, err = readWaiter.WaitReadPacket()
if err != nil { if err != nil {
buffer.Release()
cancel(err) cancel(err)
return err return err
} }

View file

@ -30,7 +30,7 @@ type ProxyListener struct {
tcpListener *net.TCPListener tcpListener *net.TCPListener
username string username string
password string password string
authenticator auth.Authenticator authenticator *auth.Authenticator
} }
func NewProxyListener(ctx context.Context, logger log.ContextLogger, dialer N.Dialer) *ProxyListener { func NewProxyListener(ctx context.Context, logger log.ContextLogger, dialer N.Dialer) *ProxyListener {

View file

@ -8,6 +8,7 @@ import (
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"net" "net"
"net/netip"
"strings" "strings"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
@ -18,10 +19,12 @@ import (
"github.com/sagernet/sing-box/transport/wireguard" "github.com/sagernet/sing-box/transport/wireguard"
"github.com/sagernet/sing-dns" "github.com/sagernet/sing-dns"
"github.com/sagernet/sing-tun" "github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common/debug"
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"
"github.com/sagernet/sing/common/x/list"
"github.com/sagernet/sing/service/pause"
"github.com/sagernet/wireguard-go/conn"
"github.com/sagernet/wireguard-go/device" "github.com/sagernet/wireguard-go/device"
) )
@ -32,9 +35,18 @@ var (
type WireGuard struct { type WireGuard struct {
myOutboundAdapter myOutboundAdapter
bind *wireguard.ClientBind ctx context.Context
device *device.Device workers int
tunDevice wireguard.Device peers []wireguard.PeerConfig
useStdNetBind bool
listener N.Dialer
ipcConf string
pauseManager pause.Manager
pauseCallback *list.Element[pause.Callback]
bind conn.Bind
device *device.Device
tunDevice wireguard.Device
} }
func NewWireGuard(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.WireGuardOutboundOptions) (*WireGuard, error) { func NewWireGuard(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.WireGuardOutboundOptions) (*WireGuard, error) {
@ -47,32 +59,30 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
tag: tag, tag: tag,
dependencies: withDialerDependency(options.DialerOptions), dependencies: withDialerDependency(options.DialerOptions),
}, },
ctx: ctx,
workers: options.Workers,
pauseManager: pause.ManagerFromContext(ctx),
} }
var reserved [3]uint8 peers, err := wireguard.ParsePeers(options)
if len(options.Reserved) > 0 {
if len(options.Reserved) != 3 {
return nil, E.New("invalid reserved value, required 3 bytes, got ", len(options.Reserved))
}
copy(reserved[:], options.Reserved)
}
var isConnect bool
var connectAddr M.Socksaddr
if len(options.Peers) < 2 {
isConnect = true
if len(options.Peers) == 1 {
connectAddr = options.Peers[0].ServerOptions.Build()
} else {
connectAddr = options.ServerOptions.Build()
}
}
outboundDialer, err := dialer.New(router, options.DialerOptions)
if err != nil { if err != nil {
return nil, err return nil, err
} }
outbound.bind = wireguard.NewClientBind(ctx, outbound, outboundDialer, isConnect, connectAddr, reserved) outbound.peers = peers
if len(options.LocalAddress) == 0 { if len(options.LocalAddress) == 0 {
return nil, E.New("missing local address") return nil, E.New("missing local address")
} }
if options.GSO {
if options.GSO && options.Detour != "" {
return nil, E.New("gso is conflict with detour")
}
options.IsWireGuardListener = true
outbound.useStdNetBind = true
}
listener, err := dialer.New(router, options.DialerOptions)
if err != nil {
return nil, err
}
outbound.listener = listener
var privateKey string var privateKey string
{ {
bytes, err := base64.StdEncoding.DecodeString(options.PrivateKey) bytes, err := base64.StdEncoding.DecodeString(options.PrivateKey)
@ -81,80 +91,7 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
} }
privateKey = hex.EncodeToString(bytes) privateKey = hex.EncodeToString(bytes)
} }
ipcConf := "private_key=" + privateKey outbound.ipcConf = "private_key=" + privateKey
if len(options.Peers) > 0 {
for i, peer := range options.Peers {
var peerPublicKey, preSharedKey string
{
bytes, err := base64.StdEncoding.DecodeString(peer.PublicKey)
if err != nil {
return nil, E.Cause(err, "decode public key for peer ", i)
}
peerPublicKey = hex.EncodeToString(bytes)
}
if peer.PreSharedKey != "" {
bytes, err := base64.StdEncoding.DecodeString(peer.PreSharedKey)
if err != nil {
return nil, E.Cause(err, "decode pre shared key for peer ", i)
}
preSharedKey = hex.EncodeToString(bytes)
}
destination := peer.ServerOptions.Build()
ipcConf += "\npublic_key=" + peerPublicKey
ipcConf += "\nendpoint=" + destination.String()
if preSharedKey != "" {
ipcConf += "\npreshared_key=" + preSharedKey
}
if len(peer.AllowedIPs) == 0 {
return nil, E.New("missing allowed_ips for peer ", i)
}
for _, allowedIP := range peer.AllowedIPs {
ipcConf += "\nallowed_ip=" + allowedIP
}
if len(peer.Reserved) > 0 {
if len(peer.Reserved) != 3 {
return nil, E.New("invalid reserved value for peer ", i, ", required 3 bytes, got ", len(peer.Reserved))
}
copy(reserved[:], options.Reserved)
outbound.bind.SetReservedForEndpoint(destination, reserved)
}
}
} else {
var peerPublicKey, preSharedKey string
{
bytes, err := base64.StdEncoding.DecodeString(options.PeerPublicKey)
if err != nil {
return nil, E.Cause(err, "decode peer public key")
}
peerPublicKey = hex.EncodeToString(bytes)
}
if options.PreSharedKey != "" {
bytes, err := base64.StdEncoding.DecodeString(options.PreSharedKey)
if err != nil {
return nil, E.Cause(err, "decode pre shared key")
}
preSharedKey = hex.EncodeToString(bytes)
}
ipcConf += "\npublic_key=" + peerPublicKey
ipcConf += "\nendpoint=" + options.ServerOptions.Build().String()
if preSharedKey != "" {
ipcConf += "\npreshared_key=" + preSharedKey
}
var has4, has6 bool
for _, address := range options.LocalAddress {
if address.Addr().Is4() {
has4 = true
} else {
has6 = true
}
}
if has4 {
ipcConf += "\nallowed_ip=0.0.0.0/0"
}
if has6 {
ipcConf += "\nallowed_ip=::/0"
}
}
mtu := options.MTU mtu := options.MTU
if mtu == 0 { if mtu == 0 {
mtu = 1408 mtu = 1408
@ -163,36 +100,83 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
if !options.SystemInterface && tun.WithGVisor { if !options.SystemInterface && tun.WithGVisor {
wireTunDevice, err = wireguard.NewStackDevice(options.LocalAddress, mtu) wireTunDevice, err = wireguard.NewStackDevice(options.LocalAddress, mtu)
} else { } else {
wireTunDevice, err = wireguard.NewSystemDevice(router, options.InterfaceName, options.LocalAddress, mtu) wireTunDevice, err = wireguard.NewSystemDevice(router, options.InterfaceName, options.LocalAddress, mtu, options.GSO)
} }
if err != nil { if err != nil {
return nil, E.Cause(err, "create WireGuard device") return nil, E.Cause(err, "create WireGuard device")
} }
wgDevice := device.NewDevice(ctx, wireTunDevice, outbound.bind, &device.Logger{
Verbosef: func(format string, args ...interface{}) {
logger.Debug(fmt.Sprintf(strings.ToLower(format), args...))
},
Errorf: func(format string, args ...interface{}) {
logger.Error(fmt.Sprintf(strings.ToLower(format), args...))
},
}, options.Workers)
if debug.Enabled {
logger.Trace("created wireguard ipc conf: \n", ipcConf)
}
err = wgDevice.IpcSet(ipcConf)
if err != nil {
return nil, E.Cause(err, "setup wireguard")
}
outbound.device = wgDevice
outbound.tunDevice = wireTunDevice outbound.tunDevice = wireTunDevice
return outbound, nil return outbound, nil
} }
func (w *WireGuard) Start() error {
err := wireguard.ResolvePeers(w.ctx, w.router, w.peers)
if err != nil {
return err
}
var bind conn.Bind
if w.useStdNetBind {
bind = conn.NewStdNetBind(w.listener.(dialer.WireGuardListener))
} else {
var (
isConnect bool
connectAddr netip.AddrPort
reserved [3]uint8
)
peerLen := len(w.peers)
if peerLen == 1 {
isConnect = true
connectAddr = w.peers[0].Endpoint
reserved = w.peers[0].Reserved
}
bind = wireguard.NewClientBind(w.ctx, w, w.listener, isConnect, connectAddr, reserved)
}
wgDevice := device.NewDevice(w.tunDevice, bind, &device.Logger{
Verbosef: func(format string, args ...interface{}) {
w.logger.Debug(fmt.Sprintf(strings.ToLower(format), args...))
},
Errorf: func(format string, args ...interface{}) {
w.logger.Error(fmt.Sprintf(strings.ToLower(format), args...))
},
}, w.workers)
ipcConf := w.ipcConf
for _, peer := range w.peers {
ipcConf += peer.GenerateIpcLines()
}
err = wgDevice.IpcSet(ipcConf)
if err != nil {
return E.Cause(err, "setup wireguard: \n", ipcConf)
}
w.device = wgDevice
w.pauseCallback = w.pauseManager.RegisterCallback(w.onPauseUpdated)
return w.tunDevice.Start()
}
func (w *WireGuard) Close() error {
if w.device != nil {
w.device.Close()
}
if w.pauseCallback != nil {
w.pauseManager.UnregisterCallback(w.pauseCallback)
}
w.tunDevice.Close()
return nil
}
func (w *WireGuard) InterfaceUpdated() { func (w *WireGuard) InterfaceUpdated() {
w.bind.Reset() w.device.BindUpdate()
return return
} }
func (w *WireGuard) onPauseUpdated(event int) {
switch event {
case pause.EventDevicePaused:
w.device.Down()
case pause.EventDeviceWake:
w.device.Up()
}
}
func (w *WireGuard) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { func (w *WireGuard) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
switch network { switch network {
case N.NetworkTCP: case N.NetworkTCP:
@ -233,15 +217,3 @@ func (w *WireGuard) NewConnection(ctx context.Context, conn net.Conn, metadata a
func (w *WireGuard) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { func (w *WireGuard) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
return NewDirectPacketConnection(ctx, w.router, w, conn, metadata, dns.DomainStrategyAsIS) return NewDirectPacketConnection(ctx, w.router, w, conn, metadata, dns.DomainStrategyAsIS)
} }
func (w *WireGuard) Start() error {
return w.tunDevice.Start()
}
func (w *WireGuard) Close() error {
if w.device != nil {
w.device.Close()
}
w.tunDevice.Close()
return nil
}

View file

@ -418,6 +418,35 @@ func (r *Router) Outbounds() []adapter.Outbound {
return r.outbounds return r.outbounds
} }
func (r *Router) PreStart() error {
monitor := taskmonitor.New(r.logger, C.DefaultStartTimeout)
if r.interfaceMonitor != nil {
monitor.Start("initialize interface monitor")
err := r.interfaceMonitor.Start()
monitor.Finish()
if err != nil {
return err
}
}
if r.networkMonitor != nil {
monitor.Start("initialize network monitor")
err := r.networkMonitor.Start()
monitor.Finish()
if err != nil {
return err
}
}
if r.fakeIPStore != nil {
monitor.Start("initialize fakeip store")
err := r.fakeIPStore.Start()
monitor.Finish()
if err != nil {
return err
}
}
return nil
}
func (r *Router) Start() error { func (r *Router) Start() error {
monitor := taskmonitor.New(r.logger, C.DefaultStartTimeout) monitor := taskmonitor.New(r.logger, C.DefaultStartTimeout)
if r.needGeoIPDatabase { if r.needGeoIPDatabase {
@ -436,22 +465,6 @@ func (r *Router) Start() error {
return err return err
} }
} }
if r.interfaceMonitor != nil {
monitor.Start("initialize interface monitor")
err := r.interfaceMonitor.Start()
monitor.Finish()
if err != nil {
return err
}
}
if r.networkMonitor != nil {
monitor.Start("initialize network monitor")
err := r.networkMonitor.Start()
monitor.Finish()
if err != nil {
return err
}
}
if r.needGeositeDatabase { if r.needGeositeDatabase {
for _, rule := range r.rules { for _, rule := range r.rules {
err := rule.UpdateGeosite() err := rule.UpdateGeosite()
@ -472,14 +485,7 @@ func (r *Router) Start() error {
r.geositeCache = nil r.geositeCache = nil
r.geositeReader = nil r.geositeReader = nil
} }
if r.fakeIPStore != nil {
monitor.Start("initialize fakeip store")
err := r.fakeIPStore.Start()
monitor.Finish()
if err != nil {
return err
}
}
if len(r.ruleSets) > 0 { if len(r.ruleSets) > 0 {
monitor.Start("initialize rule-set") monitor.Start("initialize rule-set")
ruleSetStartContext := NewRuleSetStartContext() ruleSetStartContext := NewRuleSetStartContext()
@ -708,6 +714,10 @@ func (r *Router) RuleSet(tag string) (adapter.RuleSet, bool) {
} }
func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
if r.pauseManager.IsDevicePaused() {
return E.New("reject connection to ", metadata.Destination, " while device paused")
}
if metadata.InboundDetour != "" { if metadata.InboundDetour != "" {
if metadata.LastInbound == metadata.InboundDetour { if metadata.LastInbound == metadata.InboundDetour {
return E.New("routing loop on detour: ", metadata.InboundDetour) return E.New("routing loop on detour: ", metadata.InboundDetour)
@ -832,6 +842,9 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
} }
func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
if r.pauseManager.IsDevicePaused() {
return E.New("reject packet connection to ", metadata.Destination, " while device paused")
}
if metadata.InboundDetour != "" { if metadata.InboundDetour != "" {
if metadata.LastInbound == metadata.InboundDetour { if metadata.LastInbound == metadata.InboundDetour {
return E.New("routing loop on detour: ", metadata.InboundDetour) return E.New("routing loop on detour: ", metadata.InboundDetour)

View file

@ -126,7 +126,7 @@ func (s *RemoteRuleSet) loadBytes(content []byte) error {
err error err error
) )
switch s.options.Format { switch s.options.Format {
case C.RuleSetFormatSource, "": case C.RuleSetFormatSource:
var compat option.PlainRuleSetCompat var compat option.PlainRuleSetCompat
compat, err = json.UnmarshalExtended[option.PlainRuleSetCompat](content) compat, err = json.UnmarshalExtended[option.PlainRuleSetCompat](content)
if err != nil { if err != nil {

View file

@ -17,16 +17,16 @@ func (c *NATPacketConn) CreatePacketReadWaiter() (N.PacketReadWaiter, bool) {
type waitNATPacketConn struct { type waitNATPacketConn struct {
*NATPacketConn *NATPacketConn
waiter N.PacketReadWaiter readWaiter N.PacketReadWaiter
} }
func (c *waitNATPacketConn) InitializeReadWaiter(newBuffer func() *buf.Buffer) { func (c *waitNATPacketConn) InitializeReadWaiter(options N.ReadWaitOptions) (needCopy bool) {
c.waiter.InitializeReadWaiter(newBuffer) return c.readWaiter.InitializeReadWaiter(options)
} }
func (c *waitNATPacketConn) WaitReadPacket() (destination M.Socksaddr, err error) { func (c *waitNATPacketConn) WaitReadPacket() (buffer *buf.Buffer, destination M.Socksaddr, err error) {
destination, err = c.waiter.WaitReadPacket() buffer, destination, err = c.readWaiter.WaitReadPacket()
if socksaddrWithoutPort(destination) == c.origin { if err == nil && socksaddrWithoutPort(destination) == c.origin {
destination = M.Socksaddr{ destination = M.Socksaddr{
Addr: c.destination.Addr, Addr: c.destination.Addr,
Fqdn: c.destination.Fqdn, Fqdn: c.destination.Fqdn,

View file

@ -53,7 +53,7 @@ func newMuxConnection0(ctx context.Context, stream net.Conn, metadata M.Metadata
case CommandTCP: case CommandTCP:
return handler.NewConnection(ctx, stream, metadata) return handler.NewConnection(ctx, stream, metadata)
case CommandUDP: case CommandUDP:
return handler.NewPacketConnection(ctx, &PacketConn{stream}, metadata) return handler.NewPacketConnection(ctx, &PacketConn{Conn: stream}, metadata)
default: default:
return E.New("unknown command ", command) return E.New("unknown command ", command)
} }

View file

@ -85,9 +85,10 @@ func (c *ClientConn) Upstream() any {
type ClientPacketConn struct { type ClientPacketConn struct {
net.Conn net.Conn
access sync.Mutex access sync.Mutex
key [KeyLength]byte key [KeyLength]byte
headerWritten bool headerWritten bool
readWaitOptions N.ReadWaitOptions
} }
func NewClientPacketConn(conn net.Conn, key [KeyLength]byte) *ClientPacketConn { func NewClientPacketConn(conn net.Conn, key [KeyLength]byte) *ClientPacketConn {

View file

@ -0,0 +1,45 @@
package trojan
import (
"encoding/binary"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/rw"
)
var _ N.PacketReadWaiter = (*ClientPacketConn)(nil)
func (c *ClientPacketConn) InitializeReadWaiter(options N.ReadWaitOptions) (needCopy bool) {
c.readWaitOptions = options
return false
}
func (c *ClientPacketConn) WaitReadPacket() (buffer *buf.Buffer, destination M.Socksaddr, err error) {
destination, err = M.SocksaddrSerializer.ReadAddrPort(c.Conn)
if err != nil {
return nil, M.Socksaddr{}, E.Cause(err, "read destination")
}
var length uint16
err = binary.Read(c.Conn, binary.BigEndian, &length)
if err != nil {
return nil, M.Socksaddr{}, E.Cause(err, "read chunk length")
}
err = rw.SkipN(c.Conn, 2)
if err != nil {
return nil, M.Socksaddr{}, E.Cause(err, "skip crlf")
}
buffer = c.readWaitOptions.NewPacketBuffer()
_, err = buffer.ReadFullFrom(c.Conn, int(length))
if err != nil {
buffer.Release()
return
}
c.readWaitOptions.PostReturn(buffer)
return
}

View file

@ -105,7 +105,7 @@ func (s *Service[K]) NewConnection(ctx context.Context, conn net.Conn, metadata
case CommandTCP: case CommandTCP:
return s.handler.NewConnection(ctx, conn, metadata) return s.handler.NewConnection(ctx, conn, metadata)
case CommandUDP: case CommandUDP:
return s.handler.NewPacketConnection(ctx, &PacketConn{conn}, metadata) return s.handler.NewPacketConnection(ctx, &PacketConn{Conn: conn}, metadata)
// case CommandMux: // case CommandMux:
default: default:
return HandleMuxConnection(ctx, conn, metadata, s.handler) return HandleMuxConnection(ctx, conn, metadata, s.handler)
@ -122,6 +122,7 @@ func (s *Service[K]) fallback(ctx context.Context, conn net.Conn, metadata M.Met
type PacketConn struct { type PacketConn struct {
net.Conn net.Conn
readWaitOptions N.ReadWaitOptions
} }
func (c *PacketConn) ReadPacket(buffer *buf.Buffer) (M.Socksaddr, error) { func (c *PacketConn) ReadPacket(buffer *buf.Buffer) (M.Socksaddr, error) {

View file

@ -0,0 +1,45 @@
package trojan
import (
"encoding/binary"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/rw"
)
var _ N.PacketReadWaiter = (*PacketConn)(nil)
func (c *PacketConn) InitializeReadWaiter(options N.ReadWaitOptions) (needCopy bool) {
c.readWaitOptions = options
return false
}
func (c *PacketConn) WaitReadPacket() (buffer *buf.Buffer, destination M.Socksaddr, err error) {
destination, err = M.SocksaddrSerializer.ReadAddrPort(c.Conn)
if err != nil {
return nil, M.Socksaddr{}, E.Cause(err, "read destination")
}
var length uint16
err = binary.Read(c.Conn, binary.BigEndian, &length)
if err != nil {
return nil, M.Socksaddr{}, E.Cause(err, "read chunk length")
}
err = rw.SkipN(c.Conn, 2)
if err != nil {
return nil, M.Socksaddr{}, E.Cause(err, "skip crlf")
}
buffer = c.readWaitOptions.NewPacketBuffer()
_, err = buffer.ReadFullFrom(c.Conn, int(length))
if err != nil {
buffer.Release()
return
}
c.readWaitOptions.PostReturn(buffer)
return
}

View file

@ -12,7 +12,6 @@ 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"
"github.com/sagernet/sing/service/pause"
"github.com/sagernet/wireguard-go/conn" "github.com/sagernet/wireguard-go/conn"
) )
@ -22,33 +21,27 @@ type ClientBind struct {
ctx context.Context ctx context.Context
errorHandler E.Handler errorHandler E.Handler
dialer N.Dialer dialer N.Dialer
reservedForEndpoint map[M.Socksaddr][3]uint8 reservedForEndpoint map[netip.AddrPort][3]uint8
connAccess sync.Mutex connAccess sync.Mutex
conn *wireConn conn *wireConn
done chan struct{} done chan struct{}
isConnect bool isConnect bool
connectAddr M.Socksaddr connectAddr netip.AddrPort
reserved [3]uint8 reserved [3]uint8
pauseManager pause.Manager
} }
func NewClientBind(ctx context.Context, errorHandler E.Handler, dialer N.Dialer, isConnect bool, connectAddr M.Socksaddr, reserved [3]uint8) *ClientBind { func NewClientBind(ctx context.Context, errorHandler E.Handler, dialer N.Dialer, isConnect bool, connectAddr netip.AddrPort, reserved [3]uint8) *ClientBind {
return &ClientBind{ return &ClientBind{
ctx: ctx, ctx: ctx,
errorHandler: errorHandler, errorHandler: errorHandler,
dialer: dialer, dialer: dialer,
reservedForEndpoint: make(map[M.Socksaddr][3]uint8), reservedForEndpoint: make(map[netip.AddrPort][3]uint8),
isConnect: isConnect, isConnect: isConnect,
connectAddr: connectAddr, connectAddr: connectAddr,
reserved: reserved, reserved: reserved,
pauseManager: pause.ManagerFromContext(ctx),
} }
} }
func (c *ClientBind) SetReservedForEndpoint(destination M.Socksaddr, reserved [3]byte) {
c.reservedForEndpoint[destination] = reserved
}
func (c *ClientBind) connect() (*wireConn, error) { func (c *ClientBind) connect() (*wireConn, error) {
serverConn := c.conn serverConn := c.conn
if serverConn != nil { if serverConn != nil {
@ -71,16 +64,13 @@ func (c *ClientBind) connect() (*wireConn, error) {
} }
} }
if c.isConnect { if c.isConnect {
udpConn, err := c.dialer.DialContext(c.ctx, N.NetworkUDP, c.connectAddr) udpConn, err := c.dialer.DialContext(c.ctx, N.NetworkUDP, M.SocksaddrFromNetIP(c.connectAddr))
if err != nil { if err != nil {
return nil, err return nil, err
} }
c.conn = &wireConn{ c.conn = &wireConn{
PacketConn: &bufio.UnbindPacketConn{ PacketConn: bufio.NewUnbindPacketConn(udpConn),
ExtendedConn: bufio.NewExtendedConn(udpConn), done: make(chan struct{}),
Addr: c.connectAddr,
},
done: make(chan struct{}),
} }
} else { } else {
udpConn, err := c.dialer.ListenPacket(c.ctx, M.Socksaddr{Addr: netip.IPv4Unspecified()}) udpConn, err := c.dialer.ListenPacket(c.ctx, M.Socksaddr{Addr: netip.IPv4Unspecified()})
@ -116,7 +106,6 @@ func (c *ClientBind) receive(packets [][]byte, sizes []int, eps []conn.Endpoint)
c.errorHandler.NewError(context.Background(), E.Cause(err, "connect to server")) c.errorHandler.NewError(context.Background(), E.Cause(err, "connect to server"))
err = nil err = nil
time.Sleep(time.Second) time.Sleep(time.Second)
c.pauseManager.WaitActive()
return return
} }
n, addr, err := udpConn.ReadFrom(packets[0]) n, addr, err := udpConn.ReadFrom(packets[0])
@ -133,11 +122,9 @@ func (c *ClientBind) receive(packets [][]byte, sizes []int, eps []conn.Endpoint)
sizes[0] = n sizes[0] = n
if n > 3 { if n > 3 {
b := packets[0] b := packets[0]
b[1] = 0 common.ClearArray(b[1:4])
b[2] = 0
b[3] = 0
} }
eps[0] = Endpoint(M.SocksaddrFromNet(addr)) eps[0] = Endpoint(M.AddrPortFromNet(addr))
count = 1 count = 1
return return
} }
@ -170,18 +157,16 @@ func (c *ClientBind) Send(bufs [][]byte, ep conn.Endpoint) error {
if err != nil { if err != nil {
return err return err
} }
destination := M.Socksaddr(ep.(Endpoint)) destination := netip.AddrPort(ep.(Endpoint))
for _, b := range bufs { for _, b := range bufs {
if len(b) > 3 { if len(b) > 3 {
reserved, loaded := c.reservedForEndpoint[destination] reserved, loaded := c.reservedForEndpoint[destination]
if !loaded { if !loaded {
reserved = c.reserved reserved = c.reserved
} }
b[1] = reserved[0] copy(b[1:4], reserved[:])
b[2] = reserved[1]
b[3] = reserved[2]
} }
_, err = udpConn.WriteTo(b, destination) _, err = udpConn.WriteTo(b, M.SocksaddrFromNetIP(destination))
if err != nil { if err != nil {
udpConn.Close() udpConn.Close()
return err return err
@ -191,13 +176,21 @@ func (c *ClientBind) Send(bufs [][]byte, ep conn.Endpoint) error {
} }
func (c *ClientBind) ParseEndpoint(s string) (conn.Endpoint, error) { func (c *ClientBind) ParseEndpoint(s string) (conn.Endpoint, error) {
return Endpoint(M.ParseSocksaddr(s)), nil ap, err := netip.ParseAddrPort(s)
if err != nil {
return nil, err
}
return Endpoint(ap), nil
} }
func (c *ClientBind) BatchSize() int { func (c *ClientBind) BatchSize() int {
return 1 return 1
} }
func (c *ClientBind) SetReservedForEndpoint(destination netip.AddrPort, reserved [3]byte) {
c.reservedForEndpoint[destination] = reserved
}
type wireConn struct { type wireConn struct {
net.PacketConn net.PacketConn
access sync.Mutex access sync.Mutex

View file

@ -265,7 +265,7 @@ func (ep *wireEndpoint) LinkAddress() tcpip.LinkAddress {
} }
func (ep *wireEndpoint) Capabilities() stack.LinkEndpointCapabilities { func (ep *wireEndpoint) Capabilities() stack.LinkEndpointCapabilities {
return stack.CapabilityNone return stack.CapabilityRXChecksumOffload
} }
func (ep *wireEndpoint) Attach(dispatcher stack.NetworkDispatcher) { func (ep *wireEndpoint) Attach(dispatcher stack.NetworkDispatcher) {

View file

@ -2,6 +2,7 @@ package wireguard
import ( import (
"context" "context"
"errors"
"net" "net"
"net/netip" "net/netip"
"os" "os"
@ -11,6 +12,7 @@ import (
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-tun" "github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
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"
wgTun "github.com/sagernet/wireguard-go/tun" wgTun "github.com/sagernet/wireguard-go/tun"
@ -19,16 +21,17 @@ import (
var _ Device = (*SystemDevice)(nil) var _ Device = (*SystemDevice)(nil)
type SystemDevice struct { type SystemDevice struct {
dialer N.Dialer dialer N.Dialer
device tun.Tun device tun.Tun
name string batchDevice tun.LinuxTUN
mtu int name string
events chan wgTun.Event mtu int
addr4 netip.Addr events chan wgTun.Event
addr6 netip.Addr addr4 netip.Addr
addr6 netip.Addr
} }
func NewSystemDevice(router adapter.Router, interfaceName string, localPrefixes []netip.Prefix, mtu uint32) (*SystemDevice, error) { func NewSystemDevice(router adapter.Router, interfaceName string, localPrefixes []netip.Prefix, mtu uint32, gso bool) (*SystemDevice, error) {
var inet4Addresses []netip.Prefix var inet4Addresses []netip.Prefix
var inet6Addresses []netip.Prefix var inet6Addresses []netip.Prefix
for _, prefixes := range localPrefixes { for _, prefixes := range localPrefixes {
@ -46,6 +49,7 @@ func NewSystemDevice(router adapter.Router, interfaceName string, localPrefixes
Inet4Address: inet4Addresses, Inet4Address: inet4Addresses,
Inet6Address: inet6Addresses, Inet6Address: inet6Addresses,
MTU: mtu, MTU: mtu,
GSO: gso,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@ -58,16 +62,25 @@ func NewSystemDevice(router adapter.Router, interfaceName string, localPrefixes
if len(inet6Addresses) > 0 { if len(inet6Addresses) > 0 {
inet6Address = inet6Addresses[0].Addr() inet6Address = inet6Addresses[0].Addr()
} }
var batchDevice tun.LinuxTUN
if gso {
batchTUN, isBatchTUN := tunInterface.(tun.LinuxTUN)
if !isBatchTUN {
return nil, E.New("GSO is not supported on current platform")
}
batchDevice = batchTUN
}
return &SystemDevice{ return &SystemDevice{
dialer: common.Must1(dialer.NewDefault(router, option.DialerOptions{ dialer: common.Must1(dialer.NewDefault(router, option.DialerOptions{
BindInterface: interfaceName, BindInterface: interfaceName,
})), })),
device: tunInterface, device: tunInterface,
name: interfaceName, batchDevice: batchDevice,
mtu: int(mtu), name: interfaceName,
events: make(chan wgTun.Event), mtu: int(mtu),
addr4: inet4Address, events: make(chan wgTun.Event),
addr6: inet6Address, addr4: inet4Address,
addr6: inet6Address,
}, nil }, nil
} }
@ -97,21 +110,31 @@ func (w *SystemDevice) File() *os.File {
} }
func (w *SystemDevice) Read(bufs [][]byte, sizes []int, offset int) (count int, err error) { func (w *SystemDevice) Read(bufs [][]byte, sizes []int, offset int) (count int, err error) {
sizes[0], err = w.device.Read(bufs[0][offset-tun.PacketOffset:]) if w.batchDevice != nil {
if err == nil { count, err = w.batchDevice.BatchRead(bufs, offset, sizes)
count = 1 } else {
sizes[0], err = w.device.Read(bufs[0][offset:])
if err == nil {
count = 1
} else if errors.Is(err, tun.ErrTooManySegments) {
err = wgTun.ErrTooManySegments
}
} }
return return
} }
func (w *SystemDevice) Write(bufs [][]byte, offset int) (count int, err error) { func (w *SystemDevice) Write(bufs [][]byte, offset int) (count int, err error) {
for _, b := range bufs { if w.batchDevice != nil {
_, err = w.device.Write(b[offset:]) return 0, w.batchDevice.BatchWrite(bufs, offset)
if err != nil { } else {
return for _, b := range bufs {
_, err = w.device.Write(b[offset:])
if err != nil {
return
}
} }
count++
} }
// WireGuard will not read count
return return
} }
@ -136,5 +159,8 @@ func (w *SystemDevice) Close() error {
} }
func (w *SystemDevice) BatchSize() int { func (w *SystemDevice) BatchSize() int {
if w.batchDevice != nil {
return w.batchDevice.BatchSize()
}
return 1 return 1
} }

View file

@ -3,13 +3,12 @@ package wireguard
import ( import (
"net/netip" "net/netip"
M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/wireguard-go/conn" "github.com/sagernet/wireguard-go/conn"
) )
var _ conn.Endpoint = (*Endpoint)(nil) var _ conn.Endpoint = (*Endpoint)(nil)
type Endpoint M.Socksaddr type Endpoint netip.AddrPort
func (e Endpoint) ClearSrc() { func (e Endpoint) ClearSrc() {
} }
@ -19,16 +18,16 @@ func (e Endpoint) SrcToString() string {
} }
func (e Endpoint) DstToString() string { func (e Endpoint) DstToString() string {
return (M.Socksaddr)(e).String() return (netip.AddrPort)(e).String()
} }
func (e Endpoint) DstToBytes() []byte { func (e Endpoint) DstToBytes() []byte {
b, _ := (M.Socksaddr)(e).AddrPort().MarshalBinary() b, _ := (netip.AddrPort)(e).MarshalBinary()
return b return b
} }
func (e Endpoint) DstIP() netip.Addr { func (e Endpoint) DstIP() netip.Addr {
return (M.Socksaddr)(e).Addr return (netip.AddrPort)(e).Addr()
} }
func (e Endpoint) SrcIP() netip.Addr { func (e Endpoint) SrcIP() netip.Addr {

View file

@ -0,0 +1,148 @@
package wireguard
import (
"context"
"encoding/base64"
"encoding/hex"
"net/netip"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option"
dns "github.com/sagernet/sing-dns"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
)
type PeerConfig struct {
destination M.Socksaddr
domainStrategy dns.DomainStrategy
Endpoint netip.AddrPort
PublicKey string
PreSharedKey string
AllowedIPs []string
Reserved [3]uint8
}
func (c PeerConfig) GenerateIpcLines() string {
ipcLines := "\npublic_key=" + c.PublicKey
ipcLines += "\nendpoint=" + c.Endpoint.String()
if c.PreSharedKey != "" {
ipcLines += "\npreshared_key=" + c.PreSharedKey
}
for _, allowedIP := range c.AllowedIPs {
ipcLines += "\nallowed_ip=" + allowedIP
}
return ipcLines
}
func ParsePeers(options option.WireGuardOutboundOptions) ([]PeerConfig, error) {
var peers []PeerConfig
if len(options.Peers) > 0 {
for peerIndex, rawPeer := range options.Peers {
peer := PeerConfig{
AllowedIPs: rawPeer.AllowedIPs,
}
destination := rawPeer.ServerOptions.Build()
if destination.IsFqdn() {
peer.destination = destination
peer.domainStrategy = dns.DomainStrategy(options.DomainStrategy)
} else {
peer.Endpoint = destination.AddrPort()
}
{
bytes, err := base64.StdEncoding.DecodeString(rawPeer.PublicKey)
if err != nil {
return nil, E.Cause(err, "decode public key for peer ", peerIndex)
}
peer.PublicKey = hex.EncodeToString(bytes)
}
if rawPeer.PreSharedKey != "" {
bytes, err := base64.StdEncoding.DecodeString(rawPeer.PreSharedKey)
if err != nil {
return nil, E.Cause(err, "decode pre shared key for peer ", peerIndex)
}
peer.PreSharedKey = hex.EncodeToString(bytes)
}
if len(rawPeer.AllowedIPs) == 0 {
return nil, E.New("missing allowed_ips for peer ", peerIndex)
}
if len(rawPeer.Reserved) > 0 {
if len(rawPeer.Reserved) != 3 {
return nil, E.New("invalid reserved value for peer ", peerIndex, ", required 3 bytes, got ", len(peer.Reserved))
}
copy(peer.Reserved[:], options.Reserved)
}
peers = append(peers, peer)
}
} else {
peer := PeerConfig{}
var (
addressHas4 bool
addressHas6 bool
)
for _, localAddress := range options.LocalAddress {
if localAddress.Addr().Is4() {
addressHas4 = true
} else {
addressHas6 = true
}
}
if addressHas4 {
peer.AllowedIPs = append(peer.AllowedIPs, netip.PrefixFrom(netip.IPv4Unspecified(), 0).String())
}
if addressHas6 {
peer.AllowedIPs = append(peer.AllowedIPs, netip.PrefixFrom(netip.IPv6Unspecified(), 0).String())
}
destination := options.ServerOptions.Build()
if destination.IsFqdn() {
peer.destination = destination
peer.domainStrategy = dns.DomainStrategy(options.DomainStrategy)
} else {
peer.Endpoint = destination.AddrPort()
}
{
bytes, err := base64.StdEncoding.DecodeString(options.PeerPublicKey)
if err != nil {
return nil, E.Cause(err, "decode peer public key")
}
peer.PublicKey = hex.EncodeToString(bytes)
}
if options.PreSharedKey != "" {
bytes, err := base64.StdEncoding.DecodeString(options.PreSharedKey)
if err != nil {
return nil, E.Cause(err, "decode pre shared key")
}
peer.PreSharedKey = hex.EncodeToString(bytes)
}
if len(options.Reserved) > 0 {
if len(options.Reserved) != 3 {
return nil, E.New("invalid reserved value, required 3 bytes, got ", len(peer.Reserved))
}
copy(peer.Reserved[:], options.Reserved)
}
peers = append(peers, peer)
}
return peers, nil
}
func ResolvePeers(ctx context.Context, router adapter.Router, peers []PeerConfig) error {
for peerIndex, peer := range peers {
if peer.Endpoint.IsValid() {
continue
}
destinationAddresses, err := router.Lookup(ctx, peer.destination.Fqdn, peer.domainStrategy)
if err != nil {
if len(peers) == 1 {
return E.Cause(err, "resolve endpoint domain")
} else {
return E.Cause(err, "resolve endpoint domain for peer ", peerIndex)
}
}
if len(destinationAddresses) == 0 {
return E.New("no addresses found for endpoint domain: ", peer.destination.Fqdn)
}
peers[peerIndex].Endpoint = netip.AddrPortFrom(destinationAddresses[0], peer.destination.Port)
}
return nil
}