mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-23 17:11:29 +00:00
Improve read wait interface &
Refactor Authenticator interface to struct & Update smux & Update gVisor to 20231204.0 & 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:
parent
2f4f4e18ee
commit
a99c3108a7
|
@ -17,6 +17,7 @@ import (
|
||||||
|
|
||||||
type Router interface {
|
type Router interface {
|
||||||
Service
|
Service
|
||||||
|
PreStarter
|
||||||
PostStarter
|
PostStarter
|
||||||
|
|
||||||
Outbounds() []Outbound
|
Outbounds() []Outbound
|
||||||
|
|
9
box.go
9
box.go
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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
115
common/badtls/read_wait.go
Normal 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
|
13
common/badtls/read_wait_stub.go
Normal file
13
common/badtls/read_wait_stub.go
Normal 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
|
||||||
|
}
|
|
@ -15,6 +15,8 @@ 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
|
||||||
|
@ -23,6 +25,7 @@ type DefaultDialer struct {
|
||||||
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
|
||||||
|
|
|
@ -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
|
||||||
|
|
9
common/dialer/wireguard.go
Normal file
9
common/dialer/wireguard.go
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package dialer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WireGuardListener interface {
|
||||||
|
ListenPacketCompat(network, address string) (net.PacketConn, error)
|
||||||
|
}
|
11
common/dialer/wireguard_control.go
Normal file
11
common/dialer/wireguard_control.go
Normal 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
|
9
common/dialer/wiregurad_stub.go
Normal file
9
common/dialer/wiregurad_stub.go
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
//go:build !with_wireguard
|
||||||
|
|
||||||
|
package dialer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sagernet/sing/common/control"
|
||||||
|
)
|
||||||
|
|
||||||
|
var wgControlFns []control.Func
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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: | / |
|
||||||
|
|
|
@ -15,27 +15,28 @@ 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 |
|
||||||
|-----------------------|------------------|-----------------------|
|
|-----------------------|------------------|-----------------------|
|
||||||
|
|
|
@ -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": [
|
||||||
|
@ -98,6 +108,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.
|
||||||
|
@ -160,18 +180,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,7 +239,7 @@ 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 |
|
||||||
|
|
||||||
|
|
|
@ -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": [
|
||||||
|
@ -98,6 +108,16 @@ tun 接口的 IPv6 前缀。
|
||||||
|
|
||||||
最大传输单元。
|
最大传输单元。
|
||||||
|
|
||||||
|
#### gso
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.8.0 起"
|
||||||
|
|
||||||
|
!!! quote ""
|
||||||
|
|
||||||
|
仅支持 Linux。
|
||||||
|
|
||||||
|
启用通用分段卸载。
|
||||||
|
|
||||||
#### auto_route
|
#### auto_route
|
||||||
|
|
||||||
设置到 Tun 的默认路由。
|
设置到 Tun 的默认路由。
|
||||||
|
@ -157,17 +177,19 @@ UDP NAT 过期时间,以秒为单位,默认为 300(5 分钟)。
|
||||||
|
|
||||||
#### 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
|
||||||
|
|
||||||
|
@ -214,7 +236,7 @@ TCP/IP 栈。
|
||||||
限制被路由的 Android 用户。
|
限制被路由的 Android 用户。
|
||||||
|
|
||||||
| 常用用户 | ID |
|
| 常用用户 | ID |
|
||||||
|--|-----|
|
|------|----|
|
||||||
| 您 | 0 |
|
| 您 | 0 |
|
||||||
| 工作资料 | 10 |
|
| 工作资料 | 10 |
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -45,19 +45,18 @@ 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.
|
||||||
|
|
|
@ -44,20 +44,18 @@ 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). |
|
|
||||||
|
|
||||||
|
|
||||||
除非您确实知道您正在启用什么,否则不建议更改默认构建标签列表。
|
除非您确实知道您正在启用什么,否则不建议更改默认构建标签列表。
|
||||||
|
|
21
go.mod
21
go.mod
|
@ -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-20230829051644-4a68352d0c4a
|
github.com/sagernet/cloudflare-tls v0.0.0-20230829051644-4a68352d0c4a
|
||||||
github.com/sagernet/gomobile v0.0.0-20230915142329-c6740b6d2950
|
github.com/sagernet/gomobile v0.0.0-20230915142329-c6740b6d2950
|
||||||
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.0
|
||||||
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-beta.1
|
github.com/sagernet/sing v0.3.0-beta.6
|
||||||
github.com/sagernet/sing-dns v0.1.11
|
github.com/sagernet/sing-dns v0.1.11
|
||||||
github.com/sagernet/sing-mux v0.1.5
|
github.com/sagernet/sing-mux v0.1.6-beta.1
|
||||||
github.com/sagernet/sing-quic v0.1.5
|
github.com/sagernet/sing-quic v0.1.6-beta.1
|
||||||
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-beta.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-beta.4
|
||||||
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-20230816093905-5a5c285d44a6
|
github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6
|
||||||
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2
|
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2
|
||||||
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
|
||||||
|
@ -78,7 +78,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
|
||||||
|
@ -86,10 +85,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-20231127185646-65229373498e // indirect
|
golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611 // 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-20230822172742-b8732ec3820d // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||||
|
|
47
go.sum
47
go.sum
|
@ -96,46 +96,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-20230829051644-4a68352d0c4a h1:wZHruBxZCsQLXHAozWpnJBL3wJ/XufDpz0qKtgpSnA4=
|
github.com/sagernet/cloudflare-tls v0.0.0-20230829051644-4a68352d0c4a h1:wZHruBxZCsQLXHAozWpnJBL3wJ/XufDpz0qKtgpSnA4=
|
||||||
github.com/sagernet/cloudflare-tls v0.0.0-20230829051644-4a68352d0c4a/go.mod h1:dNV1ZP9y3qx5ltULeKaQZTZWTLHflgW5DES+Ses7cMI=
|
github.com/sagernet/cloudflare-tls v0.0.0-20230829051644-4a68352d0c4a/go.mod h1:dNV1ZP9y3qx5ltULeKaQZTZWTLHflgW5DES+Ses7cMI=
|
||||||
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.0.0-20230915142329-c6740b6d2950 h1:hUz/2mJLgi7l2H36JGpDY+jou9FmI6kAm0ZkU+xPpgE=
|
github.com/sagernet/gomobile v0.0.0-20230915142329-c6740b6d2950 h1:hUz/2mJLgi7l2H36JGpDY+jou9FmI6kAm0ZkU+xPpgE=
|
||||||
github.com/sagernet/gomobile v0.0.0-20230915142329-c6740b6d2950/go.mod h1:5YE39YkJkCcMsfq1jMKkjsrM2GfBoF9JVWnvU89hmvU=
|
github.com/sagernet/gomobile v0.0.0-20230915142329-c6740b6d2950/go.mod h1:5YE39YkJkCcMsfq1jMKkjsrM2GfBoF9JVWnvU89hmvU=
|
||||||
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.0 h1:DvQNPb72lzvNQDe9tcUyHTw8eRv6PLtM2mNYmdlzUMo=
|
||||||
github.com/sagernet/quic-go v0.40.0/go.mod h1:VqtdhlbkeeG5Okhb3eDMb/9o0EoglReHunNT9ukrJAI=
|
github.com/sagernet/quic-go v0.40.0/go.mod h1:VqtdhlbkeeG5Okhb3eDMb/9o0EoglReHunNT9ukrJAI=
|
||||||
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-beta.6 h1:MvCJO2sBKr+k/jt6WfJ/YbohDujEnlaGqA4YQ3/Vh4U=
|
||||||
github.com/sagernet/sing v0.3.0-beta.1 h1:v9VTg1DtNEPqJEWRc+QcQ/KXr3naUyXN+sKU7NkIVbw=
|
github.com/sagernet/sing v0.3.0-beta.6/go.mod h1:9pfuAH6mZfgnz/YjP6xu5sxx882rfyjpcrTdUpd6w3g=
|
||||||
github.com/sagernet/sing v0.3.0-beta.1/go.mod h1:Ce5LNojQOgOiWhiD8pPD6E9H7e2KgtOe3Zxx4Ou5u80=
|
|
||||||
github.com/sagernet/sing-dns v0.1.11 h1:PPrMCVVrAeR3f5X23I+cmvacXJ+kzuyAsBiWyUKhGSE=
|
github.com/sagernet/sing-dns v0.1.11 h1:PPrMCVVrAeR3f5X23I+cmvacXJ+kzuyAsBiWyUKhGSE=
|
||||||
github.com/sagernet/sing-dns v0.1.11/go.mod h1:zJ/YjnYB61SYE+ubMcMqVdpaSvsyQ2iShQGO3vuLvvE=
|
github.com/sagernet/sing-dns v0.1.11/go.mod h1:zJ/YjnYB61SYE+ubMcMqVdpaSvsyQ2iShQGO3vuLvvE=
|
||||||
github.com/sagernet/sing-mux v0.1.5 h1:jUbYth9QQd1wsDmU8Ush+fKce7lNo9TMv2dp8PJtSOY=
|
github.com/sagernet/sing-mux v0.1.6-beta.1 h1:ADs1TgiMfA628Y2qfv21tEvePDZjBRRYddwtNFZiwe8=
|
||||||
github.com/sagernet/sing-mux v0.1.5/go.mod h1:MoH6Soz1R+CYZcCeIXZWx6fkZa6hQc9o3HZu9G6CDTw=
|
github.com/sagernet/sing-mux v0.1.6-beta.1/go.mod h1:WWtRmrwCDgb+g+7Da6o62I9WiMNB0a3w6BJhEpNQlNA=
|
||||||
github.com/sagernet/sing-quic v0.1.5 h1:PIQzE4cGrry+JkkMEJH/EH3wRkv/QgD48+ScNr/2oig=
|
github.com/sagernet/sing-quic v0.1.6-beta.1 h1:OhNk0jxp7yZSvG/2Pg/gRLrhApqM1gFLB8LG2S4wSCc=
|
||||||
github.com/sagernet/sing-quic v0.1.5/go.mod h1:n2mXukpubasyV4SlWyyW0+LCdAn7DZ8/brAkUxZujrw=
|
github.com/sagernet/sing-quic v0.1.6-beta.1/go.mod h1:+OLsIVPmgHzCqcF3lGBVngc7ItYM6v3T0rn6Qtd4T1g=
|
||||||
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-beta.1 h1:0vbf4XA6QybVXhUqlmbceYWYpH96s4RRt6J+K+Xtr0A=
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.1.5/go.mod h1:KF65y8lI5PGHyMgRZGYXYsH9ilgRc/yr+NYbSNGuBm4=
|
github.com/sagernet/sing-shadowsocks2 v0.1.6-beta.1/go.mod h1:H7p/X9U15oLBpdAHPk+n3GMEuJiimImj1B3GfwY2igE=
|
||||||
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-beta.4 h1:KcblUxYmG9/Qyn38AP1uFf/a/fh1kmFrybmF0eYlfrQ=
|
||||||
github.com/sagernet/sing-tun v0.1.24/go.mod h1:Mnd7+8iGNb9uGnMAh3bp0ZA+nPFBZNaMHZPMEGdAQJM=
|
github.com/sagernet/sing-tun v0.2.0-beta.4/go.mod h1:bV5YMmTun6g8AavxZDLu9S69MLVlu3Y9isBr3z0ujW4=
|
||||||
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-20230816093905-5a5c285d44a6 h1:Px+hN4Vzgx+iCGVnWH5A8eR7JhNnIV3rGQmBxA7cw6Q=
|
github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 h1:Px+hN4Vzgx+iCGVnWH5A8eR7JhNnIV3rGQmBxA7cw6Q=
|
||||||
github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6/go.mod h1:zovq6vTvEM6ECiqE3Eeb9rpIylPpamPcmrJ9tv0Bt0M=
|
github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6/go.mod h1:zovq6vTvEM6ECiqE3Eeb9rpIylPpamPcmrJ9tv0Bt0M=
|
||||||
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 h1:kDUqhc9Vsk5HJuhfIATJ8oQwBmpOZJuozQG7Vk88lL4=
|
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 h1:kDUqhc9Vsk5HJuhfIATJ8oQwBmpOZJuozQG7Vk88lL4=
|
||||||
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM=
|
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM=
|
||||||
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=
|
||||||
|
@ -171,8 +168,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.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
|
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
|
||||||
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||||
golang.org/x/exp v0.0.0-20231127185646-65229373498e h1:Gvh4YaCaXNs6dKTlfgismwWZKyjVZXwOPfIyUaqU3No=
|
golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611 h1:qCEDpW1G+vcj3Y7Fy52pEM1AWm3abj8WimGYejI3SC4=
|
||||||
golang.org/x/exp v0.0.0-20231127185646-65229373498e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
|
golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611/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=
|
||||||
|
@ -185,10 +182,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=
|
||||||
|
@ -197,8 +194,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=
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -74,6 +74,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,
|
||||||
|
@ -167,10 +168,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,
|
||||||
|
|
|
@ -146,6 +146,7 @@ type DialerOptions struct {
|
||||||
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:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServerOptions struct {
|
type ServerOptions struct {
|
||||||
|
|
|
@ -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"`
|
||||||
|
|
|
@ -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"`
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,7 +35,16 @@ var (
|
||||||
|
|
||||||
type WireGuard struct {
|
type WireGuard struct {
|
||||||
myOutboundAdapter
|
myOutboundAdapter
|
||||||
bind *wireguard.ClientBind
|
ctx context.Context
|
||||||
|
workers int
|
||||||
|
peers []wireguard.PeerConfig
|
||||||
|
useStdNetBind bool
|
||||||
|
listener N.Dialer
|
||||||
|
ipcConf string
|
||||||
|
|
||||||
|
pauseManager pause.Manager
|
||||||
|
pauseCallback *list.Element[pause.Callback]
|
||||||
|
bind conn.Bind
|
||||||
device *device.Device
|
device *device.Device
|
||||||
tunDevice wireguard.Device
|
tunDevice wireguard.Device
|
||||||
}
|
}
|
||||||
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
decoder := json.NewDecoder(json.NewCommentFilter(bytes.NewReader(content)))
|
decoder := json.NewDecoder(json.NewCommentFilter(bytes.NewReader(content)))
|
||||||
decoder.DisallowUnknownFields()
|
decoder.DisallowUnknownFields()
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,6 +88,7 @@ type ClientPacketConn struct {
|
||||||
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 {
|
||||||
|
|
45
transport/trojan/protocol_wait.go
Normal file
45
transport/trojan/protocol_wait.go
Normal 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
|
||||||
|
}
|
|
@ -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) {
|
||||||
|
|
45
transport/trojan/service_wait.go
Normal file
45
transport/trojan/service_wait.go
Normal 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
|
||||||
|
}
|
|
@ -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,15 +64,12 @@ 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),
|
|
||||||
Addr: c.connectAddr,
|
|
||||||
},
|
|
||||||
done: make(chan struct{}),
|
done: make(chan struct{}),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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"
|
||||||
|
@ -21,6 +23,7 @@ var _ Device = (*SystemDevice)(nil)
|
||||||
type SystemDevice struct {
|
type SystemDevice struct {
|
||||||
dialer N.Dialer
|
dialer N.Dialer
|
||||||
device tun.Tun
|
device tun.Tun
|
||||||
|
batchDevice tun.LinuxTUN
|
||||||
name string
|
name string
|
||||||
mtu int
|
mtu int
|
||||||
events chan wgTun.Event
|
events chan wgTun.Event
|
||||||
|
@ -28,7 +31,7 @@ type SystemDevice struct {
|
||||||
addr6 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,11 +62,20 @@ 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,
|
||||||
|
batchDevice: batchDevice,
|
||||||
name: interfaceName,
|
name: interfaceName,
|
||||||
mtu: int(mtu),
|
mtu: int(mtu),
|
||||||
events: make(chan wgTun.Event),
|
events: make(chan wgTun.Event),
|
||||||
|
@ -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 {
|
||||||
|
count, err = w.batchDevice.BatchRead(bufs, offset, sizes)
|
||||||
|
} else {
|
||||||
|
sizes[0], err = w.device.Read(bufs[0][offset:])
|
||||||
if err == nil {
|
if err == nil {
|
||||||
count = 1
|
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) {
|
||||||
|
if w.batchDevice != nil {
|
||||||
|
return 0, w.batchDevice.BatchWrite(bufs, offset)
|
||||||
|
} else {
|
||||||
for _, b := range bufs {
|
for _, b := range bufs {
|
||||||
_, err = w.device.Write(b[offset:])
|
_, err = w.device.Write(b[offset:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
148
transport/wireguard/resolve.go
Normal file
148
transport/wireguard/resolve.go
Normal 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
|
||||||
|
}
|
Loading…
Reference in a new issue