Compare commits

..

41 Commits

Author SHA1 Message Date
世界
1b680d4748 documentation: Update changelog 2023-04-14 19:24:16 +08:00
世界
d407b35b49 Update dependencies 2023-04-14 19:19:27 +08:00
世界
7f49c45673 urltest: Recheck after network change 2023-04-13 19:07:20 +08:00
世界
221cce6e57 Fix wireguard reconnect 2023-04-13 19:07:19 +08:00
世界
95809dce11 Fix deadline race 2023-04-13 19:07:19 +08:00
世界
32875e7cbc Fix conntrack return pointer 2023-04-13 13:23:04 +08:00
世界
3de56620ce android: Remove Seq.Delete warning 2023-04-13 13:23:04 +08:00
世界
3651605d3b Use HTTPS URLTest source 2023-04-13 11:02:00 +08:00
世界
7d1174e545 Print sniff result 2023-04-13 09:03:52 +08:00
世界
bd9e6e5cd9 Fix processing domain address in packet 2023-04-12 20:49:45 +08:00
世界
ed37cb858b Update documentation 2023-04-12 16:17:01 +08:00
世界
62bcf22c26 Fix parsing query in http path 2023-04-12 15:58:03 +08:00
Xiaokang Wang (Shelikhoo)
84bd997742 Fix '?' at end of WebSocket path get escaped
This fix align sing-box's behaviour with V2Ray when it comes to processing ? at the end of WebSocket's path.
2023-04-12 15:58:03 +08:00
H1JK
a548e45ad7 Add BaseContext to http servers 2023-04-11 18:00:43 +08:00
H1JK
5c1de2bb06 Fix gRPC service name escape 2023-04-11 18:00:43 +08:00
Hellojack
e5f0add1ab Remove TLS requirement for gRPC client 2023-04-11 18:00:43 +08:00
PuerNya
70e47df295 Add headers option for HTTP outbound 2023-04-11 17:49:37 +08:00
世界
f20642d6fd Fix workflows 2023-04-11 17:44:24 +08:00
世界
73fa926b48 clash-api: Add Clash.Meta APIs 2023-04-11 17:44:24 +08:00
世界
5d9dce8078 Fix find process user 2023-04-11 14:40:10 +08:00
世界
e20e2d57c9 Fix set HTTP TLS ALPN 2023-04-11 14:40:10 +08:00
世界
25f31890ed deadline: Fix disable pipe 2023-04-11 14:13:22 +08:00
世界
194b36b987 Fix v2ray http transport server read request 2023-04-11 10:30:10 +08:00
世界
1e39196bc9 Update cancel context usage 2023-04-10 17:01:21 +08:00
世界
da82a41697 Fix write log to stderr 2023-04-10 08:48:58 +08:00
世界
aceb82a75e Fix fallback packet conn 2023-04-09 22:53:06 +08:00
世界
f2749bc29d platform: Add stderr redirect 2023-04-09 22:52:45 +08:00
世界
55afaa87da Fix write http status after response sent 2023-04-09 22:52:45 +08:00
世界
d77940ab39 Fix deadline not initialized 2023-04-09 22:52:45 +08:00
世界
1eea446e45 documentation: Update external-ui download fields 2023-04-09 15:44:19 +08:00
世界
19c6241e10 Update dependencies 2023-04-09 15:37:06 +08:00
世界
b290d0ed32 documentation: Update changelog 2023-04-09 15:06:20 +08:00
世界
2afe662646 clash api: download clash-dashboard if external-ui directory is empty 2023-04-09 12:39:33 +08:00
世界
107a9a3b51 Fix read deadline implementation 2023-04-09 12:39:33 +08:00
世界
3d0c64f523 Replace usages of uber/atomic 2023-04-09 12:39:33 +08:00
世界
422ca34ac2 Fix timeout error check 2023-04-08 12:25:51 +08:00
世界
6d63f9255f documentation: Update changelog 2023-04-08 09:37:58 +08:00
世界
6f2cc9761d Add multi-peer support for wireguard outbound 2023-04-08 09:37:58 +08:00
世界
b484d9bca6 Add fakeip support 2023-04-08 09:37:58 +08:00
世界
58c4fd745a Add L3 routing support 2023-04-08 09:17:12 +08:00
世界
7d1e6affb3 Add dns reverse mapping 2023-04-08 09:17:03 +08:00
102 changed files with 889 additions and 2178 deletions

View File

@@ -8,10 +8,6 @@ The universal proxy platform.
https://sing-box.sagernet.org https://sing-box.sagernet.org
## Support
https://community.sagernet.org/c/sing-box/
## License ## License
``` ```

View File

@@ -37,7 +37,6 @@ type Router interface {
LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error) LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error)
InterfaceFinder() control.InterfaceFinder InterfaceFinder() control.InterfaceFinder
UpdateInterfaces() error
DefaultInterface() string DefaultInterface() string
AutoDetectInterface() bool AutoDetectInterface() bool
AutoDetectInterfaceFunc() control.Func AutoDetectInterfaceFunc() control.Func

9
box.go
View File

@@ -62,7 +62,6 @@ func New(options Options) (*Box, error) {
defaultLogWriter = io.Discard defaultLogWriter = io.Discard
} }
logFactory, err := log.New(log.Options{ logFactory, err := log.New(log.Options{
Context: ctx,
Options: common.PtrValueOrDefault(options.Log), Options: common.PtrValueOrDefault(options.Log),
Observable: needClashAPI, Observable: needClashAPI,
DefaultWriter: defaultLogWriter, DefaultWriter: defaultLogWriter,
@@ -134,16 +133,10 @@ func New(options Options) (*Box, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if options.PlatformInterface != nil {
err = options.PlatformInterface.Initialize(ctx, router)
if err != nil {
return nil, E.Cause(err, "initialize platform interface")
}
}
preServices := make(map[string]adapter.Service) preServices := make(map[string]adapter.Service)
postServices := make(map[string]adapter.Service) postServices := make(map[string]adapter.Service)
if needClashAPI { if needClashAPI {
clashServer, err := experimental.NewClashServer(ctx, router, logFactory.(log.ObservableFactory), common.PtrValueOrDefault(options.Experimental.ClashAPI)) clashServer, err := experimental.NewClashServer(router, logFactory.(log.ObservableFactory), common.PtrValueOrDefault(options.Experimental.ClashAPI))
if err != nil { if err != nil {
return nil, E.Cause(err, "create clash api server") return nil, E.Cause(err, "create clash api server")
} }

44
cmd/sing-box/debug.go Normal file
View File

@@ -0,0 +1,44 @@
//go:build debug
package main
import (
"encoding/json"
"net/http"
_ "net/http/pprof"
"runtime"
"runtime/debug"
"github.com/sagernet/sing-box/common/badjson"
"github.com/sagernet/sing-box/log"
"github.com/dustin/go-humanize"
)
func init() {
http.HandleFunc("/debug/gc", func(writer http.ResponseWriter, request *http.Request) {
writer.WriteHeader(http.StatusNoContent)
go debug.FreeOSMemory()
})
http.HandleFunc("/debug/memory", func(writer http.ResponseWriter, request *http.Request) {
var memStats runtime.MemStats
runtime.ReadMemStats(&memStats)
var memObject badjson.JSONObject
memObject.Put("heap", humanize.IBytes(memStats.HeapInuse))
memObject.Put("stack", humanize.IBytes(memStats.StackInuse))
memObject.Put("idle", humanize.IBytes(memStats.HeapIdle-memStats.HeapReleased))
memObject.Put("goroutines", runtime.NumGoroutine())
memObject.Put("rss", rusageMaxRSS())
encoder := json.NewEncoder(writer)
encoder.SetIndent("", " ")
encoder.Encode(memObject)
})
go func() {
err := http.ListenAndServe("0.0.0.0:8964", nil)
if err != nil {
log.Debug(err)
}
}()
}

View File

@@ -1,4 +1,6 @@
package box //go:build debug
package main
import ( import (
"runtime" "runtime"

View File

@@ -1,6 +1,6 @@
//go:build !linux //go:build debug && !linux
package box package main
func rusageMaxRSS() float64 { func rusageMaxRSS() float64 {
return -1 return -1

View File

@@ -1,4 +1,4 @@
//go:build go1.20 && !go1.21 //go:build go1.19 && !go1.20
package badtls package badtls
@@ -14,60 +14,39 @@ import (
"sync/atomic" "sync/atomic"
"unsafe" "unsafe"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio" "github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
aTLS "github.com/sagernet/sing/common/tls"
) )
type Conn struct { type Conn struct {
*tls.Conn *tls.Conn
writer N.ExtendedWriter writer N.ExtendedWriter
isHandshakeComplete *atomic.Bool activeCall *int32
activeCall *atomic.Int32 closeNotifySent *bool
closeNotifySent *bool version *uint16
version *uint16 rand io.Reader
rand io.Reader halfAccess *sync.Mutex
halfAccess *sync.Mutex halfError *error
halfError *error cipher cipher.AEAD
cipher cipher.AEAD explicitNonceLen int
explicitNonceLen int halfPtr uintptr
halfPtr uintptr halfSeq []byte
halfSeq []byte halfScratchBuf []byte
halfScratchBuf []byte
} }
func TryCreate(conn aTLS.Conn) aTLS.Conn { func Create(conn *tls.Conn) (TLSConn, error) {
tlsConn, ok := conn.(*tls.Conn) if !handshakeComplete(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") return nil, E.New("handshake not finished")
} }
rawConn := reflect.Indirect(reflect.ValueOf(conn))
rawActiveCall := rawConn.FieldByName("activeCall") rawActiveCall := rawConn.FieldByName("activeCall")
if !rawActiveCall.IsValid() || rawActiveCall.Kind() != reflect.Struct { if !rawActiveCall.IsValid() || rawActiveCall.Kind() != reflect.Int32 {
return nil, E.New("badtls: invalid active call") return nil, E.New("badtls: invalid active call")
} }
activeCall := (*atomic.Int32)(unsafe.Pointer(rawActiveCall.UnsafeAddr())) activeCall := (*int32)(unsafe.Pointer(rawActiveCall.UnsafeAddr()))
rawHalfConn := rawConn.FieldByName("out") rawHalfConn := rawConn.FieldByName("out")
if !rawHalfConn.IsValid() || rawHalfConn.Kind() != reflect.Struct { if !rawHalfConn.IsValid() || rawHalfConn.Kind() != reflect.Struct {
return nil, E.New("badtls: invalid half conn") return nil, E.New("badtls: invalid half conn")
@@ -129,20 +108,19 @@ func Create(conn *tls.Conn) (aTLS.Conn, error) {
} }
halfScratchBuf := rawHalfScratchBuf.Bytes() halfScratchBuf := rawHalfScratchBuf.Bytes()
return &Conn{ return &Conn{
Conn: conn, Conn: conn,
writer: bufio.NewExtendedWriter(conn.NetConn()), writer: bufio.NewExtendedWriter(conn.NetConn()),
isHandshakeComplete: isHandshakeComplete, activeCall: activeCall,
activeCall: activeCall, closeNotifySent: closeNotifySent,
closeNotifySent: closeNotifySent, version: version,
version: version, halfAccess: halfAccess,
halfAccess: halfAccess, halfError: halfError,
halfError: halfError, cipher: aeadCipher,
cipher: aeadCipher, explicitNonceLen: explicitNonceLen,
explicitNonceLen: explicitNonceLen, rand: randReader,
rand: randReader, halfPtr: rawHalfConn.UnsafeAddr(),
halfPtr: rawHalfConn.UnsafeAddr(), halfSeq: halfSeq,
halfSeq: halfSeq, halfScratchBuf: halfScratchBuf,
halfScratchBuf: halfScratchBuf,
}, nil }, nil
} }
@@ -152,15 +130,15 @@ func (c *Conn) WriteBuffer(buffer *buf.Buffer) error {
return common.Error(c.Write(buffer.Bytes())) return common.Error(c.Write(buffer.Bytes()))
} }
for { for {
x := c.activeCall.Load() x := atomic.LoadInt32(c.activeCall)
if x&1 != 0 { if x&1 != 0 {
return net.ErrClosed return net.ErrClosed
} }
if c.activeCall.CompareAndSwap(x, x+2) { if atomic.CompareAndSwapInt32(c.activeCall, x, x+2) {
break break
} }
} }
defer c.activeCall.Add(-2) defer atomic.AddInt32(c.activeCall, -2)
c.halfAccess.Lock() c.halfAccess.Lock()
defer c.halfAccess.Unlock() defer c.halfAccess.Unlock()
if err := *c.halfError; err != nil { if err := *c.halfError; err != nil {
@@ -208,7 +186,6 @@ func (c *Conn) WriteBuffer(buffer *buf.Buffer) error {
binary.BigEndian.PutUint16(outBuf[3:], uint16(dataLen+c.explicitNonceLen+c.cipher.Overhead())) binary.BigEndian.PutUint16(outBuf[3:], uint16(dataLen+c.explicitNonceLen+c.cipher.Overhead()))
} }
incSeq(c.halfPtr) incSeq(c.halfPtr)
log.Trace("badtls write ", buffer.Len())
return c.writer.WriteBuffer(buffer) return c.writer.WriteBuffer(buffer)
} }

View File

@@ -1,4 +1,4 @@
//go:build !go1.19 || go1.21 //go:build !go1.19 || go1.20
package badtls package badtls

13
common/badtls/conn.go Normal file
View File

@@ -0,0 +1,13 @@
package badtls
import (
"context"
"crypto/tls"
"net"
)
type TLSConn interface {
net.Conn
HandshakeContext(ctx context.Context) error
ConnectionState() tls.ConnectionState
}

View File

@@ -1,8 +1,9 @@
//go:build go1.20 && !go.1.21 //go:build go1.19 && !go.1.20
package badtls package badtls
import ( import (
"crypto/tls"
"reflect" "reflect"
_ "unsafe" _ "unsafe"
) )
@@ -15,6 +16,9 @@ const (
//go:linkname errShutdown crypto/tls.errShutdown //go:linkname errShutdown crypto/tls.errShutdown
var errShutdown error var errShutdown error
//go:linkname handshakeComplete crypto/tls.(*Conn).handshakeComplete
func handshakeComplete(conn *tls.Conn) bool
//go:linkname incSeq crypto/tls.(*halfConn).incSeq //go:linkname incSeq crypto/tls.(*halfConn).incSeq
func incSeq(conn uintptr) func incSeq(conn uintptr)

View File

@@ -7,6 +7,7 @@ import (
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/dialer/conntrack" "github.com/sagernet/sing-box/common/dialer/conntrack"
"github.com/sagernet/sing-box/common/warning"
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"
"github.com/sagernet/sing/common/control" "github.com/sagernet/sing/common/control"
@@ -16,6 +17,41 @@ import (
"github.com/sagernet/tfo-go" "github.com/sagernet/tfo-go"
) )
var warnBindInterfaceOnUnsupportedPlatform = warning.New(
func() bool {
return !(C.IsLinux || C.IsWindows || C.IsDarwin)
},
"outbound option `bind_interface` is only supported on Linux and Windows",
)
var warnRoutingMarkOnUnsupportedPlatform = warning.New(
func() bool {
return !C.IsLinux
},
"outbound option `routing_mark` is only supported on Linux",
)
var warnReuseAdderOnUnsupportedPlatform = warning.New(
func() bool {
return !(C.IsDarwin || C.IsDragonfly || C.IsFreebsd || C.IsLinux || C.IsNetbsd || C.IsOpenbsd || C.IsSolaris || C.IsWindows)
},
"outbound option `reuse_addr` is unsupported on current platform",
)
var warnProtectPathOnNonAndroid = warning.New(
func() bool {
return !C.IsAndroid
},
"outbound option `protect_path` is only supported on Android",
)
var warnTFOOnUnsupportedPlatform = warning.New(
func() bool {
return !(C.IsDarwin || C.IsFreebsd || C.IsLinux || C.IsWindows)
},
"outbound option `tcp_fast_open` is unsupported on current platform",
)
type DefaultDialer struct { type DefaultDialer struct {
dialer4 tfo.Dialer dialer4 tfo.Dialer
dialer6 tfo.Dialer dialer6 tfo.Dialer
@@ -30,6 +66,7 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
var dialer net.Dialer var dialer net.Dialer
var listener net.ListenConfig var listener net.ListenConfig
if options.BindInterface != "" { if options.BindInterface != "" {
warnBindInterfaceOnUnsupportedPlatform.Check()
bindFunc := control.BindToInterface(router.InterfaceFinder(), options.BindInterface, -1) bindFunc := control.BindToInterface(router.InterfaceFinder(), options.BindInterface, -1)
dialer.Control = control.Append(dialer.Control, bindFunc) dialer.Control = control.Append(dialer.Control, bindFunc)
listener.Control = control.Append(listener.Control, bindFunc) listener.Control = control.Append(listener.Control, bindFunc)
@@ -43,6 +80,7 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
listener.Control = control.Append(listener.Control, bindFunc) listener.Control = control.Append(listener.Control, bindFunc)
} }
if options.RoutingMark != 0 { if options.RoutingMark != 0 {
warnRoutingMarkOnUnsupportedPlatform.Check()
dialer.Control = control.Append(dialer.Control, control.RoutingMark(options.RoutingMark)) dialer.Control = control.Append(dialer.Control, control.RoutingMark(options.RoutingMark))
listener.Control = control.Append(listener.Control, control.RoutingMark(options.RoutingMark)) listener.Control = control.Append(listener.Control, control.RoutingMark(options.RoutingMark))
} else if router.DefaultMark() != 0 { } else if router.DefaultMark() != 0 {
@@ -50,9 +88,11 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
listener.Control = control.Append(listener.Control, control.RoutingMark(router.DefaultMark())) listener.Control = control.Append(listener.Control, control.RoutingMark(router.DefaultMark()))
} }
if options.ReuseAddr { if options.ReuseAddr {
warnReuseAdderOnUnsupportedPlatform.Check()
listener.Control = control.Append(listener.Control, control.ReuseAddr()) listener.Control = control.Append(listener.Control, control.ReuseAddr())
} }
if options.ProtectPath != "" { if options.ProtectPath != "" {
warnProtectPathOnNonAndroid.Check()
dialer.Control = control.Append(dialer.Control, control.ProtectPath(options.ProtectPath)) dialer.Control = control.Append(dialer.Control, control.ProtectPath(options.ProtectPath))
listener.Control = control.Append(listener.Control, control.ProtectPath(options.ProtectPath)) listener.Control = control.Append(listener.Control, control.ProtectPath(options.ProtectPath))
} }
@@ -61,6 +101,9 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
} else { } else {
dialer.Timeout = C.TCPTimeout dialer.Timeout = C.TCPTimeout
} }
if options.TCPFastOpen {
warnTFOOnUnsupportedPlatform.Check()
}
var udpFragment bool var udpFragment bool
if options.UDPFragment != nil { if options.UDPFragment != nil {
udpFragment = *options.UDPFragment udpFragment = *options.UDPFragment

View File

@@ -11,6 +11,7 @@ import (
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio" "github.com/sagernet/sing/common/bufio"
"github.com/sagernet/sing/common/bufio/deadline"
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"
@@ -28,10 +29,9 @@ type Client struct {
maxConnections int maxConnections int
minStreams int minStreams int
maxStreams int maxStreams int
paddingEnabled bool
} }
func NewClient(ctx context.Context, dialer N.Dialer, protocol Protocol, maxConnections int, minStreams int, maxStreams int, paddingEnabled bool) (*Client, error) { func NewClient(ctx context.Context, dialer N.Dialer, protocol Protocol, maxConnections int, minStreams int, maxStreams int) *Client {
return &Client{ return &Client{
ctx: ctx, ctx: ctx,
dialer: dialer, dialer: dialer,
@@ -39,11 +39,10 @@ func NewClient(ctx context.Context, dialer N.Dialer, protocol Protocol, maxConne
maxConnections: maxConnections, maxConnections: maxConnections,
minStreams: minStreams, minStreams: minStreams,
maxStreams: maxStreams, maxStreams: maxStreams,
paddingEnabled: paddingEnabled, }
}, nil
} }
func NewClientWithOptions(ctx context.Context, dialer N.Dialer, options option.MultiplexOptions) (*Client, error) { func NewClientWithOptions(ctx context.Context, dialer N.Dialer, options option.MultiplexOptions) (N.Dialer, error) {
if !options.Enabled { if !options.Enabled {
return nil, nil return nil, nil
} }
@@ -54,7 +53,7 @@ func NewClientWithOptions(ctx context.Context, dialer N.Dialer, options option.M
if err != nil { if err != nil {
return nil, err return nil, err
} }
return NewClient(ctx, dialer, protocol, options.MaxConnections, options.MinStreams, options.MaxStreams, options.Padding) return NewClient(ctx, dialer, protocol, options.MaxConnections, options.MinStreams, options.MaxStreams), nil
} }
func (c *Client) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { func (c *Client) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
@@ -70,7 +69,7 @@ func (c *Client) DialContext(ctx context.Context, network string, destination M.
if err != nil { if err != nil {
return nil, err return nil, err
} }
return bufio.NewUnbindPacketConn(&ClientPacketConn{ExtendedConn: bufio.NewExtendedConn(stream), destination: destination}), nil return bufio.NewBindPacketConn(deadline.NewPacketConn(bufio.NewNetPacketConn(&ClientPacketConn{ExtendedConn: bufio.NewExtendedConn(stream), destination: destination})), destination), nil
default: default:
return nil, E.Extend(N.ErrUnknownNetwork, network) return nil, E.Extend(N.ErrUnknownNetwork, network)
} }
@@ -81,7 +80,7 @@ func (c *Client) ListenPacket(ctx context.Context, destination M.Socksaddr) (net
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &ClientPacketAddrConn{ExtendedConn: bufio.NewExtendedConn(stream), destination: destination}, nil return deadline.NewPacketConn(&ClientPacketAddrConn{ExtendedConn: bufio.NewExtendedConn(stream), destination: destination}), nil
} }
func (c *Client) openStream() (net.Conn, error) { func (c *Client) openStream() (net.Conn, error) {
@@ -122,16 +121,17 @@ func (c *Client) offer() (abstractSession, error) {
sessions = append(sessions, element.Value) sessions = append(sessions, element.Value)
element = element.Next() element = element.Next()
} }
session := common.MinBy(common.Filter(sessions, abstractSession.CanTakeNewRequest), abstractSession.NumStreams) sLen := len(sessions)
if session == nil { if sLen == 0 {
return c.offerNew() return c.offerNew()
} }
session := common.MinBy(sessions, abstractSession.NumStreams)
numStreams := session.NumStreams() numStreams := session.NumStreams()
if numStreams == 0 { if numStreams == 0 {
return session, nil return session, nil
} }
if c.maxConnections > 0 { if c.maxConnections > 0 {
if len(sessions) >= c.maxConnections || numStreams < c.minStreams { if sLen >= c.maxConnections || numStreams < c.minStreams {
return session, nil return session, nil
} }
} else { } else {
@@ -147,19 +147,10 @@ func (c *Client) offerNew() (abstractSession, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
var version byte if vectorisedWriter, isVectorised := bufio.CreateVectorisedWriter(conn); isVectorised {
if c.paddingEnabled { conn = &vectorisedProtocolConn{protocolConn{Conn: conn, protocol: c.protocol}, vectorisedWriter}
version = Version1
} else { } else {
version = Version0 conn = &protocolConn{Conn: conn, protocol: c.protocol}
}
conn = newProtocolConn(conn, Request{
Version: version,
Protocol: c.protocol,
PaddingEnabled: c.paddingEnabled,
})
if c.paddingEnabled {
conn = newPaddingConn(conn)
} }
session, err := c.protocol.newClient(conn) session, err := c.protocol.newClient(conn)
if err != nil { if err != nil {
@@ -169,15 +160,6 @@ func (c *Client) offerNew() (abstractSession, error) {
return session, nil return session, nil
} }
func (c *Client) Reset() {
c.access.Lock()
defer c.access.Unlock()
for _, session := range c.connections.Array() {
session.Close()
}
c.connections.Init()
}
func (c *Client) Close() error { func (c *Client) Close() error {
c.access.Lock() c.access.Lock()
defer c.access.Unlock() defer c.access.Unlock()
@@ -224,7 +206,7 @@ func (c *ClientConn) Write(b []byte) (n int, err error) {
Network: N.NetworkTCP, Network: N.NetworkTCP,
Destination: c.destination, Destination: c.destination,
} }
_buffer := buf.StackNewSize(streamRequestLen(request) + len(b)) _buffer := buf.StackNewSize(requestLen(request) + len(b))
defer common.KeepAlive(_buffer) defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer) buffer := common.Dup(_buffer)
defer buffer.Release() defer buffer.Release()
@@ -268,10 +250,6 @@ func (c *ClientConn) WriterReplaceable() bool {
return c.requestWrite return c.requestWrite
} }
func (c *ClientConn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *ClientConn) Upstream() any { func (c *ClientConn) Upstream() any {
return c.Conn return c.Conn
} }
@@ -318,7 +296,7 @@ func (c *ClientPacketConn) writeRequest(payload []byte) (n int, err error) {
Network: N.NetworkUDP, Network: N.NetworkUDP,
Destination: c.destination, Destination: c.destination,
} }
rLen := streamRequestLen(request) rLen := requestLen(request)
if len(payload) > 0 { if len(payload) > 0 {
rLen += 2 + len(payload) rLen += 2 + len(payload)
} }
@@ -400,10 +378,6 @@ func (c *ClientPacketConn) RemoteAddr() net.Addr {
return c.destination.UDPAddr() return c.destination.UDPAddr()
} }
func (c *ClientPacketConn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *ClientPacketConn) Upstream() any { func (c *ClientPacketConn) Upstream() any {
return c.ExtendedConn return c.ExtendedConn
} }
@@ -463,7 +437,7 @@ func (c *ClientPacketAddrConn) writeRequest(payload []byte, destination M.Socksa
Destination: c.destination, Destination: c.destination,
PacketAddr: true, PacketAddr: true,
} }
rLen := streamRequestLen(request) rLen := requestLen(request)
if len(payload) > 0 { if len(payload) > 0 {
rLen += M.SocksaddrSerializer.AddrPortLen(destination) + 2 + len(payload) rLen += M.SocksaddrSerializer.AddrPortLen(destination) + 2 + len(payload)
} }
@@ -545,10 +519,6 @@ func (c *ClientPacketAddrConn) FrontHeadroom() int {
return 2 + M.MaxSocksaddrLength return 2 + M.MaxSocksaddrLength
} }
func (c *ClientPacketAddrConn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *ClientPacketAddrConn) Upstream() any { func (c *ClientPacketAddrConn) Upstream() any {
return c.ExtendedConn return c.ExtendedConn
} }

View File

@@ -1,235 +0,0 @@
package mux
import (
"context"
"crypto/tls"
"io"
"net"
"net/http"
"net/url"
"os"
"time"
"github.com/sagernet/sing-box/transport/v2rayhttp"
"github.com/sagernet/sing/common/atomic"
"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"
"golang.org/x/net/http2"
)
const idleTimeout = 30 * time.Second
var _ abstractSession = (*H2MuxServerSession)(nil)
type H2MuxServerSession struct {
server http2.Server
active atomic.Int32
conn net.Conn
inbound chan net.Conn
done chan struct{}
}
func NewH2MuxServer(conn net.Conn) *H2MuxServerSession {
session := &H2MuxServerSession{
conn: conn,
inbound: make(chan net.Conn),
done: make(chan struct{}),
server: http2.Server{
IdleTimeout: idleTimeout,
},
}
go func() {
session.server.ServeConn(conn, &http2.ServeConnOpts{
Handler: session,
})
_ = session.Close()
}()
return session
}
func (s *H2MuxServerSession) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
s.active.Add(1)
defer s.active.Add(-1)
writer.WriteHeader(http.StatusOK)
conn := newHTTP2Wrapper(&v2rayhttp.ServerHTTPConn{
HTTP2Conn: v2rayhttp.NewHTTPConn(request.Body, writer),
Flusher: writer.(http.Flusher),
})
s.inbound <- conn
select {
case <-conn.done:
case <-s.done:
}
}
func (s *H2MuxServerSession) Open() (net.Conn, error) {
return nil, os.ErrInvalid
}
func (s *H2MuxServerSession) Accept() (net.Conn, error) {
select {
case conn := <-s.inbound:
return conn, nil
case <-s.done:
return nil, os.ErrClosed
}
}
func (s *H2MuxServerSession) NumStreams() int {
return int(s.active.Load())
}
func (s *H2MuxServerSession) Close() error {
select {
case <-s.done:
default:
close(s.done)
}
return s.conn.Close()
}
func (s *H2MuxServerSession) IsClosed() bool {
select {
case <-s.done:
return true
default:
return false
}
}
func (s *H2MuxServerSession) CanTakeNewRequest() bool {
return false
}
type h2MuxConnWrapper struct {
N.ExtendedConn
done chan struct{}
}
func newHTTP2Wrapper(conn net.Conn) *h2MuxConnWrapper {
return &h2MuxConnWrapper{
ExtendedConn: bufio.NewExtendedConn(conn),
done: make(chan struct{}),
}
}
func (w *h2MuxConnWrapper) Write(p []byte) (n int, err error) {
select {
case <-w.done:
return 0, net.ErrClosed
default:
}
return w.ExtendedConn.Write(p)
}
func (w *h2MuxConnWrapper) WriteBuffer(buffer *buf.Buffer) error {
select {
case <-w.done:
return net.ErrClosed
default:
}
return w.ExtendedConn.WriteBuffer(buffer)
}
func (w *h2MuxConnWrapper) Close() error {
select {
case <-w.done:
default:
close(w.done)
}
return w.ExtendedConn.Close()
}
func (w *h2MuxConnWrapper) Upstream() any {
return w.ExtendedConn
}
var _ abstractSession = (*H2MuxClientSession)(nil)
type H2MuxClientSession struct {
transport *http2.Transport
clientConn *http2.ClientConn
done chan struct{}
}
func NewH2MuxClient(conn net.Conn) (*H2MuxClientSession, error) {
session := &H2MuxClientSession{
transport: &http2.Transport{
DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) {
return conn, nil
},
ReadIdleTimeout: idleTimeout,
},
done: make(chan struct{}),
}
session.transport.ConnPool = session
clientConn, err := session.transport.NewClientConn(conn)
if err != nil {
return nil, err
}
session.clientConn = clientConn
return session, nil
}
func (s *H2MuxClientSession) GetClientConn(req *http.Request, addr string) (*http2.ClientConn, error) {
return s.clientConn, nil
}
func (s *H2MuxClientSession) MarkDead(conn *http2.ClientConn) {
s.Close()
}
func (s *H2MuxClientSession) Open() (net.Conn, error) {
pipeInReader, pipeInWriter := io.Pipe()
request := &http.Request{
Method: http.MethodConnect,
Body: pipeInReader,
URL: &url.URL{Scheme: "https", Host: "localhost"},
}
conn := v2rayhttp.NewLateHTTPConn(pipeInWriter)
go func() {
response, err := s.transport.RoundTrip(request)
if err != nil {
conn.Setup(nil, err)
} else if response.StatusCode != 200 {
response.Body.Close()
conn.Setup(nil, E.New("unexpected status: ", response.StatusCode, " ", response.Status))
} else {
conn.Setup(response.Body, nil)
}
}()
return conn, nil
}
func (s *H2MuxClientSession) Accept() (net.Conn, error) {
return nil, os.ErrInvalid
}
func (s *H2MuxClientSession) NumStreams() int {
return s.clientConn.State().StreamsActive
}
func (s *H2MuxClientSession) Close() error {
select {
case <-s.done:
default:
close(s.done)
}
return s.clientConn.Close()
}
func (s *H2MuxClientSession) IsClosed() bool {
select {
case <-s.done:
return true
default:
}
return s.clientConn.State().Closed
}
func (s *H2MuxClientSession) CanTakeNewRequest() bool {
return s.clientConn.CanTakeNewRequest()
}

View File

@@ -1,240 +0,0 @@
package mux
import (
"encoding/binary"
"io"
"math/rand"
"net"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/rw"
)
const kFirstPaddings = 16
type paddingConn struct {
N.ExtendedConn
writer N.VectorisedWriter
readPadding int
writePadding int
readRemaining int
paddingRemaining int
}
func newPaddingConn(conn net.Conn) net.Conn {
writer, isVectorised := bufio.CreateVectorisedWriter(conn)
if isVectorised {
return &vectorisedPaddingConn{
paddingConn{
ExtendedConn: bufio.NewExtendedConn(conn),
writer: bufio.NewVectorisedWriter(conn),
},
writer,
}
} else {
return &paddingConn{
ExtendedConn: bufio.NewExtendedConn(conn),
writer: bufio.NewVectorisedWriter(conn),
}
}
}
func (c *paddingConn) Read(p []byte) (n int, err error) {
if c.readRemaining > 0 {
if len(p) > c.readRemaining {
p = p[:c.readRemaining]
}
n, err = c.ExtendedConn.Read(p)
if err != nil {
return
}
c.readRemaining -= n
return
}
if c.paddingRemaining > 0 {
err = rw.SkipN(c.ExtendedConn, c.paddingRemaining)
if err != nil {
return
}
c.paddingRemaining = 0
}
if c.readPadding < kFirstPaddings {
var paddingHdr []byte
if len(p) >= 4 {
paddingHdr = p[:4]
} else {
_paddingHdr := make([]byte, 4)
defer common.KeepAlive(_paddingHdr)
paddingHdr = common.Dup(_paddingHdr)
}
_, err = io.ReadFull(c.ExtendedConn, paddingHdr)
if err != nil {
return
}
originalDataSize := int(binary.BigEndian.Uint16(paddingHdr[:2]))
paddingLen := int(binary.BigEndian.Uint16(paddingHdr[2:]))
if len(p) > originalDataSize {
p = p[:originalDataSize]
}
n, err = c.ExtendedConn.Read(p)
if err != nil {
return
}
c.readPadding++
c.readRemaining = originalDataSize - n
c.paddingRemaining = paddingLen
return
}
return c.ExtendedConn.Read(p)
}
func (c *paddingConn) Write(p []byte) (n int, err error) {
for pLen := len(p); pLen > 0; {
var data []byte
if pLen > 65535 {
data = p[:65535]
p = p[65535:]
pLen -= 65535
} else {
data = p
pLen = 0
}
var writeN int
writeN, err = c.write(data)
n += writeN
if err != nil {
break
}
}
return n, err
}
func (c *paddingConn) write(p []byte) (n int, err error) {
if c.writePadding < kFirstPaddings {
paddingLen := 256 + rand.Intn(512)
_buffer := buf.StackNewSize(4 + len(p) + paddingLen)
defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
defer buffer.Release()
header := buffer.Extend(4)
binary.BigEndian.PutUint16(header[:2], uint16(len(p)))
binary.BigEndian.PutUint16(header[2:], uint16(paddingLen))
common.Must1(buffer.Write(p))
buffer.Extend(paddingLen)
_, err = c.ExtendedConn.Write(buffer.Bytes())
if err == nil {
n = len(p)
}
c.writePadding++
return
}
return c.ExtendedConn.Write(p)
}
func (c *paddingConn) ReadBuffer(buffer *buf.Buffer) error {
p := buffer.FreeBytes()
if c.readRemaining > 0 {
if len(p) > c.readRemaining {
p = p[:c.readRemaining]
}
n, err := c.ExtendedConn.Read(p)
if err != nil {
return err
}
c.readRemaining -= n
buffer.Truncate(n)
return nil
}
if c.paddingRemaining > 0 {
err := rw.SkipN(c.ExtendedConn, c.paddingRemaining)
if err != nil {
return err
}
c.paddingRemaining = 0
}
if c.readPadding < kFirstPaddings {
var paddingHdr []byte
if len(p) >= 4 {
paddingHdr = p[:4]
} else {
_paddingHdr := make([]byte, 4)
defer common.KeepAlive(_paddingHdr)
paddingHdr = common.Dup(_paddingHdr)
}
_, err := io.ReadFull(c.ExtendedConn, paddingHdr)
if err != nil {
return err
}
originalDataSize := int(binary.BigEndian.Uint16(paddingHdr[:2]))
paddingLen := int(binary.BigEndian.Uint16(paddingHdr[2:]))
if len(p) > originalDataSize {
p = p[:originalDataSize]
}
n, err := c.ExtendedConn.Read(p)
if err != nil {
return err
}
c.readPadding++
c.readRemaining = originalDataSize - n
c.paddingRemaining = paddingLen
buffer.Truncate(n)
return nil
}
return c.ExtendedConn.ReadBuffer(buffer)
}
func (c *paddingConn) WriteBuffer(buffer *buf.Buffer) error {
if c.writePadding < kFirstPaddings {
bufferLen := buffer.Len()
if bufferLen > 65535 {
return common.Error(c.Write(buffer.Bytes()))
}
paddingLen := 256 + rand.Intn(512)
header := buffer.ExtendHeader(4)
binary.BigEndian.PutUint16(header[:2], uint16(bufferLen))
binary.BigEndian.PutUint16(header[2:], uint16(paddingLen))
buffer.Extend(paddingLen)
c.writePadding++
}
return c.ExtendedConn.WriteBuffer(buffer)
}
func (c *paddingConn) FrontHeadroom() int {
return 4 + 256 + 1024
}
type vectorisedPaddingConn struct {
paddingConn
writer N.VectorisedWriter
}
func (c *vectorisedPaddingConn) WriteVectorised(buffers []*buf.Buffer) error {
if c.writePadding < kFirstPaddings {
bufferLen := buf.LenMulti(buffers)
if bufferLen > 65535 {
defer buf.ReleaseMulti(buffers)
for _, buffer := range buffers {
_, err := c.Write(buffer.Bytes())
if err != nil {
return err
}
}
return nil
}
paddingLen := 256 + rand.Intn(512)
header := buf.NewSize(4)
common.Must(
binary.Write(header, binary.BigEndian, uint16(bufferLen)),
binary.Write(header, binary.BigEndian, uint16(paddingLen)),
)
c.writePadding++
padding := buf.NewSize(paddingLen)
padding.Extend(paddingLen)
buffers = append(append([]*buf.Buffer{header}, buffers...), padding)
}
return c.writer.WriteVectorised(buffers)
}

View File

@@ -3,7 +3,6 @@ package mux
import ( import (
"encoding/binary" "encoding/binary"
"io" "io"
"math/rand"
"net" "net"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
@@ -26,7 +25,6 @@ var Destination = M.Socksaddr{
const ( const (
ProtocolSMux Protocol = iota ProtocolSMux Protocol = iota
ProtocolYAMux ProtocolYAMux
ProtocolH2Mux
) )
type Protocol byte type Protocol byte
@@ -37,10 +35,8 @@ func ParseProtocol(name string) (Protocol, error) {
return ProtocolSMux, nil return ProtocolSMux, nil
case "yamux": case "yamux":
return ProtocolYAMux, nil return ProtocolYAMux, nil
case "h2mux":
return ProtocolH2Mux, nil
default: default:
return ProtocolSMux, E.New("unknown multiplex protocol: ", name) return ProtocolYAMux, E.New("unknown multiplex protocol: ", name)
} }
} }
@@ -53,13 +49,7 @@ func (p Protocol) newServer(conn net.Conn) (abstractSession, error) {
} }
return &smuxSession{session}, nil return &smuxSession{session}, nil
case ProtocolYAMux: case ProtocolYAMux:
session, err := yamux.Server(conn, yaMuxConfig()) return yamux.Server(conn, yaMuxConfig())
if err != nil {
return nil, err
}
return &yamuxSession{session}, nil
case ProtocolH2Mux:
return NewH2MuxServer(conn), nil
default: default:
panic("unknown protocol") panic("unknown protocol")
} }
@@ -74,13 +64,7 @@ func (p Protocol) newClient(conn net.Conn) (abstractSession, error) {
} }
return &smuxSession{session}, nil return &smuxSession{session}, nil
case ProtocolYAMux: case ProtocolYAMux:
session, err := yamux.Client(conn, yaMuxConfig()) return yamux.Client(conn, yaMuxConfig())
if err != nil {
return nil, err
}
return &yamuxSession{session}, nil
case ProtocolH2Mux:
return NewH2MuxClient(conn)
default: default:
panic("unknown protocol") panic("unknown protocol")
} }
@@ -106,22 +90,17 @@ func (p Protocol) String() string {
return "smux" return "smux"
case ProtocolYAMux: case ProtocolYAMux:
return "yamux" return "yamux"
case ProtocolH2Mux:
return "h2mux"
default: default:
return "unknown" return "unknown"
} }
} }
const ( const (
Version0 = iota version0 = 0
Version1
) )
type Request struct { type Request struct {
Version byte Protocol Protocol
Protocol Protocol
PaddingEnabled bool
} }
func ReadRequest(reader io.Reader) (*Request, error) { func ReadRequest(reader io.Reader) (*Request, error) {
@@ -129,60 +108,22 @@ func ReadRequest(reader io.Reader) (*Request, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if version < Version0 || version > Version1 { if version != version0 {
return nil, E.New("unsupported version: ", version) return nil, E.New("unsupported version: ", version)
} }
protocol, err := rw.ReadByte(reader) protocol, err := rw.ReadByte(reader)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var paddingEnabled bool if protocol > byte(ProtocolYAMux) {
if version == Version1 { return nil, E.New("unsupported protocol: ", protocol)
err = binary.Read(reader, binary.BigEndian, &paddingEnabled)
if err != nil {
return nil, err
}
if paddingEnabled {
var paddingLen uint16
err = binary.Read(reader, binary.BigEndian, &paddingLen)
if err != nil {
return nil, err
}
err = rw.SkipN(reader, int(paddingLen))
if err != nil {
return nil, err
}
}
} }
return &Request{Version: version, Protocol: Protocol(protocol), PaddingEnabled: paddingEnabled}, nil return &Request{Protocol: Protocol(protocol)}, nil
} }
func EncodeRequest(request Request, payload []byte) *buf.Buffer { func EncodeRequest(buffer *buf.Buffer, request Request) {
var requestLen int buffer.WriteByte(version0)
requestLen += 2 buffer.WriteByte(byte(request.Protocol))
var paddingLen uint16
if request.Version == Version1 {
requestLen += 1
if request.PaddingEnabled {
requestLen += 2
paddingLen = uint16(256 + rand.Intn(512))
requestLen += int(paddingLen)
}
}
buffer := buf.NewSize(requestLen + len(payload))
common.Must(
buffer.WriteByte(request.Version),
buffer.WriteByte(byte(request.Protocol)),
)
if request.Version == Version1 {
common.Must(binary.Write(buffer, binary.BigEndian, request.PaddingEnabled))
if request.PaddingEnabled {
common.Must(binary.Write(buffer, binary.BigEndian, paddingLen))
buffer.Extend(int(paddingLen))
}
}
common.Must1(buffer.Write(payload))
return buffer
} }
const ( const (
@@ -219,7 +160,7 @@ func ReadStreamRequest(reader io.Reader) (*StreamRequest, error) {
return &StreamRequest{network, destination, udpAddr}, nil return &StreamRequest{network, destination, udpAddr}, nil
} }
func streamRequestLen(request StreamRequest) int { func requestLen(request StreamRequest) int {
var rLen int var rLen int
rLen += 1 // version rLen += 1 // version
rLen += 2 // flags rLen += 2 // flags

View File

@@ -10,6 +10,7 @@ import (
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio" "github.com/sagernet/sing/common/bufio"
"github.com/sagernet/sing/common/bufio/deadline"
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"
@@ -22,9 +23,6 @@ func NewConnection(ctx context.Context, router adapter.Router, errorHandler E.Ha
if err != nil { if err != nil {
return err return err
} }
if request.PaddingEnabled {
conn = newPaddingConn(conn)
}
session, err := request.Protocol.newServer(conn) session, err := request.Protocol.newServer(conn)
if err != nil { if err != nil {
return err return err
@@ -70,7 +68,7 @@ func newConnection(ctx context.Context, router adapter.Router, errorHandler E.Ha
logger.InfoContext(ctx, "inbound multiplex packet connection") logger.InfoContext(ctx, "inbound multiplex packet connection")
packetConn = &ServerPacketAddrConn{ExtendedConn: bufio.NewExtendedConn(stream)} packetConn = &ServerPacketAddrConn{ExtendedConn: bufio.NewExtendedConn(stream)}
} }
hErr := router.RoutePacketConnection(ctx, packetConn, metadata) hErr := router.RoutePacketConnection(ctx, deadline.NewPacketConn(bufio.NewNetPacketConn(packetConn)), metadata)
stream.Close() stream.Close()
if hErr != nil { if hErr != nil {
errorHandler.NewError(ctx, hErr) errorHandler.NewError(ctx, hErr)
@@ -134,10 +132,6 @@ func (c *ServerConn) FrontHeadroom() int {
return 0 return 0
} }
func (c *ServerConn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *ServerConn) Upstream() any { func (c *ServerConn) Upstream() any {
return c.ExtendedConn return c.ExtendedConn
} }
@@ -190,10 +184,6 @@ func (c *ServerPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksad
return c.ExtendedConn.WriteBuffer(buffer) return c.ExtendedConn.WriteBuffer(buffer)
} }
func (c *ServerPacketConn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *ServerPacketConn) Upstream() any { func (c *ServerPacketConn) Upstream() any {
return c.ExtendedConn return c.ExtendedConn
} }
@@ -256,10 +246,6 @@ func (c *ServerPacketAddrConn) WritePacket(buffer *buf.Buffer, destination M.Soc
return c.ExtendedConn.WriteBuffer(buffer) return c.ExtendedConn.WriteBuffer(buffer)
} }
func (c *ServerPacketAddrConn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *ServerPacketAddrConn) Upstream() any { func (c *ServerPacketAddrConn) Upstream() any {
return c.ExtendedConn return c.ExtendedConn
} }

View File

@@ -4,12 +4,11 @@ import (
"io" "io"
"net" "net"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio" "github.com/sagernet/sing/common/bufio"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/smux" "github.com/sagernet/smux"
"github.com/hashicorp/yamux"
) )
type abstractSession interface { type abstractSession interface {
@@ -18,7 +17,6 @@ type abstractSession interface {
NumStreams() int NumStreams() int
Close() error Close() error
IsClosed() bool IsClosed() bool
CanTakeNewRequest() bool
} }
var _ abstractSession = (*smuxSession)(nil) var _ abstractSession = (*smuxSession)(nil)
@@ -35,49 +33,25 @@ func (s *smuxSession) Accept() (net.Conn, error) {
return s.AcceptStream() return s.AcceptStream()
} }
func (s *smuxSession) CanTakeNewRequest() bool {
return true
}
type yamuxSession struct {
*yamux.Session
}
func (y *yamuxSession) CanTakeNewRequest() bool {
return true
}
type protocolConn struct { type protocolConn struct {
net.Conn net.Conn
request Request protocol Protocol
protocolWritten bool protocolWritten bool
} }
func newProtocolConn(conn net.Conn, request Request) net.Conn {
writer, isVectorised := bufio.CreateVectorisedWriter(conn)
if isVectorised {
return &vectorisedProtocolConn{
protocolConn{
Conn: conn,
request: request,
},
writer,
}
} else {
return &protocolConn{
Conn: conn,
request: request,
}
}
}
func (c *protocolConn) Write(p []byte) (n int, err error) { func (c *protocolConn) Write(p []byte) (n int, err error) {
if c.protocolWritten { if c.protocolWritten {
return c.Conn.Write(p) return c.Conn.Write(p)
} }
buffer := EncodeRequest(c.request, p) _buffer := buf.StackNewSize(2 + len(p))
defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
defer buffer.Release()
EncodeRequest(buffer, Request{
Protocol: c.protocol,
})
common.Must(common.Error(buffer.Write(p)))
n, err = c.Conn.Write(buffer.Bytes()) n, err = c.Conn.Write(buffer.Bytes())
buffer.Release()
if err == nil { if err == nil {
n-- n--
} }
@@ -98,14 +72,20 @@ func (c *protocolConn) Upstream() any {
type vectorisedProtocolConn struct { type vectorisedProtocolConn struct {
protocolConn protocolConn
writer N.VectorisedWriter N.VectorisedWriter
} }
func (c *vectorisedProtocolConn) WriteVectorised(buffers []*buf.Buffer) error { func (c *vectorisedProtocolConn) WriteVectorised(buffers []*buf.Buffer) error {
if c.protocolWritten { if c.protocolWritten {
return c.writer.WriteVectorised(buffers) return c.VectorisedWriter.WriteVectorised(buffers)
} }
c.protocolWritten = true c.protocolWritten = true
buffer := EncodeRequest(c.request, nil) _buffer := buf.StackNewSize(2)
return c.writer.WriteVectorised(append([]*buf.Buffer{buffer}, buffers...)) defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
defer buffer.Release()
EncodeRequest(buffer, Request{
Protocol: c.protocol,
})
return c.VectorisedWriter.WriteVectorised(append([]*buf.Buffer{buffer}, buffers...))
} }

View File

@@ -7,7 +7,6 @@ import (
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/protocol/http" "github.com/sagernet/sing/protocol/http"
) )
@@ -16,5 +15,5 @@ func HTTPHost(ctx context.Context, reader io.Reader) (*adapter.InboundContext, e
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &adapter.InboundContext{Protocol: C.ProtocolHTTP, Domain: M.ParseSocksaddr(request.Host).AddrString()}, nil return &adapter.InboundContext{Protocol: C.ProtocolHTTP, Domain: request.Host}, nil
} }

View File

@@ -1,27 +0,0 @@
package sniff_test
import (
"context"
"strings"
"testing"
"github.com/sagernet/sing-box/common/sniff"
"github.com/stretchr/testify/require"
)
func TestSniffHTTP1(t *testing.T) {
t.Parallel()
pkt := "GET / HTTP/1.1\r\nHost: www.google.com\r\nAccept: */*\r\n\r\n"
metadata, err := sniff.HTTPHost(context.Background(), strings.NewReader(pkt))
require.NoError(t, err)
require.Equal(t, metadata.Domain, "www.google.com")
}
func TestSniffHTTP1WithPort(t *testing.T) {
t.Parallel()
pkt := "GET / HTTP/1.1\r\nHost: www.gov.cn:8080\r\nAccept: */*\r\n\r\n"
metadata, err := sniff.HTTPHost(context.Background(), strings.NewReader(pkt))
require.NoError(t, err)
require.Equal(t, metadata.Domain, "www.gov.cn")
}

View File

@@ -125,7 +125,7 @@ func (e *RealityClientConfig) ClientHandshake(ctx context.Context, conn net.Conn
hello.SessionId[0] = 1 hello.SessionId[0] = 1
hello.SessionId[1] = 8 hello.SessionId[1] = 8
hello.SessionId[2] = 1 hello.SessionId[2] = 0
binary.BigEndian.PutUint32(hello.SessionId[4:], uint32(time.Now().Unix())) binary.BigEndian.PutUint32(hello.SessionId[4:], uint32(time.Now().Unix()))
copy(hello.SessionId[8:], e.shortID[:]) copy(hello.SessionId[8:], e.shortID[:])

31
common/warning/warning.go Normal file
View File

@@ -0,0 +1,31 @@
package warning
import (
"sync"
"github.com/sagernet/sing-box/log"
)
type Warning struct {
logger log.Logger
check CheckFunc
message string
checkOnce sync.Once
}
type CheckFunc = func() bool
func New(checkFunc CheckFunc, message string) Warning {
return Warning{
check: checkFunc,
message: message,
}
}
func (w *Warning) Check() {
w.checkOnce.Do(func() {
if w.check() {
log.Warn(w.message)
}
})
}

View File

@@ -3,13 +3,40 @@ package constant
import ( import (
"os" "os"
"path/filepath" "path/filepath"
"strings"
"github.com/sagernet/sing/common/rw" "github.com/sagernet/sing/common/rw"
) )
const dirName = "sing-box" const dirName = "sing-box"
var resourcePaths []string var (
basePath string
tempPath string
resourcePaths []string
)
func BasePath(name string) string {
if basePath == "" || strings.HasPrefix(name, "/") {
return name
}
return filepath.Join(basePath, name)
}
func CreateTemp(pattern string) (*os.File, error) {
if tempPath == "" {
tempPath = os.TempDir()
}
return os.CreateTemp(tempPath, pattern)
}
func SetBasePath(path string) {
basePath = path
}
func SetTempPath(path string) {
tempPath = path
}
func FindPath(name string) (string, bool) { func FindPath(name string) (string, bool) {
name = os.ExpandEnv(name) name = os.ExpandEnv(name)

View File

@@ -10,7 +10,6 @@ import (
) )
func applyDebugOptions(options option.DebugOptions) { func applyDebugOptions(options option.DebugOptions) {
applyDebugListenOption(options)
if options.GCPercent != nil { if options.GCPercent != nil {
debug.SetGCPercent(*options.GCPercent) debug.SetGCPercent(*options.GCPercent)
} }

View File

@@ -10,7 +10,6 @@ import (
) )
func applyDebugOptions(options option.DebugOptions) { func applyDebugOptions(options option.DebugOptions) {
applyDebugListenOption(options)
if options.GCPercent != nil { if options.GCPercent != nil {
debug.SetGCPercent(*options.GCPercent) debug.SetGCPercent(*options.GCPercent)
} }

View File

@@ -1,67 +0,0 @@
package box
import (
"net/http"
"net/http/pprof"
"runtime"
"runtime/debug"
"github.com/sagernet/sing-box/common/badjson"
"github.com/sagernet/sing-box/common/json"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
"github.com/dustin/go-humanize"
"github.com/go-chi/chi/v5"
)
var debugHTTPServer *http.Server
func applyDebugListenOption(options option.DebugOptions) {
if debugHTTPServer != nil {
debugHTTPServer.Close()
debugHTTPServer = nil
}
if options.Listen == "" {
return
}
r := chi.NewMux()
r.Route("/debug", func(r chi.Router) {
r.Get("/gc", func(writer http.ResponseWriter, request *http.Request) {
writer.WriteHeader(http.StatusNoContent)
go debug.FreeOSMemory()
})
r.Get("/memory", func(writer http.ResponseWriter, request *http.Request) {
var memStats runtime.MemStats
runtime.ReadMemStats(&memStats)
var memObject badjson.JSONObject
memObject.Put("heap", humanize.IBytes(memStats.HeapInuse))
memObject.Put("stack", humanize.IBytes(memStats.StackInuse))
memObject.Put("idle", humanize.IBytes(memStats.HeapIdle-memStats.HeapReleased))
memObject.Put("goroutines", runtime.NumGoroutine())
memObject.Put("rss", rusageMaxRSS())
encoder := json.NewEncoder(writer)
encoder.SetIndent("", " ")
encoder.Encode(memObject)
})
r.HandleFunc("/pprof", pprof.Index)
r.HandleFunc("/pprof/*", pprof.Index)
r.HandleFunc("/pprof/cmdline", pprof.Cmdline)
r.HandleFunc("/pprof/profile", pprof.Profile)
r.HandleFunc("/pprof/symbol", pprof.Symbol)
r.HandleFunc("/pprof/trace", pprof.Trace)
})
debugHTTPServer = &http.Server{
Addr: options.Listen,
Handler: r,
}
go func() {
err := debugHTTPServer.ListenAndServe()
if err != nil && !E.IsClosed(err) {
log.Error(E.Cause(err, "serve debug HTTP server"))
}
}()
}

View File

@@ -1,37 +1,3 @@
#### 1.3-beta9
* Improve multiplex **1**
* Fixes and improvements
*1*:
Added new `h2mux` multiplex protocol and `padding` multiplex option, see [Multiplex](/configuration/shared/multiplex).
#### 1.2.6
* Fix bugs and update dependencies
#### 1.3-beta8
* Fix `system` tun stack for ios
* Fix network monitor for android/ios
* Update VLESS and XUDP protocol **1**
* Fixes and improvements
*1:
This is an incompatible update for XUDP in VLESS if vision flow is enabled.
#### 1.3-beta7
* Add `path` and `headers` options for HTTP outbound
* Add multi-user support for Shadowsocks legacy AEAD inbound
* Fixes and improvements
#### 1.2.4
* Fixes and improvements
#### 1.3-beta6 #### 1.3-beta6
* Fix WireGuard reconnect * Fix WireGuard reconnect

View File

@@ -10,8 +10,7 @@
"protocol": "smux", "protocol": "smux",
"max_connections": 4, "max_connections": 4,
"min_streams": 4, "min_streams": 4,
"max_streams": 0, "max_streams": 0
"padding": false
} }
``` ```
@@ -29,7 +28,6 @@ Multiplex protocol.
|----------|------------------------------------| |----------|------------------------------------|
| smux | https://github.com/xtaci/smux | | smux | https://github.com/xtaci/smux |
| yamux | https://github.com/hashicorp/yamux | | yamux | https://github.com/hashicorp/yamux |
| h2mux | https://golang.org/x/net/http2 |
SMux is used by default. SMux is used by default.
@@ -50,12 +48,3 @@ Conflict with `max_streams`.
Maximum multiplexed streams in a connection before opening a new connection. Maximum multiplexed streams in a connection before opening a new connection.
Conflict with `max_connections` and `min_streams`. Conflict with `max_connections` and `min_streams`.
#### padding
!!! info
Requires sing-box server version 1.3-beta9 or later.
Enable padding.

View File

@@ -28,7 +28,6 @@
|-------|------------------------------------| |-------|------------------------------------|
| smux | https://github.com/xtaci/smux | | smux | https://github.com/xtaci/smux |
| yamux | https://github.com/hashicorp/yamux | | yamux | https://github.com/hashicorp/yamux |
| h2mux | https://golang.org/x/net/http2 |
默认使用 SMux。 默认使用 SMux。
@@ -48,13 +47,4 @@
在打开新连接之前,连接中的最大多路复用流数量。 在打开新连接之前,连接中的最大多路复用流数量。
`max_connections``min_streams` 冲突。 `max_connections``min_streams` 冲突。
#### padding
!!! info
需要 sing-box 服务器版本 1.3-beta9 或更高。
启用填充。

View File

@@ -12,6 +12,5 @@ Experimental Android client for sing-box.
#### Note #### Note
* User Agent in remote profile request is `SFA/$version ($version_code; sing-box $sing_box_version)` * Working directory is at `/sdcard/Android/data/io.nekohasekai.sfa/files` (External files directory)
* The working directory is located at `/sdcard/Android/data/io.nekohasekai.sfa/files` (External files directory) * User Agent is `SFA/$version ($version_code; sing-box $sing_box_version)` in the remote profile request
* Crash logs is located in `$working_directory/stderr.log`

View File

@@ -12,6 +12,5 @@
#### 注意事项 #### 注意事项
* 远程配置文件请求中的 User Agent 为 `SFA/$version ($version_code; sing-box $sing_box_version)`
* 工作目录位于 `/sdcard/Android/data/io.nekohasekai.sfa/files` (外部文件目录) * 工作目录位于 `/sdcard/Android/data/io.nekohasekai.sfa/files` (外部文件目录)
* 崩溃日志位于 `$working_directory/stderr.log` * 远程配置文件请求中的 User Agent 为 `SFA/$version ($version_code; sing-box $sing_box_version)`

View File

@@ -13,8 +13,8 @@ Experimental iOS client for sing-box.
#### Note #### Note
* User Agent in remote profile request is `SFA/$version ($version_code; sing-box $sing_box_version)` * `system` tun stack not working on iOS
* Crash logs is located in `Settings` -> `View Service Log` * User Agent is `SFI/$version ($version_code; sing-box $sing_box_version)` in the remote profile request
#### Privacy policy #### Privacy policy

View File

@@ -13,8 +13,8 @@
#### 注意事项 #### 注意事项
* `system` tun stack 在 iOS 不工作
* 远程配置文件请求中的 User Agent 为 `SFI/$version ($version_code; sing-box $sing_box_version)` * 远程配置文件请求中的 User Agent 为 `SFI/$version ($version_code; sing-box $sing_box_version)`
* 崩溃日志位于 `设置` -> `查看服务日志`
#### 隐私政策 #### 隐私政策

View File

@@ -1,7 +1,6 @@
package experimental package experimental
import ( import (
"context"
"os" "os"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
@@ -9,7 +8,7 @@ import (
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
) )
type ClashServerConstructor = func(ctx context.Context, router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) type ClashServerConstructor = func(router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error)
var clashServerConstructor ClashServerConstructor var clashServerConstructor ClashServerConstructor
@@ -17,9 +16,9 @@ func RegisterClashServerConstructor(constructor ClashServerConstructor) {
clashServerConstructor = constructor clashServerConstructor = constructor
} }
func NewClashServer(ctx context.Context, router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) { func NewClashServer(router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) {
if clashServerConstructor == nil { if clashServerConstructor == nil {
return nil, os.ErrInvalid return nil, os.ErrInvalid
} }
return clashServerConstructor(ctx, router, logFactory, options) return clashServerConstructor(router, logFactory, options)
} }

View File

@@ -23,7 +23,6 @@ import (
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format" F "github.com/sagernet/sing/common/format"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/service/filemanager"
"github.com/sagernet/websocket" "github.com/sagernet/websocket"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
@@ -38,7 +37,6 @@ func init() {
var _ adapter.ClashServer = (*Server)(nil) var _ adapter.ClashServer = (*Server)(nil)
type Server struct { type Server struct {
ctx context.Context
router adapter.Router router adapter.Router
logger log.Logger logger log.Logger
httpServer *http.Server httpServer *http.Server
@@ -55,11 +53,10 @@ type Server struct {
externalUIDownloadDetour string externalUIDownloadDetour string
} }
func NewServer(ctx context.Context, router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) { func NewServer(router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) {
trafficManager := trafficontrol.NewManager() trafficManager := trafficontrol.NewManager()
chiRouter := chi.NewRouter() chiRouter := chi.NewRouter()
server := &Server{ server := &Server{
ctx: ctx,
router: router, router: router,
logger: logFactory.NewLogger("clash-api"), logger: logFactory.NewLogger("clash-api"),
httpServer: &http.Server{ httpServer: &http.Server{
@@ -85,7 +82,7 @@ func NewServer(ctx context.Context, router adapter.Router, logFactory log.Observ
if foundPath, loaded := C.FindPath(cachePath); loaded { if foundPath, loaded := C.FindPath(cachePath); loaded {
cachePath = foundPath cachePath = foundPath
} else { } else {
cachePath = filemanager.BasePath(ctx, cachePath) cachePath = C.BasePath(cachePath)
} }
server.cacheFilePath = cachePath server.cacheFilePath = cachePath
} }
@@ -116,7 +113,7 @@ func NewServer(ctx context.Context, router adapter.Router, logFactory log.Observ
server.setupMetaAPI(r) server.setupMetaAPI(r)
}) })
if options.ExternalUI != "" { if options.ExternalUI != "" {
server.externalUI = filemanager.BasePath(ctx, os.ExpandEnv(options.ExternalUI)) server.externalUI = C.BasePath(os.ExpandEnv(options.ExternalUI))
chiRouter.Group(func(r chi.Router) { chiRouter.Group(func(r chi.Router) {
fs := http.StripPrefix("/ui", http.FileServer(http.Dir(server.externalUI))) fs := http.StripPrefix("/ui", http.FileServer(http.Dir(server.externalUI)))
r.Get("/ui", http.RedirectHandler("/ui/", http.StatusTemporaryRedirect).ServeHTTP) r.Get("/ui", http.RedirectHandler("/ui/", http.StatusTemporaryRedirect).ServeHTTP)

View File

@@ -12,11 +12,11 @@ import (
"time" "time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
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/filemanager"
) )
func (s *Server) checkAndDownloadExternalUI() { func (s *Server) checkAndDownloadExternalUI() {
@@ -79,7 +79,7 @@ func (s *Server) downloadExternalUI() error {
} }
func (s *Server) downloadZIP(name string, body io.Reader, output string) error { func (s *Server) downloadZIP(name string, body io.Reader, output string) error {
tempFile, err := filemanager.CreateTemp(s.ctx, name) tempFile, err := C.CreateTemp(name)
if err != nil { if err != nil {
return err return err
} }
@@ -112,7 +112,7 @@ func (s *Server) downloadZIP(name string, body io.Reader, output string) error {
return err return err
} }
savePath := filepath.Join(saveDirectory, pathElements[len(pathElements)-1]) savePath := filepath.Join(saveDirectory, pathElements[len(pathElements)-1])
err = downloadZIPEntry(s.ctx, file, savePath) err = downloadZIPEntry(file, savePath)
if err != nil { if err != nil {
return err return err
} }
@@ -120,8 +120,8 @@ func (s *Server) downloadZIP(name string, body io.Reader, output string) error {
return nil return nil
} }
func downloadZIPEntry(ctx context.Context, zipFile *zip.File, savePath string) error { func downloadZIPEntry(zipFile *zip.File, savePath string) error {
saveFile, err := filemanager.Create(ctx, savePath) saveFile, err := os.Create(savePath)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -29,19 +29,3 @@ func (i *iterator[T]) Next() T {
func (i *iterator[T]) HasNext() bool { func (i *iterator[T]) HasNext() bool {
return len(i.values) > 0 return len(i.values) > 0
} }
type abstractIterator[T any] interface {
Next() T
HasNext() bool
}
func iteratorToArray[T any](iterator abstractIterator[T]) []T {
if iterator == nil {
return nil
}
var values []T
for iterator.HasNext() {
values = append(values, iterator.Next())
}
return values
}

View File

@@ -27,27 +27,3 @@ func RedirectStderr(path string) error {
stderrFile = outputFile stderrFile = outputFile
return nil return nil
} }
func RedirectStderrAsUser(path string, uid, gid int) error {
if stats, err := os.Stat(path); err == nil && stats.Size() > 0 {
_ = os.Rename(path, path+".old")
}
outputFile, err := os.Create(path)
if err != nil {
return err
}
err = outputFile.Chown(uid, gid)
if err != nil {
outputFile.Close()
os.Remove(outputFile.Name())
return err
}
err = unix.Dup2(int(outputFile.Fd()), int(os.Stderr.Fd()))
if err != nil {
outputFile.Close()
os.Remove(outputFile.Name())
return err
}
stderrFile = outputFile
return nil
}

View File

@@ -1,183 +0,0 @@
package libbox
import (
"context"
"net"
"net/netip"
"sync"
"github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/common/x/list"
)
var (
_ tun.DefaultInterfaceMonitor = (*platformDefaultInterfaceMonitor)(nil)
_ InterfaceUpdateListener = (*platformDefaultInterfaceMonitor)(nil)
)
type platformDefaultInterfaceMonitor struct {
*platformInterfaceWrapper
errorHandler E.Handler
networkAddresses []networkAddress
defaultInterfaceName string
defaultInterfaceIndex int
element *list.Element[tun.NetworkUpdateCallback]
access sync.Mutex
callbacks list.List[tun.DefaultInterfaceUpdateCallback]
}
type networkAddress struct {
interfaceName string
interfaceIndex int
addresses []netip.Prefix
}
func (m *platformDefaultInterfaceMonitor) Start() error {
return m.iif.StartDefaultInterfaceMonitor(m)
}
func (m *platformDefaultInterfaceMonitor) Close() error {
return m.iif.CloseDefaultInterfaceMonitor(m)
}
func (m *platformDefaultInterfaceMonitor) DefaultInterfaceName(destination netip.Addr) string {
for _, address := range m.networkAddresses {
for _, prefix := range address.addresses {
if prefix.Contains(destination) {
return address.interfaceName
}
}
}
return m.defaultInterfaceName
}
func (m *platformDefaultInterfaceMonitor) DefaultInterfaceIndex(destination netip.Addr) int {
for _, address := range m.networkAddresses {
for _, prefix := range address.addresses {
if prefix.Contains(destination) {
return address.interfaceIndex
}
}
}
return m.defaultInterfaceIndex
}
func (m *platformDefaultInterfaceMonitor) OverrideAndroidVPN() bool {
return false
}
func (m *platformDefaultInterfaceMonitor) AndroidVPNEnabled() bool {
return false
}
func (m *platformDefaultInterfaceMonitor) RegisterCallback(callback tun.DefaultInterfaceUpdateCallback) *list.Element[tun.DefaultInterfaceUpdateCallback] {
m.access.Lock()
defer m.access.Unlock()
return m.callbacks.PushBack(callback)
}
func (m *platformDefaultInterfaceMonitor) UnregisterCallback(element *list.Element[tun.DefaultInterfaceUpdateCallback]) {
m.access.Lock()
defer m.access.Unlock()
m.callbacks.Remove(element)
}
func (m *platformDefaultInterfaceMonitor) UpdateDefaultInterface(interfaceName string, interfaceIndex32 int32) {
var err error
if m.iif.UsePlatformInterfaceGetter() {
err = m.updateInterfacesPlatform()
} else {
err = m.updateInterfaces()
}
if err == nil {
err = m.router.UpdateInterfaces()
}
if err != nil {
m.errorHandler.NewError(context.Background(), E.Cause(err, "update interfaces"))
}
interfaceIndex := int(interfaceIndex32)
if interfaceName == "" {
for _, netIf := range m.networkAddresses {
if netIf.interfaceIndex == interfaceIndex {
interfaceName = netIf.interfaceName
break
}
}
} else if interfaceIndex == -1 {
for _, netIf := range m.networkAddresses {
if netIf.interfaceName == interfaceName {
interfaceIndex = netIf.interfaceIndex
break
}
}
}
if interfaceName == "" {
m.errorHandler.NewError(context.Background(), E.New("invalid interface name for ", interfaceIndex))
return
} else if interfaceIndex == -1 {
m.errorHandler.NewError(context.Background(), E.New("invalid interface index for ", interfaceName))
return
}
if m.defaultInterfaceName == interfaceName && m.defaultInterfaceIndex == interfaceIndex {
return
}
m.defaultInterfaceName = interfaceName
m.defaultInterfaceIndex = interfaceIndex
m.access.Lock()
callbacks := m.callbacks.Array()
m.access.Unlock()
for _, callback := range callbacks {
err = callback(tun.EventInterfaceUpdate)
if err != nil {
m.errorHandler.NewError(context.Background(), err)
}
}
}
func (m *platformDefaultInterfaceMonitor) updateInterfaces() error {
interfaces, err := net.Interfaces()
if err != nil {
return err
}
var addresses []networkAddress
for _, iif := range interfaces {
var netAddresses []net.Addr
netAddresses, err = iif.Addrs()
if err != nil {
return err
}
var address networkAddress
address.interfaceName = iif.Name
address.interfaceIndex = iif.Index
address.addresses = common.Map(common.FilterIsInstance(netAddresses, func(it net.Addr) (*net.IPNet, bool) {
value, loaded := it.(*net.IPNet)
return value, loaded
}), func(it *net.IPNet) netip.Prefix {
bits, _ := it.Mask.Size()
return netip.PrefixFrom(M.AddrFromIP(it.IP), bits)
})
addresses = append(addresses, address)
}
m.networkAddresses = addresses
return nil
}
func (m *platformDefaultInterfaceMonitor) updateInterfacesPlatform() error {
interfaces, err := m.Interfaces()
if err != nil {
return err
}
var addresses []networkAddress
for _, iif := range interfaces {
var address networkAddress
address.interfaceName = iif.Name
address.interfaceIndex = iif.Index
// address.addresses = common.Map(iif.Addresses, netip.MustParsePrefix)
addresses = append(addresses, address)
}
m.networkAddresses = addresses
return nil
}

View File

@@ -1,11 +1,8 @@
package libbox package libbox
import ( import "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/option"
)
type PlatformInterface interface { type PlatformInterface interface {
UsePlatformAutoDetectInterfaceControl() bool
AutoDetectInterfaceControl(fd int32) error AutoDetectInterfaceControl(fd int32) error
OpenTun(options TunOptions) (int32, error) OpenTun(options TunOptions) (int32, error)
WriteLog(message string) WriteLog(message string)
@@ -13,11 +10,6 @@ type PlatformInterface interface {
FindConnectionOwner(ipProtocol int32, sourceAddress string, sourcePort int32, destinationAddress string, destinationPort int32) (int32, error) FindConnectionOwner(ipProtocol int32, sourceAddress string, sourcePort int32, destinationAddress string, destinationPort int32) (int32, error)
PackageNameByUid(uid int32) (string, error) PackageNameByUid(uid int32) (string, error)
UIDByPackageName(packageName string) (int32, error) UIDByPackageName(packageName string) (int32, error)
UsePlatformDefaultInterfaceMonitor() bool
StartDefaultInterfaceMonitor(listener InterfaceUpdateListener) error
CloseDefaultInterfaceMonitor(listener InterfaceUpdateListener) error
UsePlatformInterfaceGetter() bool
GetInterfaces() (NetworkInterfaceIterator, error)
} }
type TunInterface interface { type TunInterface interface {
@@ -25,19 +17,8 @@ type TunInterface interface {
Close() error Close() error
} }
type InterfaceUpdateListener interface { type OnDemandRuleIterator interface {
UpdateDefaultInterface(interfaceName string, interfaceIndex int32) Next() OnDemandRule
}
type NetworkInterface struct {
Index int32
MTU int32
Name string
Addresses StringIterator
}
type NetworkInterfaceIterator interface {
Next() *NetworkInterface
HasNext() bool HasNext() bool
} }
@@ -50,11 +31,6 @@ type OnDemandRule interface {
ProbeURL() string ProbeURL() string
} }
type OnDemandRuleIterator interface {
Next() OnDemandRule
HasNext() bool
}
type onDemandRule struct { type onDemandRule struct {
option.OnDemandRule option.OnDemandRule
} }

View File

@@ -1,34 +1,17 @@
package platform package platform
import ( import (
"context"
"io" "io"
"net/netip"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/process" "github.com/sagernet/sing-box/common/process"
"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/control" "github.com/sagernet/sing/common/control"
E "github.com/sagernet/sing/common/exceptions"
) )
type Interface interface { type Interface interface {
Initialize(ctx context.Context, router adapter.Router) error
UsePlatformAutoDetectInterfaceControl() bool
AutoDetectInterfaceControl() control.Func AutoDetectInterfaceControl() control.Func
OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error) OpenTun(options tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error)
UsePlatformDefaultInterfaceMonitor() bool
CreateDefaultInterfaceMonitor(errorHandler E.Handler) tun.DefaultInterfaceMonitor
UsePlatformInterfaceGetter() bool
Interfaces() ([]NetworkInterface, error)
process.Searcher process.Searcher
io.Writer io.Writer
} }
type NetworkInterface struct {
Index int
MTU int
Name string
Addresses []netip.Prefix
}

View File

@@ -6,17 +6,14 @@ import (
"syscall" "syscall"
"github.com/sagernet/sing-box" "github.com/sagernet/sing-box"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/process" "github.com/sagernet/sing-box/common/process"
"github.com/sagernet/sing-box/experimental/libbox/internal/procfs" "github.com/sagernet/sing-box/experimental/libbox/internal/procfs"
"github.com/sagernet/sing-box/experimental/libbox/platform" "github.com/sagernet/sing-box/experimental/libbox/platform"
"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/control" "github.com/sagernet/sing/common/control"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/service/filemanager"
) )
type BoxService struct { type BoxService struct {
@@ -31,11 +28,10 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box
return nil, err return nil, err
} }
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
ctx = filemanager.WithDefault(ctx, sBasePath, sTempPath, sUserID, sGroupID)
instance, err := box.New(box.Options{ instance, err := box.New(box.Options{
Context: ctx, Context: ctx,
Options: options, Options: options,
PlatformInterface: &platformInterfaceWrapper{iif: platformInterface, useProcFS: platformInterface.UseProcFS()}, PlatformInterface: &platformInterfaceWrapper{platformInterface, platformInterface.UseProcFS()},
}) })
if err != nil { if err != nil {
cancel() cancel()
@@ -62,16 +58,6 @@ var _ platform.Interface = (*platformInterfaceWrapper)(nil)
type platformInterfaceWrapper struct { type platformInterfaceWrapper struct {
iif PlatformInterface iif PlatformInterface
useProcFS bool useProcFS bool
router adapter.Router
}
func (w *platformInterfaceWrapper) Initialize(ctx context.Context, router adapter.Router) error {
w.router = router
return nil
}
func (w *platformInterfaceWrapper) UsePlatformAutoDetectInterfaceControl() bool {
return w.iif.UsePlatformAutoDetectInterfaceControl()
} }
func (w *platformInterfaceWrapper) AutoDetectInterfaceControl() control.Func { func (w *platformInterfaceWrapper) AutoDetectInterfaceControl() control.Func {
@@ -82,7 +68,7 @@ func (w *platformInterfaceWrapper) AutoDetectInterfaceControl() control.Func {
} }
} }
func (w *platformInterfaceWrapper) OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error) { func (w *platformInterfaceWrapper) OpenTun(options tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error) {
if len(options.IncludeUID) > 0 || len(options.ExcludeUID) > 0 { if len(options.IncludeUID) > 0 || len(options.ExcludeUID) > 0 {
return nil, E.New("android: unsupported uid options") return nil, E.New("android: unsupported uid options")
} }
@@ -93,16 +79,12 @@ func (w *platformInterfaceWrapper) OpenTun(options *tun.Options, platformOptions
if err != nil { if err != nil {
return nil, err return nil, err
} }
options.Name, err = getTunnelName(tunFd)
if err != nil {
return nil, E.Cause(err, "query tun name")
}
dupFd, err := dup(int(tunFd)) dupFd, err := dup(int(tunFd))
if err != nil { if err != nil {
return nil, E.Cause(err, "dup tun file descriptor") return nil, E.Cause(err, "dup tun file descriptor")
} }
options.FileDescriptor = dupFd options.FileDescriptor = dupFd
return tun.New(*options) return tun.New(options)
} }
func (w *platformInterfaceWrapper) Write(p []byte) (n int, err error) { func (w *platformInterfaceWrapper) Write(p []byte) (n int, err error) {
@@ -136,36 +118,3 @@ func (w *platformInterfaceWrapper) FindProcessInfo(ctx context.Context, network
packageName, _ := w.iif.PackageNameByUid(uid) packageName, _ := w.iif.PackageNameByUid(uid)
return &process.Info{UserId: uid, PackageName: packageName}, nil return &process.Info{UserId: uid, PackageName: packageName}, nil
} }
func (w *platformInterfaceWrapper) UsePlatformDefaultInterfaceMonitor() bool {
return w.iif.UsePlatformDefaultInterfaceMonitor()
}
func (w *platformInterfaceWrapper) CreateDefaultInterfaceMonitor(errorHandler E.Handler) tun.DefaultInterfaceMonitor {
return &platformDefaultInterfaceMonitor{
platformInterfaceWrapper: w,
errorHandler: errorHandler,
defaultInterfaceIndex: -1,
}
}
func (w *platformInterfaceWrapper) UsePlatformInterfaceGetter() bool {
return w.iif.UsePlatformInterfaceGetter()
}
func (w *platformInterfaceWrapper) Interfaces() ([]platform.NetworkInterface, error) {
interfaceIterator, err := w.iif.GetInterfaces()
if err != nil {
return nil, err
}
var interfaces []platform.NetworkInterface
for _, netInterface := range iteratorToArray[*NetworkInterface](interfaceIterator) {
interfaces = append(interfaces, platform.NetworkInterface{
Index: int(netInterface.Index),
MTU: int(netInterface.MTU),
Name: netInterface.Name,
Addresses: common.Map(iteratorToArray[string](netInterface.Addresses), netip.MustParsePrefix),
})
}
return interfaces, nil
}

View File

@@ -1,31 +1,17 @@
package libbox package libbox
import ( import (
"os"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/dustin/go-humanize" "github.com/dustin/go-humanize"
) )
var ( func SetBasePath(path string) {
sBasePath string C.SetBasePath(path)
sTempPath string }
sUserID int
sGroupID int
)
func Setup(basePath string, tempPath string, userID int, groupID int) { func SetTempPath(path string) {
sBasePath = basePath C.SetTempPath(path)
sTempPath = tempPath
sUserID = userID
sGroupID = groupID
if sUserID == -1 {
sUserID = os.Getuid()
}
if sGroupID == -1 {
sGroupID = os.Getgid()
}
} }
func Version() string { func Version() string {

View File

@@ -59,7 +59,7 @@ func mapRoutePrefix(prefixes []netip.Prefix) RoutePrefixIterator {
var _ TunOptions = (*tunOptions)(nil) var _ TunOptions = (*tunOptions)(nil)
type tunOptions struct { type tunOptions struct {
*tun.Options tun.Options
option.TunPlatformOptions option.TunPlatformOptions
} }

View File

@@ -1,11 +0,0 @@
package libbox
import "golang.org/x/sys/unix"
func getTunnelName(fd int32) (string, error) {
return unix.GetsockoptString(
int(fd),
2, /* #define SYSPROTO_CONTROL 2 */
2, /* #define UTUN_OPT_IFNAME 2 */
)
}

View File

@@ -1,26 +0,0 @@
package libbox
import (
"fmt"
"syscall"
"unsafe"
"golang.org/x/sys/unix"
)
const ifReqSize = unix.IFNAMSIZ + 64
func getTunnelName(fd int32) (string, error) {
var ifr [ifReqSize]byte
var errno syscall.Errno
_, _, errno = unix.Syscall(
unix.SYS_IOCTL,
uintptr(fd),
uintptr(unix.TUNGETIFF),
uintptr(unsafe.Pointer(&ifr[0])),
)
if errno != 0 {
return "", fmt.Errorf("failed to get name of TUN device: %w", errno)
}
return unix.ByteSliceToString(ifr[:]), nil
}

View File

@@ -1,9 +0,0 @@
//go:build !(darwin || linux)
package libbox
import "os"
func getTunnelName(fd int32) (string, error) {
return "", os.ErrInvalid
}

18
go.mod
View File

@@ -25,17 +25,17 @@ require (
github.com/sagernet/gomobile v0.0.0-20230413023804-244d7ff07035 github.com/sagernet/gomobile v0.0.0-20230413023804-244d7ff07035
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
github.com/sagernet/sing v0.2.4 github.com/sagernet/sing v0.2.3-0.20230413105021-520dd58fb01d
github.com/sagernet/sing-dns v0.1.5-0.20230415085626-111ecf799dfc github.com/sagernet/sing-dns v0.1.5-0.20230408004833-5adaf486d440
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230417102954-f77257340507 github.com/sagernet/sing-shadowsocks v0.2.1-0.20230412123110-1a7c32b4e2e7
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b github.com/sagernet/sing-shadowtls v0.1.1-0.20230409094821-9abef019436f
github.com/sagernet/sing-tun v0.1.5-0.20230422121432-209ec123ca7b github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 github.com/sagernet/sing-vmess v0.1.4-0.20230412122845-9470e68f5e45
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e
github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77 github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c
github.com/spf13/cobra v1.7.0 github.com/spf13/cobra v1.7.0
github.com/stretchr/testify v1.8.2 github.com/stretchr/testify v1.8.2
go.etcd.io/bbolt v1.3.7 go.etcd.io/bbolt v1.3.7
@@ -48,7 +48,7 @@ require (
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230215201556-9c5414ab4bde golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230215201556-9c5414ab4bde
google.golang.org/grpc v1.54.0 google.golang.org/grpc v1.54.0
google.golang.org/protobuf v1.30.0 google.golang.org/protobuf v1.30.0
gvisor.dev/gvisor v0.0.0-20230415003630-3981d5d5e523 gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c
) )
//replace github.com/sagernet/sing => ../sing //replace github.com/sagernet/sing => ../sing
@@ -85,7 +85,7 @@ require (
go.uber.org/multierr v1.6.0 // indirect go.uber.org/multierr v1.6.0 // indirect
golang.org/x/mod v0.8.0 // indirect golang.org/x/mod v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect golang.org/x/text v0.9.0 // indirect
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
golang.org/x/tools v0.6.0 // indirect golang.org/x/tools v0.6.0 // indirect
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect

36
go.sum
View File

@@ -111,18 +111,18 @@ github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byL
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.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
github.com/sagernet/sing v0.2.4 h1:gC8BR5sglbJZX23RtMyFa8EETP9YEUADhfbEzU1yVbo= github.com/sagernet/sing v0.2.3-0.20230413105021-520dd58fb01d h1:+hp4necjzOvjg9dQwqmTfH/jAgCCnguVNQzIRUOpO00=
github.com/sagernet/sing v0.2.4/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= github.com/sagernet/sing v0.2.3-0.20230413105021-520dd58fb01d/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
github.com/sagernet/sing-dns v0.1.5-0.20230415085626-111ecf799dfc h1:hmbuqKv48SAjiKPoqtJGvS5pEHVPZjTHq9CPwQY2cZ4= github.com/sagernet/sing-dns v0.1.5-0.20230408004833-5adaf486d440 h1:VH8/BcOVuApHtS+vKP+khxlGRcXH7KKhgkTDtNynqSQ=
github.com/sagernet/sing-dns v0.1.5-0.20230415085626-111ecf799dfc/go.mod h1:ZKuuqgsHRxDahYrzgSgy4vIAGGuKPlIf4hLcNzYzLkY= github.com/sagernet/sing-dns v0.1.5-0.20230408004833-5adaf486d440/go.mod h1:69PNSHyEmXdjf6C+bXBOdr2GQnPeEyWjIzo/MV8gmz8=
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230417102954-f77257340507 h1:bAHZCdWqJkb8LEW98+YsMVDXGRMUVjka8IC+St6ot88= github.com/sagernet/sing-shadowsocks v0.2.1-0.20230412123110-1a7c32b4e2e7 h1:3WDMIF1aE/twc5gJ+9PF2ZJqUxwZ80MPtNBKE3yBevU=
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230417102954-f77257340507/go.mod h1:UJjvQGw0lyYaDGIDvUraL16fwaAEH1WFw1Y6sUcMPog= github.com/sagernet/sing-shadowsocks v0.2.1-0.20230412123110-1a7c32b4e2e7/go.mod h1:WoVjGUvRqsx5yhYeDAB5CijCHpNDi0LUPHl3cf7u8Lc=
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b h1:ouW/6IDCrxkBe19YSbdCd7buHix7b+UZ6BM4Zz74XF4= github.com/sagernet/sing-shadowtls v0.1.1-0.20230409094821-9abef019436f h1:qzQvpcDm60zPW8UlZa8UEaBoFORFeGAnhDncPc3VWT4=
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b/go.mod h1:oG8bPerYI6cZ74KquY3DvA7ynECyrILPBnce6wtBqeI= github.com/sagernet/sing-shadowtls v0.1.1-0.20230409094821-9abef019436f/go.mod h1:MxB+Q9H0pAHcrlvNmwSs1crljRwHFFVhtXyOMBy44Nw=
github.com/sagernet/sing-tun v0.1.5-0.20230422121432-209ec123ca7b h1:9NsciSJGwzdkXwVvT2c2g+RvkTVkANeBLr2l+soJ7LM= github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab h1:a9oeWuPBuIZ70qMhIIH6RrYhp886xN9jJIwsuu4ZFUo=
github.com/sagernet/sing-tun v0.1.5-0.20230422121432-209ec123ca7b/go.mod h1:DD7Ce2Gt0GFc6I/1+Uw4D/aUlBsGqrQsC52CMK/V818= github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab/go.mod h1:4YxIDEkkCjGXDOTMPw1SXpLmCQUFAWuaQN250oo+928=
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 h1:BHOnxrbC929JonuKqFdJ7ZbDp7zs4oTlH5KFvKtWu9U= github.com/sagernet/sing-vmess v0.1.4-0.20230412122845-9470e68f5e45 h1:QqYhWah3u+o2tvLRuTfEu3BwsGpf/wNnVK/VNQV2YBM=
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3/go.mod h1:yKrAr+dqZd64DxBXCHWrYicp+n4qbqO73mtwv3dck8U= github.com/sagernet/sing-vmess v0.1.4-0.20230412122845-9470e68f5e45/go.mod h1:eULig3LgaeNiWSquSlzXF42Joypsj3fO1W+Qy93o6hk=
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as= github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as=
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37/go.mod h1:3skNSftZDJWTGVtVaM2jfbce8qHnmH/AGDRe62iNOg0= github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37/go.mod h1:3skNSftZDJWTGVtVaM2jfbce8qHnmH/AGDRe62iNOg0=
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE= github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE=
@@ -131,8 +131,8 @@ github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 h1:kDUqhc9Vsk5HJuhfI
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/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs= github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs=
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e/go.mod h1:45TUl8+gH4SIKr4ykREbxKWTxkDlSzFENzctB1dVRRY= github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e/go.mod h1:45TUl8+gH4SIKr4ykREbxKWTxkDlSzFENzctB1dVRRY=
github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77 h1:g6QtRWQ2dKX7EQP++1JLNtw4C2TNxd4/ov8YUpOPOSo= github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo=
github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77/go.mod h1:pJDdXzZIwJ+2vmnT0TKzmf8meeum+e2mTDSehw79eE0= github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI=
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
@@ -208,8 +208,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.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
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.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
@@ -236,7 +236,7 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gvisor.dev/gvisor v0.0.0-20230415003630-3981d5d5e523 h1:zUQYeyyPLnSR6yMvLSOmLH37xDWCZ7BqlpE69fE5K3Q= gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c h1:m5lcgWnL3OElQNVyp3qcncItJ2c0sQlSGjYK2+nJTA4=
gvisor.dev/gvisor v0.0.0-20230415003630-3981d5d5e523/go.mod h1:pzr6sy8gDLfVmDAg8OYrlKvGEHw5C3PGTiBXBTCx76Q= gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0= lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0=
lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=

View File

@@ -609,10 +609,6 @@ func (c *naiveH2Conn) SetWriteDeadline(t time.Time) error {
return os.ErrInvalid return os.ErrInvalid
} }
func (c *naiveH2Conn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *naiveH2Conn) UpstreamReader() any { func (c *naiveH2Conn) UpstreamReader() any {
return c.reader return c.reader
} }

View File

@@ -9,8 +9,6 @@ import (
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"
"github.com/sagernet/sing-shadowsocks"
"github.com/sagernet/sing-shadowsocks/shadowaead"
"github.com/sagernet/sing-shadowsocks/shadowaead_2022" "github.com/sagernet/sing-shadowsocks/shadowaead_2022"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/auth" "github.com/sagernet/sing/common/auth"
@@ -27,7 +25,7 @@ var (
type ShadowsocksMulti struct { type ShadowsocksMulti struct {
myInboundAdapter myInboundAdapter
service shadowsocks.MultiService[int] service *shadowaead_2022.MultiService[int]
users []option.ShadowsocksUser users []option.ShadowsocksUser
} }
@@ -51,26 +49,16 @@ func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.
} else { } else {
udpTimeout = int64(C.UDPTimeout.Seconds()) udpTimeout = int64(C.UDPTimeout.Seconds())
} }
var ( if !common.Contains(shadowaead_2022.List, options.Method) {
service shadowsocks.MultiService[int]
err error
)
if common.Contains(shadowaead_2022.List, options.Method) {
service, err = shadowaead_2022.NewMultiServiceWithPassword[int](
options.Method,
options.Password,
udpTimeout,
adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound),
router.TimeFunc(),
)
} else if common.Contains(shadowaead.List, options.Method) {
service, err = shadowaead.NewMultiService[int](
options.Method,
udpTimeout,
adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound))
} else {
return nil, E.New("unsupported method: " + options.Method) return nil, E.New("unsupported method: " + options.Method)
} }
service, err := shadowaead_2022.NewMultiServiceWithPassword[int](
options.Method,
options.Password,
udpTimeout,
adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound),
router.TimeFunc(),
)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -152,7 +152,7 @@ func (t *Tun) Start() error {
) )
t.logger.Trace("opening interface") t.logger.Trace("opening interface")
if t.platformInterface != nil { if t.platformInterface != nil {
tunInterface, err = t.platformInterface.OpenTun(&t.tunOptions, t.platformOptions) tunInterface, err = t.platformInterface.OpenTun(t.tunOptions, t.platformOptions)
} else { } else {
tunInterface, err = tun.New(t.tunOptions) tunInterface, err = tun.New(t.tunOptions)
} }
@@ -177,8 +177,7 @@ func (t *Tun) Start() error {
Router: tunRouter, Router: tunRouter,
Handler: t, Handler: t,
Logger: t.logger, Logger: t.logger,
ForwarderBindInterface: t.platformInterface != nil, UnderPlatform: t.platformInterface != nil,
InterfaceFinder: t.router.InterfaceFinder(),
}) })
if err != nil { if err != nil {
return err return err

View File

@@ -16,6 +16,7 @@ import (
"github.com/sagernet/sing-vmess/packetaddr" "github.com/sagernet/sing-vmess/packetaddr"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/auth" "github.com/sagernet/sing/common/auth"
"github.com/sagernet/sing/common/bufio/deadline"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format" F "github.com/sagernet/sing/common/format"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
@@ -171,7 +172,7 @@ func (h *VLESS) newPacketConnection(ctx context.Context, conn N.PacketConn, meta
} }
if metadata.Destination.Fqdn == packetaddr.SeqPacketMagicAddress { if metadata.Destination.Fqdn == packetaddr.SeqPacketMagicAddress {
metadata.Destination = M.Socksaddr{} metadata.Destination = M.Socksaddr{}
conn = packetaddr.NewConn(conn.(vmess.PacketConn), metadata.Destination) conn = deadline.NewPacketConn(packetaddr.NewConn(conn.(vmess.PacketConn), metadata.Destination))
h.logger.InfoContext(ctx, "[", user, "] inbound packet addr connection") h.logger.InfoContext(ctx, "[", user, "] inbound packet addr connection")
} else { } else {
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination) h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)

View File

@@ -15,6 +15,7 @@ import (
"github.com/sagernet/sing-vmess/packetaddr" "github.com/sagernet/sing-vmess/packetaddr"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/auth" "github.com/sagernet/sing/common/auth"
"github.com/sagernet/sing/common/bufio/deadline"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format" F "github.com/sagernet/sing/common/format"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
@@ -179,7 +180,7 @@ func (h *VMess) newPacketConnection(ctx context.Context, conn N.PacketConn, meta
} }
if metadata.Destination.Fqdn == packetaddr.SeqPacketMagicAddress { if metadata.Destination.Fqdn == packetaddr.SeqPacketMagicAddress {
metadata.Destination = M.Socksaddr{} metadata.Destination = M.Socksaddr{}
conn = packetaddr.NewConn(conn.(vmess.PacketConn), metadata.Destination) conn = deadline.NewPacketConn(packetaddr.NewConn(conn.(vmess.PacketConn), metadata.Destination))
h.logger.InfoContext(ctx, "[", user, "] inbound packet addr connection") h.logger.InfoContext(ctx, "[", user, "] inbound packet addr connection")
} else { } else {
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination) h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)

View File

@@ -3,8 +3,6 @@
package include package include
import ( import (
"context"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/experimental" "github.com/sagernet/sing-box/experimental"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
@@ -13,7 +11,7 @@ import (
) )
func init() { func init() {
experimental.RegisterClashServerConstructor(func(ctx context.Context, router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) { experimental.RegisterClashServerConstructor(func(router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) {
return nil, E.New(`clash api is not included in this build, rebuild with -tags with_clash_api`) return nil, E.New(`clash api is not included in this build, rebuild with -tags with_clash_api`)
}) })
} }

View File

@@ -1,15 +1,14 @@
package log package log
import ( import (
"context"
"io" "io"
"os" "os"
"time" "time"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/service/filemanager"
) )
type factoryWithFile struct { type factoryWithFile struct {
@@ -37,7 +36,6 @@ func (f *observableFactoryWithFile) Close() error {
} }
type Options struct { type Options struct {
Context context.Context
Options option.LogOptions Options option.LogOptions
Observable bool Observable bool
DefaultWriter io.Writer DefaultWriter io.Writer
@@ -67,7 +65,7 @@ func New(options Options) (Factory, error) {
logWriter = os.Stdout logWriter = os.Stdout
default: default:
var err error var err error
logFile, err = filemanager.OpenFile(options.Context, logOptions.Output, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644) logFile, err = os.OpenFile(C.BasePath(logOptions.Output), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -7,7 +7,6 @@ import (
) )
type DebugOptions struct { type DebugOptions struct {
Listen string `json:"listen,omitempty"`
GCPercent *int `json:"gc_percent,omitempty"` GCPercent *int `json:"gc_percent,omitempty"`
MaxStack *int `json:"max_stack,omitempty"` MaxStack *int `json:"max_stack,omitempty"`
MaxThreads *int `json:"max_threads,omitempty"` MaxThreads *int `json:"max_threads,omitempty"`

View File

@@ -150,5 +150,4 @@ type MultiplexOptions struct {
MaxConnections int `json:"max_connections,omitempty"` MaxConnections int `json:"max_connections,omitempty"`
MinStreams int `json:"min_streams,omitempty"` MinStreams int `json:"min_streams,omitempty"`
MaxStreams int `json:"max_streams,omitempty"` MaxStreams int `json:"max_streams,omitempty"`
Padding bool `json:"padding,omitempty"`
} }

View File

@@ -4,7 +4,7 @@ type ShadowsocksInboundOptions struct {
ListenOptions ListenOptions
Network NetworkList `json:"network,omitempty"` Network NetworkList `json:"network,omitempty"`
Method string `json:"method"` Method string `json:"method"`
Password string `json:"password,omitempty"` Password string `json:"password"`
Users []ShadowsocksUser `json:"users,omitempty"` Users []ShadowsocksUser `json:"users,omitempty"`
Destinations []ShadowsocksDestination `json:"destinations,omitempty"` Destinations []ShadowsocksDestination `json:"destinations,omitempty"`
} }

View File

@@ -30,7 +30,7 @@ type Shadowsocks struct {
serverAddr M.Socksaddr serverAddr M.Socksaddr
plugin sip003.Plugin plugin sip003.Plugin
uotClient *uot.Client uotClient *uot.Client
multiplexDialer *mux.Client multiplexDialer N.Dialer
} }
func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksOutboundOptions) (*Shadowsocks, error) { func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksOutboundOptions) (*Shadowsocks, error) {
@@ -127,15 +127,8 @@ func (h *Shadowsocks) NewPacketConnection(ctx context.Context, conn N.PacketConn
return NewPacketConnection(ctx, h, conn, metadata) return NewPacketConnection(ctx, h, conn, metadata)
} }
func (h *Shadowsocks) InterfaceUpdated() error {
if h.multiplexDialer != nil {
h.multiplexDialer.Reset()
}
return nil
}
func (h *Shadowsocks) Close() error { func (h *Shadowsocks) Close() error {
return common.Close(common.PtrOrNil(h.multiplexDialer)) return common.Close(h.multiplexDialer)
} }
var _ N.Dialer = (*shadowsocksDialer)(nil) var _ N.Dialer = (*shadowsocksDialer)(nil)

View File

@@ -27,7 +27,7 @@ type Trojan struct {
dialer N.Dialer dialer N.Dialer
serverAddr M.Socksaddr serverAddr M.Socksaddr
key [56]byte key [56]byte
multiplexDialer *mux.Client multiplexDialer N.Dialer
tlsConfig tls.Config tlsConfig tls.Config
transport adapter.V2RayClientTransport transport adapter.V2RayClientTransport
} }
@@ -103,15 +103,8 @@ func (h *Trojan) NewPacketConnection(ctx context.Context, conn N.PacketConn, met
return NewPacketConnection(ctx, h, conn, metadata) return NewPacketConnection(ctx, h, conn, metadata)
} }
func (h *Trojan) InterfaceUpdated() error {
if h.multiplexDialer != nil {
h.multiplexDialer.Reset()
}
return nil
}
func (h *Trojan) Close() error { func (h *Trojan) Close() error {
return common.Close(common.PtrOrNil(h.multiplexDialer), h.transport) return common.Close(h.multiplexDialer, h.transport)
} }
type trojanDialer Trojan type trojanDialer Trojan

View File

@@ -13,7 +13,6 @@ import (
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/atomic"
"github.com/sagernet/sing/common/batch" "github.com/sagernet/sing/common/batch"
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"
@@ -73,8 +72,7 @@ func (s *URLTest) Start() error {
outbounds = append(outbounds, detour) outbounds = append(outbounds, detour)
} }
s.group = NewURLTestGroup(s.ctx, s.router, s.logger, outbounds, s.link, s.interval, s.tolerance) s.group = NewURLTestGroup(s.ctx, s.router, s.logger, outbounds, s.link, s.interval, s.tolerance)
go s.group.CheckOutbounds(false) return s.group.Start()
return nil
} }
func (s *URLTest) Close() error { func (s *URLTest) Close() error {
@@ -96,7 +94,6 @@ func (s *URLTest) URLTest(ctx context.Context, link string) (map[string]uint16,
} }
func (s *URLTest) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { func (s *URLTest) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
s.group.Start()
outbound := s.group.Select(network) outbound := s.group.Select(network)
conn, err := outbound.DialContext(ctx, network, destination) conn, err := outbound.DialContext(ctx, network, destination)
if err == nil { if err == nil {
@@ -108,7 +105,6 @@ func (s *URLTest) DialContext(ctx context.Context, network string, destination M
} }
func (s *URLTest) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { func (s *URLTest) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
s.group.Start()
outbound := s.group.Select(N.NetworkUDP) outbound := s.group.Select(N.NetworkUDP)
conn, err := outbound.ListenPacket(ctx, destination) conn, err := outbound.ListenPacket(ctx, destination)
if err == nil { if err == nil {
@@ -128,7 +124,7 @@ func (s *URLTest) NewPacketConnection(ctx context.Context, conn N.PacketConn, me
} }
func (s *URLTest) InterfaceUpdated() error { func (s *URLTest) InterfaceUpdated() error {
go s.group.CheckOutbounds(true) go s.group.checkOutbounds()
return nil return nil
} }
@@ -141,9 +137,7 @@ type URLTestGroup struct {
interval time.Duration interval time.Duration
tolerance uint16 tolerance uint16
history *urltest.HistoryStorage history *urltest.HistoryStorage
checking atomic.Bool
access sync.Mutex
ticker *time.Ticker ticker *time.Ticker
close chan struct{} close chan struct{}
} }
@@ -174,23 +168,13 @@ func NewURLTestGroup(ctx context.Context, router adapter.Router, logger log.Logg
} }
} }
func (g *URLTestGroup) Start() { func (g *URLTestGroup) Start() error {
if g.ticker != nil {
return
}
g.access.Lock()
defer g.access.Unlock()
if g.ticker != nil {
return
}
g.ticker = time.NewTicker(g.interval) g.ticker = time.NewTicker(g.interval)
go g.loopCheck() go g.loopCheck()
return nil
} }
func (g *URLTestGroup) Close() error { func (g *URLTestGroup) Close() error {
if g.ticker == nil {
return nil
}
g.ticker.Stop() g.ticker.Stop()
close(g.close) close(g.close)
return nil return nil
@@ -250,33 +234,25 @@ func (g *URLTestGroup) Fallback(used adapter.Outbound) []adapter.Outbound {
} }
func (g *URLTestGroup) loopCheck() { func (g *URLTestGroup) loopCheck() {
go g.CheckOutbounds(true) go g.checkOutbounds()
for { for {
select { select {
case <-g.close: case <-g.close:
return return
case <-g.ticker.C: case <-g.ticker.C:
g.CheckOutbounds(false) g.checkOutbounds()
} }
} }
} }
func (g *URLTestGroup) CheckOutbounds(force bool) { func (g *URLTestGroup) checkOutbounds() {
_, _ = g.urlTest(g.ctx, g.link, force) _, _ = g.URLTest(g.ctx, g.link)
} }
func (g *URLTestGroup) URLTest(ctx context.Context, link string) (map[string]uint16, error) { func (g *URLTestGroup) URLTest(ctx context.Context, link string) (map[string]uint16, error) {
return g.urlTest(ctx, link, false)
}
func (g *URLTestGroup) urlTest(ctx context.Context, link string, force bool) (map[string]uint16, error) {
result := make(map[string]uint16)
if g.checking.Swap(true) {
return result, nil
}
defer g.checking.Store(false)
b, _ := batch.New(ctx, batch.WithConcurrencyNum[any](10)) b, _ := batch.New(ctx, batch.WithConcurrencyNum[any](10))
checked := make(map[string]bool) checked := make(map[string]bool)
result := make(map[string]uint16)
var resultAccess sync.Mutex var resultAccess sync.Mutex
for _, detour := range g.outbounds { for _, detour := range g.outbounds {
tag := detour.Tag() tag := detour.Tag()
@@ -285,7 +261,7 @@ func (g *URLTestGroup) urlTest(ctx context.Context, link string, force bool) (ma
continue continue
} }
history := g.history.LoadURLTestHistory(realTag) history := g.history.LoadURLTestHistory(realTag)
if !force && history != nil && time.Now().Sub(history.Time) < g.interval { if history != nil && time.Now().Sub(history.Time) < g.interval {
continue continue
} }
checked[realTag] = true checked[realTag] = true

View File

@@ -27,7 +27,7 @@ type VMess struct {
dialer N.Dialer dialer N.Dialer
client *vmess.Client client *vmess.Client
serverAddr M.Socksaddr serverAddr M.Socksaddr
multiplexDialer *mux.Client multiplexDialer N.Dialer
tlsConfig tls.Config tlsConfig tls.Config
transport adapter.V2RayClientTransport transport adapter.V2RayClientTransport
packetAddr bool packetAddr bool
@@ -97,15 +97,8 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
return outbound, nil return outbound, nil
} }
func (h *VMess) InterfaceUpdated() error {
if h.multiplexDialer != nil {
h.multiplexDialer.Reset()
}
return nil
}
func (h *VMess) Close() error { func (h *VMess) Close() error {
return common.Close(common.PtrOrNil(h.multiplexDialer), h.transport) return common.Close(h.multiplexDialer, h.transport)
} }
func (h *VMess) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { func (h *VMess) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {

View File

@@ -9,7 +9,7 @@ import (
var _ control.InterfaceFinder = (*myInterfaceFinder)(nil) var _ control.InterfaceFinder = (*myInterfaceFinder)(nil)
type myInterfaceFinder struct { type myInterfaceFinder struct {
interfaces []net.Interface ifs []net.Interface
} }
func (f *myInterfaceFinder) update() error { func (f *myInterfaceFinder) update() error {
@@ -17,16 +17,12 @@ func (f *myInterfaceFinder) update() error {
if err != nil { if err != nil {
return err return err
} }
f.interfaces = ifs f.ifs = ifs
return nil return nil
} }
func (f *myInterfaceFinder) updateInterfaces(interfaces []net.Interface) {
f.interfaces = interfaces
}
func (f *myInterfaceFinder) InterfaceIndexByName(name string) (interfaceIndex int, err error) { func (f *myInterfaceFinder) InterfaceIndexByName(name string) (interfaceIndex int, err error) {
for _, netInterface := range f.interfaces { for _, netInterface := range f.ifs {
if netInterface.Name == name { if netInterface.Name == name {
return netInterface.Index, nil return netInterface.Index, nil
} }
@@ -40,7 +36,7 @@ func (f *myInterfaceFinder) InterfaceIndexByName(name string) (interfaceIndex in
} }
func (f *myInterfaceFinder) InterfaceNameByIndex(index int) (interfaceName string, err error) { func (f *myInterfaceFinder) InterfaceNameByIndex(index int) (interfaceName string, err error) {
for _, netInterface := range f.interfaces { for _, netInterface := range f.ifs {
if netInterface.Index == index { if netInterface.Index == index {
return netInterface.Name, nil return netInterface.Name, nil
} }

View File

@@ -12,12 +12,12 @@ import (
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/dialer" "github.com/sagernet/sing-box/common/dialer"
"github.com/sagernet/sing-box/common/dialer/conntrack"
"github.com/sagernet/sing-box/common/geoip" "github.com/sagernet/sing-box/common/geoip"
"github.com/sagernet/sing-box/common/geosite" "github.com/sagernet/sing-box/common/geosite"
"github.com/sagernet/sing-box/common/mux" "github.com/sagernet/sing-box/common/mux"
"github.com/sagernet/sing-box/common/process" "github.com/sagernet/sing-box/common/process"
"github.com/sagernet/sing-box/common/sniff" "github.com/sagernet/sing-box/common/sniff"
"github.com/sagernet/sing-box/common/warning"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/experimental/libbox/platform" "github.com/sagernet/sing-box/experimental/libbox/platform"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
@@ -31,7 +31,6 @@ import (
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio" "github.com/sagernet/sing/common/bufio"
"github.com/sagernet/sing/common/bufio/deadline"
"github.com/sagernet/sing/common/control" "github.com/sagernet/sing/common/control"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format" F "github.com/sagernet/sing/common/format"
@@ -40,6 +39,27 @@ import (
"github.com/sagernet/sing/common/uot" "github.com/sagernet/sing/common/uot"
) )
var warnDefaultInterfaceOnUnsupportedPlatform = warning.New(
func() bool {
return !(C.IsLinux || C.IsWindows || C.IsDarwin)
},
"route option `default_mark` is only supported on Linux and Windows",
)
var warnDefaultMarkOnNonLinux = warning.New(
func() bool {
return !C.IsLinux
},
"route option `default_mark` is only supported on Linux",
)
var warnFindProcessOnUnsupportedPlatform = warning.New(
func() bool {
return !(C.IsLinux || C.IsWindows || C.IsDarwin)
},
"route option `find_process` is only supported on Linux, Windows, and macOS",
)
var _ adapter.Router = (*Router)(nil) var _ adapter.Router = (*Router)(nil)
type Router struct { type Router struct {
@@ -93,6 +113,16 @@ func NewRouter(
inbounds []option.Inbound, inbounds []option.Inbound,
platformInterface platform.Interface, platformInterface platform.Interface,
) (*Router, error) { ) (*Router, error) {
if options.DefaultInterface != "" {
warnDefaultInterfaceOnUnsupportedPlatform.Check()
}
if options.DefaultMark != 0 {
warnDefaultMarkOnNonLinux.Check()
}
if options.FindProcess {
warnFindProcessOnUnsupportedPlatform.Check()
}
router := &Router{ router := &Router{
ctx: ctx, ctx: ctx,
logger: logFactory.NewLogger("router"), logger: logFactory.NewLogger("router"),
@@ -259,36 +289,29 @@ func NewRouter(
router.fakeIPStore = fakeip.NewStore(router, inet4Range, inet6Range) router.fakeIPStore = fakeip.NewStore(router, inet4Range, inet6Range)
} }
usePlatformDefaultInterfaceMonitor := platformInterface != nil && platformInterface.UsePlatformDefaultInterfaceMonitor() needInterfaceMonitor := platformInterface == nil && (options.AutoDetectInterface || common.Any(inbounds, func(inbound option.Inbound) bool {
needInterfaceMonitor := options.AutoDetectInterface || common.Any(inbounds, func(inbound option.Inbound) bool {
return inbound.HTTPOptions.SetSystemProxy || inbound.MixedOptions.SetSystemProxy || inbound.TunOptions.AutoRoute return inbound.HTTPOptions.SetSystemProxy || inbound.MixedOptions.SetSystemProxy || inbound.TunOptions.AutoRoute
}) }))
if needInterfaceMonitor { if needInterfaceMonitor {
if !usePlatformDefaultInterfaceMonitor { networkMonitor, err := tun.NewNetworkUpdateMonitor(router)
networkMonitor, err := tun.NewNetworkUpdateMonitor(router) if err == nil {
if err != os.ErrInvalid { router.networkMonitor = networkMonitor
if err != nil { networkMonitor.RegisterCallback(router.interfaceFinder.update)
return nil, err
}
router.networkMonitor = networkMonitor
networkMonitor.RegisterCallback(router.interfaceFinder.update)
interfaceMonitor, err := tun.NewDefaultInterfaceMonitor(router.networkMonitor, tun.DefaultInterfaceMonitorOptions{
OverrideAndroidVPN: options.OverrideAndroidVPN,
})
if err != nil {
return nil, E.New("auto_detect_interface unsupported on current platform")
}
interfaceMonitor.RegisterCallback(router.notifyNetworkUpdate)
router.interfaceMonitor = interfaceMonitor
}
} else {
interfaceMonitor := platformInterface.CreateDefaultInterfaceMonitor(router)
interfaceMonitor.RegisterCallback(router.notifyNetworkUpdate)
router.interfaceMonitor = interfaceMonitor
} }
} }
if router.networkMonitor != nil && needInterfaceMonitor {
interfaceMonitor, err := tun.NewDefaultInterfaceMonitor(router.networkMonitor, tun.DefaultInterfaceMonitorOptions{
OverrideAndroidVPN: options.OverrideAndroidVPN,
})
if err != nil {
return nil, E.New("auto_detect_interface unsupported on current platform")
}
interfaceMonitor.RegisterCallback(router.notifyNetworkUpdate)
router.interfaceMonitor = interfaceMonitor
}
needFindProcess := hasRule(options.Rules, isProcessRule) || hasDNSRule(dnsOptions.Rules, isProcessDNSRule) || options.FindProcess needFindProcess := hasRule(options.Rules, isProcessRule) || hasDNSRule(dnsOptions.Rules, isProcessDNSRule) || options.FindProcess
needPackageManager := C.IsAndroid && platformInterface == nil && (needFindProcess || common.Any(inbounds, func(inbound option.Inbound) bool { needPackageManager := C.IsAndroid && platformInterface == nil && (needFindProcess || common.Any(inbounds, func(inbound option.Inbound) bool {
return len(inbound.TunOptions.IncludePackage) > 0 || len(inbound.TunOptions.ExcludePackage) > 0 return len(inbound.TunOptions.IncludePackage) > 0 || len(inbound.TunOptions.ExcludePackage) > 0
@@ -634,10 +657,6 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
r.logger.DebugContext(ctx, "found fakeip domain: ", domain) r.logger.DebugContext(ctx, "found fakeip domain: ", domain)
} }
if deadline.NeedAdditionalReadDeadline(conn) {
conn = deadline.NewConn(conn)
}
if metadata.InboundOptions.SniffEnabled { if metadata.InboundOptions.SniffEnabled {
buffer := buf.NewPacket() buffer := buf.NewPacket()
buffer.FullReset() buffer.FullReset()
@@ -743,11 +762,6 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
r.logger.DebugContext(ctx, "found fakeip domain: ", domain) r.logger.DebugContext(ctx, "found fakeip domain: ", domain)
} }
// Currently we don't have deadline usages for UDP connections
/*if deadline.NeedAdditionalReadDeadline(conn) {
conn = deadline.NewPacketConn(bufio.NewNetPacketConn(conn))
}*/
if metadata.InboundOptions.SniffEnabled { if metadata.InboundOptions.SniffEnabled {
buffer := buf.NewPacket() buffer := buf.NewPacket()
buffer.FullReset() buffer.FullReset()
@@ -872,31 +886,12 @@ func (r *Router) InterfaceFinder() control.InterfaceFinder {
return &r.interfaceFinder return &r.interfaceFinder
} }
func (r *Router) UpdateInterfaces() error {
if r.platformInterface == nil || !r.platformInterface.UsePlatformInterfaceGetter() {
return r.interfaceFinder.update()
} else {
interfaces, err := r.platformInterface.Interfaces()
if err != nil {
return err
}
r.interfaceFinder.updateInterfaces(common.Map(interfaces, func(it platform.NetworkInterface) net.Interface {
return net.Interface{
Name: it.Name,
Index: it.Index,
MTU: it.MTU,
}
}))
return nil
}
}
func (r *Router) AutoDetectInterface() bool { func (r *Router) AutoDetectInterface() bool {
return r.autoDetectInterface return r.autoDetectInterface
} }
func (r *Router) AutoDetectInterfaceFunc() control.Func { func (r *Router) AutoDetectInterfaceFunc() control.Func {
if r.platformInterface != nil && r.platformInterface.UsePlatformAutoDetectInterfaceControl() { if r.platformInterface != nil {
return r.platformInterface.AutoDetectInterfaceControl() return r.platformInterface.AutoDetectInterfaceControl()
} else { } else {
return control.BindToInterfaceFunc(r.InterfaceFinder(), func(network string, address string) (interfaceName string, interfaceIndex int) { return control.BindToInterfaceFunc(r.InterfaceFinder(), func(network string, address string) (interfaceName string, interfaceIndex int) {
@@ -975,7 +970,7 @@ func (r *Router) NewError(ctx context.Context, err error) {
} }
func (r *Router) notifyNetworkUpdate(int) error { func (r *Router) notifyNetworkUpdate(int) error {
if C.IsAndroid && r.platformInterface == nil { if C.IsAndroid {
var vpnStatus string var vpnStatus string
if r.interfaceMonitor.AndroidVPNEnabled() { if r.interfaceMonitor.AndroidVPNEnabled() {
vpnStatus = "enabled" vpnStatus = "enabled"
@@ -987,10 +982,6 @@ func (r *Router) notifyNetworkUpdate(int) error {
r.logger.Info("updated default interface ", r.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", r.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified())) r.logger.Info("updated default interface ", r.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", r.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified()))
} }
if conntrack.Enabled {
conntrack.Close()
}
for _, outbound := range r.outbounds { for _, outbound := range r.outbounds {
listener, isListener := outbound.(adapter.InterfaceUpdateListener) listener, isListener := outbound.(adapter.InterfaceUpdateListener)
if isListener { if isListener {

View File

@@ -18,7 +18,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"
"github.com/sagernet/sing/common/rw" "github.com/sagernet/sing/common/rw"
"github.com/sagernet/sing/service/filemanager"
) )
func (r *Router) GeoIPReader() *geoip.Reader { func (r *Router) GeoIPReader() *geoip.Reader {
@@ -52,7 +51,7 @@ func (r *Router) prepareGeoIPDatabase() error {
geoPath = foundPath geoPath = foundPath
} }
} }
geoPath = filemanager.BasePath(r.ctx, geoPath) geoPath = C.BasePath(geoPath)
if rw.FileExists(geoPath) { if rw.FileExists(geoPath) {
geoReader, codes, err := geoip.Open(geoPath) geoReader, codes, err := geoip.Open(geoPath)
if err == nil { if err == nil {
@@ -96,7 +95,7 @@ func (r *Router) prepareGeositeDatabase() error {
geoPath = foundPath geoPath = foundPath
} }
} }
geoPath = filemanager.BasePath(r.ctx, geoPath) geoPath = C.BasePath(geoPath)
if !rw.FileExists(geoPath) { if !rw.FileExists(geoPath) {
r.logger.Warn("geosite database not exists: ", geoPath) r.logger.Warn("geosite database not exists: ", geoPath)
var err error var err error
@@ -143,10 +142,10 @@ func (r *Router) downloadGeoIPDatabase(savePath string) error {
} }
if parentDir := filepath.Dir(savePath); parentDir != "" { if parentDir := filepath.Dir(savePath); parentDir != "" {
filemanager.MkdirAll(r.ctx, parentDir, 0o755) os.MkdirAll(parentDir, 0o755)
} }
saveFile, err := filemanager.Create(r.ctx, savePath) saveFile, err := os.OpenFile(savePath, os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil { if err != nil {
return E.Cause(err, "open output file: ", downloadURL) return E.Cause(err, "open output file: ", downloadURL)
} }
@@ -191,10 +190,10 @@ func (r *Router) downloadGeositeDatabase(savePath string) error {
} }
if parentDir := filepath.Dir(savePath); parentDir != "" { if parentDir := filepath.Dir(savePath); parentDir != "" {
filemanager.MkdirAll(r.ctx, parentDir, 0o755) os.MkdirAll(parentDir, 0o755)
} }
saveFile, err := filemanager.Create(r.ctx, savePath) saveFile, err := os.OpenFile(savePath, os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil { if err != nil {
return E.Cause(err, "open output file: ", downloadURL) return E.Cause(err, "open output file: ", downloadURL)
} }

View File

@@ -4,6 +4,13 @@ import (
"strings" "strings"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/warning"
C "github.com/sagernet/sing-box/constant"
)
var warnPackageNameOnNonAndroid = warning.New(
func() bool { return !C.IsAndroid },
"rule item `package_name` is only supported on Android",
) )
var _ RuleItem = (*PackageNameItem)(nil) var _ RuleItem = (*PackageNameItem)(nil)
@@ -14,6 +21,7 @@ type PackageNameItem struct {
} }
func NewPackageNameItem(packageNameList []string) *PackageNameItem { func NewPackageNameItem(packageNameList []string) *PackageNameItem {
warnPackageNameOnNonAndroid.Check()
rule := &PackageNameItem{ rule := &PackageNameItem{
packageNames: packageNameList, packageNames: packageNameList,
packageMap: make(map[string]bool), packageMap: make(map[string]bool),

View File

@@ -5,6 +5,13 @@ import (
"strings" "strings"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/warning"
C "github.com/sagernet/sing-box/constant"
)
var warnProcessNameOnNonSupportedPlatform = warning.New(
func() bool { return !(C.IsLinux || C.IsWindows || C.IsDarwin) },
"rule item `process_name` is only supported on Linux, Windows and macOS",
) )
var _ RuleItem = (*ProcessItem)(nil) var _ RuleItem = (*ProcessItem)(nil)
@@ -15,6 +22,7 @@ type ProcessItem struct {
} }
func NewProcessItem(processNameList []string) *ProcessItem { func NewProcessItem(processNameList []string) *ProcessItem {
warnProcessNameOnNonSupportedPlatform.Check()
rule := &ProcessItem{ rule := &ProcessItem{
processes: processNameList, processes: processNameList,
processMap: make(map[string]bool), processMap: make(map[string]bool),

View File

@@ -4,6 +4,13 @@ import (
"strings" "strings"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/warning"
C "github.com/sagernet/sing-box/constant"
)
var warnProcessPathOnNonSupportedPlatform = warning.New(
func() bool { return !(C.IsLinux || C.IsWindows || C.IsDarwin) },
"rule item `process_path` is only supported on Linux, Windows and macOS",
) )
var _ RuleItem = (*ProcessPathItem)(nil) var _ RuleItem = (*ProcessPathItem)(nil)
@@ -14,6 +21,7 @@ type ProcessPathItem struct {
} }
func NewProcessPathItem(processNameList []string) *ProcessPathItem { func NewProcessPathItem(processNameList []string) *ProcessPathItem {
warnProcessPathOnNonSupportedPlatform.Check()
rule := &ProcessPathItem{ rule := &ProcessPathItem{
processes: processNameList, processes: processNameList,
processMap: make(map[string]bool), processMap: make(map[string]bool),

View File

@@ -4,9 +4,16 @@ import (
"strings" "strings"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/warning"
C "github.com/sagernet/sing-box/constant"
F "github.com/sagernet/sing/common/format" F "github.com/sagernet/sing/common/format"
) )
var warnUserOnNonLinux = warning.New(
func() bool { return !C.IsLinux },
"rule item `user` is only supported on Linux",
)
var _ RuleItem = (*UserItem)(nil) var _ RuleItem = (*UserItem)(nil)
type UserItem struct { type UserItem struct {
@@ -15,6 +22,7 @@ type UserItem struct {
} }
func NewUserItem(users []string) *UserItem { func NewUserItem(users []string) *UserItem {
warnUserOnNonLinux.Check()
userMap := make(map[string]bool) userMap := make(map[string]bool)
for _, protocol := range users { for _, protocol := range users {
userMap[protocol] = true userMap[protocol] = true

View File

@@ -4,9 +4,16 @@ import (
"strings" "strings"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/warning"
C "github.com/sagernet/sing-box/constant"
F "github.com/sagernet/sing/common/format" F "github.com/sagernet/sing/common/format"
) )
var warnUserIDOnNonLinux = warning.New(
func() bool { return !C.IsLinux },
"rule item `user_id` is only supported on Linux",
)
var _ RuleItem = (*UserIdItem)(nil) var _ RuleItem = (*UserIdItem)(nil)
type UserIdItem struct { type UserIdItem struct {
@@ -15,6 +22,7 @@ type UserIdItem struct {
} }
func NewUserIDItem(userIdList []int32) *UserIdItem { func NewUserIDItem(userIdList []int32) *UserIdItem {
warnUserIDOnNonLinux.Check()
rule := &UserIdItem{ rule := &UserIdItem{
userIds: userIdList, userIds: userIdList,
userIdMap: make(map[int32]bool), userIdMap: make(map[int32]bool),

View File

@@ -7,21 +7,21 @@ require github.com/sagernet/sing-box v0.0.0
replace github.com/sagernet/sing-box => ../ replace github.com/sagernet/sing-box => ../
require ( require (
github.com/docker/docker v20.10.18+incompatible github.com/docker/docker v23.0.3+incompatible
github.com/docker/go-connections v0.4.0 github.com/docker/go-connections v0.4.0
github.com/gofrs/uuid/v5 v5.0.0 github.com/gofrs/uuid/v5 v5.0.0
github.com/sagernet/sing v0.2.4 github.com/sagernet/sing v0.2.2
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230417102954-f77257340507 github.com/sagernet/sing-shadowsocks v0.2.1-0.20230408141421-e40d6a4e42d4
github.com/spyzhov/ajson v0.7.1 github.com/spyzhov/ajson v0.8.0
github.com/stretchr/testify v1.8.2 github.com/stretchr/testify v1.8.2
go.uber.org/goleak v1.2.0 go.uber.org/goleak v1.2.1
golang.org/x/net v0.9.0 golang.org/x/net v0.9.0
) )
require ( require (
berty.tech/go-libtor v1.0.385 // indirect berty.tech/go-libtor v1.0.385 // indirect
github.com/Dreamacro/clash v1.15.0 // indirect github.com/Dreamacro/clash v1.14.0 // indirect
github.com/Microsoft/go-winio v0.6.0 // indirect github.com/Microsoft/go-winio v0.5.1 // indirect
github.com/ajg/form v1.5.1 // indirect github.com/ajg/form v1.5.1 // indirect
github.com/andybalholm/brotli v1.0.5 // indirect github.com/andybalholm/brotli v1.0.5 // indirect
github.com/caddyserver/certmagic v0.17.2 // indirect github.com/caddyserver/certmagic v0.17.2 // indirect
@@ -50,7 +50,7 @@ require (
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
github.com/mholt/acmez v1.1.0 // indirect github.com/mholt/acmez v1.1.0 // indirect
github.com/miekg/dns v1.1.53 // indirect github.com/miekg/dns v1.1.53 // indirect
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c // indirect
github.com/morikuni/aec v1.0.0 // indirect github.com/morikuni/aec v1.0.0 // indirect
github.com/onsi/ginkgo/v2 v2.2.0 // indirect github.com/onsi/ginkgo/v2 v2.2.0 // indirect
github.com/ooni/go-libtor v1.1.7 // indirect github.com/ooni/go-libtor v1.1.7 // indirect
@@ -70,16 +70,15 @@ require (
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 // indirect github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 // indirect
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect
github.com/sagernet/sing-dns v0.1.5-0.20230415085626-111ecf799dfc // indirect github.com/sagernet/sing-dns v0.1.5-0.20230408004833-5adaf486d440 // indirect
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b // indirect github.com/sagernet/sing-shadowtls v0.1.1-0.20230408141548-81d74d2a8661 // indirect
github.com/sagernet/sing-tun v0.1.5-0.20230422121432-209ec123ca7b // indirect github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab // indirect
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 // indirect github.com/sagernet/sing-vmess v0.1.4-0.20230409073451-6921c3dd77c7 // indirect
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 // indirect github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 // indirect
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 // indirect github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 // indirect
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e // indirect github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e // indirect
github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77 // indirect github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
go.etcd.io/bbolt v1.3.7 // indirect go.etcd.io/bbolt v1.3.7 // indirect
@@ -92,12 +91,15 @@ require (
golang.org/x/mod v0.8.0 // indirect golang.org/x/mod v0.8.0 // indirect
golang.org/x/sys v0.7.0 // indirect golang.org/x/sys v0.7.0 // indirect
golang.org/x/text v0.9.0 // indirect golang.org/x/text v0.9.0 // indirect
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
golang.org/x/tools v0.6.0 // indirect golang.org/x/tools v0.6.0 // indirect
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
google.golang.org/grpc v1.54.0 // indirect google.golang.org/grpc v1.54.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
gvisor.dev/gvisor v0.0.0-20230415003630-3981d5d5e523 // indirect gotest.tools/v3 v3.4.0 // indirect
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c // indirect
lukechampine.com/blake3 v1.1.7 // indirect lukechampine.com/blake3 v1.1.7 // indirect
) )
//replace github.com/sagernet/sing => ../../sing

View File

@@ -1,10 +1,10 @@
berty.tech/go-libtor v1.0.385 h1:RWK94C3hZj6Z2GdvePpHJLnWYobFr3bY/OdUJ5aoEXw= berty.tech/go-libtor v1.0.385 h1:RWK94C3hZj6Z2GdvePpHJLnWYobFr3bY/OdUJ5aoEXw=
berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+fJEw= berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+fJEw=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Dreamacro/clash v1.15.0 h1:mlpD950VEggXZBNahV66hyKDRxcczkj3vymoAt78KyE= github.com/Dreamacro/clash v1.14.0 h1:ehJ/C/1m9LEjmME72WSE/Y2YqbR3Q54AbjqiRCvtyW4=
github.com/Dreamacro/clash v1.15.0/go.mod h1:WNH69bN11LiAdgdSr4hpkEuXVMfBbWyhEKMCTx9BtNE= github.com/Dreamacro/clash v1.14.0/go.mod h1:ia2CU7V713H1QdCqMwOLK9U9V5Ay8X0voj3yQr2tk+I=
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY=
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
@@ -25,8 +25,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v20.10.18+incompatible h1:SN84VYXTBNGn92T/QwIRPlum9zfemfitN7pbsp26WSc= github.com/docker/docker v23.0.3+incompatible h1:9GhVsShNWz1hO//9BNg/dpMnZW25KydO4wtVxWAIbho=
github.com/docker/docker v20.10.18+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v23.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
@@ -82,8 +82,8 @@ github.com/mholt/acmez v1.1.0 h1:IQ9CGHKOHokorxnffsqDvmmE30mDenO1lptYZ1AYkHY=
github.com/mholt/acmez v1.1.0/go.mod h1:zwo5+fbLLTowAX8o8ETfQzbDtwGEXnPhkmGdKIP+bgs= github.com/mholt/acmez v1.1.0/go.mod h1:zwo5+fbLLTowAX8o8ETfQzbDtwGEXnPhkmGdKIP+bgs=
github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw= github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw=
github.com/miekg/dns v1.1.53/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= github.com/miekg/dns v1.1.53/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c h1:RC8WMpjonrBfyAh6VN/POIPtYD5tRAq0qMqCRjQNK+g=
github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c/go.mod h1:9OcmHNQQUTbk4XCffrLgN1NEKc2mh5u++biHVrvHsSU=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
@@ -126,18 +126,18 @@ github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byL
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.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
github.com/sagernet/sing v0.2.4 h1:gC8BR5sglbJZX23RtMyFa8EETP9YEUADhfbEzU1yVbo= github.com/sagernet/sing v0.2.2 h1:qfEdSLuwFgIIkeLOcwVQkVDzKLHtclXb93Ql0zZA+aE=
github.com/sagernet/sing v0.2.4/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= github.com/sagernet/sing v0.2.2/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
github.com/sagernet/sing-dns v0.1.5-0.20230415085626-111ecf799dfc h1:hmbuqKv48SAjiKPoqtJGvS5pEHVPZjTHq9CPwQY2cZ4= github.com/sagernet/sing-dns v0.1.5-0.20230408004833-5adaf486d440 h1:VH8/BcOVuApHtS+vKP+khxlGRcXH7KKhgkTDtNynqSQ=
github.com/sagernet/sing-dns v0.1.5-0.20230415085626-111ecf799dfc/go.mod h1:ZKuuqgsHRxDahYrzgSgy4vIAGGuKPlIf4hLcNzYzLkY= github.com/sagernet/sing-dns v0.1.5-0.20230408004833-5adaf486d440/go.mod h1:69PNSHyEmXdjf6C+bXBOdr2GQnPeEyWjIzo/MV8gmz8=
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230417102954-f77257340507 h1:bAHZCdWqJkb8LEW98+YsMVDXGRMUVjka8IC+St6ot88= github.com/sagernet/sing-shadowsocks v0.2.1-0.20230408141421-e40d6a4e42d4 h1:mKpXBBnAhTy9/CvDKqt5cN78LKX8cVUqjoWEXI/g0No=
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230417102954-f77257340507/go.mod h1:UJjvQGw0lyYaDGIDvUraL16fwaAEH1WFw1Y6sUcMPog= github.com/sagernet/sing-shadowsocks v0.2.1-0.20230408141421-e40d6a4e42d4/go.mod h1:NFEROpOEiLG+lEoSPNpSL2yDyXx6q0OJvwWnEAd40cI=
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b h1:ouW/6IDCrxkBe19YSbdCd7buHix7b+UZ6BM4Zz74XF4= github.com/sagernet/sing-shadowtls v0.1.1-0.20230408141548-81d74d2a8661 h1:QnV79JbJbJGT0MJJfd8o7QMEfRu3eUVKsmahxFMonrc=
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b/go.mod h1:oG8bPerYI6cZ74KquY3DvA7ynECyrILPBnce6wtBqeI= github.com/sagernet/sing-shadowtls v0.1.1-0.20230408141548-81d74d2a8661/go.mod h1:xCeSRP8cV32aPsY+6BbRdJjyD6q8ufdKwhgqxEbU/3U=
github.com/sagernet/sing-tun v0.1.5-0.20230422121432-209ec123ca7b h1:9NsciSJGwzdkXwVvT2c2g+RvkTVkANeBLr2l+soJ7LM= github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab h1:a9oeWuPBuIZ70qMhIIH6RrYhp886xN9jJIwsuu4ZFUo=
github.com/sagernet/sing-tun v0.1.5-0.20230422121432-209ec123ca7b/go.mod h1:DD7Ce2Gt0GFc6I/1+Uw4D/aUlBsGqrQsC52CMK/V818= github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab/go.mod h1:4YxIDEkkCjGXDOTMPw1SXpLmCQUFAWuaQN250oo+928=
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 h1:BHOnxrbC929JonuKqFdJ7ZbDp7zs4oTlH5KFvKtWu9U= github.com/sagernet/sing-vmess v0.1.4-0.20230409073451-6921c3dd77c7 h1:ZArINfN+zcHMdZCeRFOm4rO3SWyvYuLg3VhWcA5zonc=
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3/go.mod h1:yKrAr+dqZd64DxBXCHWrYicp+n4qbqO73mtwv3dck8U= github.com/sagernet/sing-vmess v0.1.4-0.20230409073451-6921c3dd77c7/go.mod h1:A9iGfwYmAEmVg8bavkqQ7Hx7nr9Pc2oWMYDwbLIBDjY=
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as= github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as=
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37/go.mod h1:3skNSftZDJWTGVtVaM2jfbce8qHnmH/AGDRe62iNOg0= github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37/go.mod h1:3skNSftZDJWTGVtVaM2jfbce8qHnmH/AGDRe62iNOg0=
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE= github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE=
@@ -146,15 +146,15 @@ github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 h1:kDUqhc9Vsk5HJuhfI
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/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs= github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs=
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e/go.mod h1:45TUl8+gH4SIKr4ykREbxKWTxkDlSzFENzctB1dVRRY= github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e/go.mod h1:45TUl8+gH4SIKr4ykREbxKWTxkDlSzFENzctB1dVRRY=
github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77 h1:g6QtRWQ2dKX7EQP++1JLNtw4C2TNxd4/ov8YUpOPOSo= github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo=
github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77/go.mod h1:pJDdXzZIwJ+2vmnT0TKzmf8meeum+e2mTDSehw79eE0= github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spyzhov/ajson v0.8.0 h1:sFXyMbi4Y/BKjrsfkUZHSjA2JM1184enheSjjoT/zCc=
github.com/spyzhov/ajson v0.7.1 h1:1MDIlPc6x0zjNtpa7tDzRAyFAvRX+X8ZsvtYz5lZg6A= github.com/spyzhov/ajson v0.8.0/go.mod h1:63V+CGM6f1Bu/p4nLIN8885ojBdt88TbLoSFzyqMuVA=
github.com/spyzhov/ajson v0.7.1/go.mod h1:63V+CGM6f1Bu/p4nLIN8885ojBdt88TbLoSFzyqMuVA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@@ -174,8 +174,8 @@ go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
@@ -191,7 +191,6 @@ golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -214,16 +213,18 @@ golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/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-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/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-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/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=
@@ -236,12 +237,13 @@ 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.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
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.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
@@ -264,7 +266,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
gvisor.dev/gvisor v0.0.0-20230415003630-3981d5d5e523 h1:zUQYeyyPLnSR6yMvLSOmLH37xDWCZ7BqlpE69fE5K3Q= gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
gvisor.dev/gvisor v0.0.0-20230415003630-3981d5d5e523/go.mod h1:pzr6sy8gDLfVmDAg8OYrlKvGEHw5C3PGTiBXBTCx76Q= gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c h1:m5lcgWnL3OElQNVyp3qcncItJ2c0sQlSGjYK2+nJTA4=
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0= lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0=
lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=

View File

@@ -18,40 +18,18 @@ var muxProtocols = []mux.Protocol{
} }
func TestVMessSMux(t *testing.T) { func TestVMessSMux(t *testing.T) {
testVMessMux(t, option.MultiplexOptions{ testVMessMux(t, mux.ProtocolSMux.String())
Enabled: true,
Protocol: mux.ProtocolSMux.String(),
})
} }
func TestShadowsocksMux(t *testing.T) { func TestShadowsocksMux(t *testing.T) {
for _, protocol := range muxProtocols { for _, protocol := range muxProtocols {
t.Run(protocol.String(), func(t *testing.T) { t.Run(protocol.String(), func(t *testing.T) {
testShadowsocksMux(t, option.MultiplexOptions{ testShadowsocksMux(t, protocol.String())
Enabled: true,
Protocol: protocol.String(),
})
}) })
} }
} }
func TestShadowsockH2Mux(t *testing.T) { func testShadowsocksMux(t *testing.T, protocol string) {
testShadowsocksMux(t, option.MultiplexOptions{
Enabled: true,
Protocol: mux.ProtocolH2Mux.String(),
Padding: true,
})
}
func TestShadowsockSMuxPadding(t *testing.T) {
testShadowsocksMux(t, option.MultiplexOptions{
Enabled: true,
Protocol: mux.ProtocolSMux.String(),
Padding: true,
})
}
func testShadowsocksMux(t *testing.T, options option.MultiplexOptions) {
method := shadowaead_2022.List[0] method := shadowaead_2022.List[0]
password := mkBase64(t, 16) password := mkBase64(t, 16)
startInstance(t, option.Options{ startInstance(t, option.Options{
@@ -90,9 +68,12 @@ func testShadowsocksMux(t *testing.T, options option.MultiplexOptions) {
Server: "127.0.0.1", Server: "127.0.0.1",
ServerPort: serverPort, ServerPort: serverPort,
}, },
Method: method, Method: method,
Password: password, Password: password,
MultiplexOptions: &options, MultiplexOptions: &option.MultiplexOptions{
Enabled: true,
Protocol: protocol,
},
}, },
}, },
}, },
@@ -110,7 +91,7 @@ func testShadowsocksMux(t *testing.T, options option.MultiplexOptions) {
testSuit(t, clientPort, testPort) testSuit(t, clientPort, testPort)
} }
func testVMessMux(t *testing.T, options option.MultiplexOptions) { func testVMessMux(t *testing.T, protocol string) {
user, _ := uuid.NewV4() user, _ := uuid.NewV4()
startInstance(t, option.Options{ startInstance(t, option.Options{
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
@@ -151,9 +132,12 @@ func testVMessMux(t *testing.T, options option.MultiplexOptions) {
Server: "127.0.0.1", Server: "127.0.0.1",
ServerPort: serverPort, ServerPort: serverPort,
}, },
Security: "auto", Security: "auto",
UUID: user.String(), UUID: user.String(),
Multiplex: &options, Multiplex: &option.MultiplexOptions{
Enabled: true,
Protocol: protocol,
},
}, },
}, },
}, },

View File

@@ -141,95 +141,6 @@ func TestVLESSVisionReality(t *testing.T) {
testSuit(t, clientPort, testPort) testSuit(t, clientPort, testPort)
} }
func TestVLESSVisionRealityPlain(t *testing.T) {
userUUID := newUUID()
startInstance(t, option.Options{
Inbounds: []option.Inbound{
{
Type: C.TypeMixed,
Tag: "mixed-in",
MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort,
},
},
},
{
Type: C.TypeVLESS,
VLESSOptions: option.VLESSInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: serverPort,
},
Users: []option.VLESSUser{
{
Name: "sekai",
UUID: userUUID.String(),
Flow: vless.FlowVision,
},
},
TLS: &option.InboundTLSOptions{
Enabled: true,
ServerName: "google.com",
Reality: &option.InboundRealityOptions{
Enabled: true,
Handshake: option.InboundRealityHandshakeOptions{
ServerOptions: option.ServerOptions{
Server: "google.com",
ServerPort: 443,
},
},
ShortID: []string{"0123456789abcdef"},
PrivateKey: "UuMBgl7MXTPx9inmQp2UC7Jcnwc6XYbwDNebonM-FCc",
},
},
},
},
},
Outbounds: []option.Outbound{
{
Type: C.TypeDirect,
},
{
Type: C.TypeVLESS,
Tag: "vless-out",
VLESSOptions: option.VLESSOutboundOptions{
ServerOptions: option.ServerOptions{
Server: "127.0.0.1",
ServerPort: serverPort,
},
UUID: userUUID.String(),
Flow: vless.FlowVision,
TLS: &option.OutboundTLSOptions{
Enabled: true,
ServerName: "google.com",
Reality: &option.OutboundRealityOptions{
Enabled: true,
ShortID: "0123456789abcdef",
PublicKey: "jNXHt1yRo0vDuchQlIP6Z0ZvjT3KtzVI-T4E7RoLJS0",
},
UTLS: &option.OutboundUTLSOptions{
Enabled: true,
},
},
},
},
},
Route: &option.RouteOptions{
Rules: []option.Rule{
{
DefaultOptions: option.DefaultRule{
Inbound: []string{"mixed-in"},
Outbound: "vless-out",
},
},
},
},
})
testSuit(t, clientPort, testPort)
}
func TestVLESSRealityTransport(t *testing.T) { func TestVLESSRealityTransport(t *testing.T) {
t.Run("grpc", func(t *testing.T) { t.Run("grpc", func(t *testing.T) {
testVLESSRealityTransport(t, &option.V2RayTransportOptions{ testVLESSRealityTransport(t, &option.V2RayTransportOptions{
@@ -249,6 +160,8 @@ func TestVLESSRealityTransport(t *testing.T) {
} }
func testVLESSRealityTransport(t *testing.T, transport *option.V2RayTransportOptions) { func testVLESSRealityTransport(t *testing.T, transport *option.V2RayTransportOptions) {
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
userUUID := newUUID() userUUID := newUUID()
startInstance(t, option.Options{ startInstance(t, option.Options{
Inbounds: []option.Inbound{ Inbounds: []option.Inbound{
@@ -293,11 +206,52 @@ func testVLESSRealityTransport(t *testing.T, transport *option.V2RayTransportOpt
Transport: transport, Transport: transport,
}, },
}, },
{
Type: C.TypeTrojan,
Tag: "trojan",
TrojanOptions: option.TrojanInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
ListenPort: otherPort,
},
Users: []option.TrojanUser{
{
Name: "sekai",
Password: userUUID.String(),
},
},
TLS: &option.InboundTLSOptions{
Enabled: true,
ServerName: "example.org",
CertificatePath: certPem,
KeyPath: keyPem,
},
},
},
}, },
Outbounds: []option.Outbound{ Outbounds: []option.Outbound{
{ {
Type: C.TypeDirect, Type: C.TypeDirect,
}, },
{
Type: C.TypeTrojan,
Tag: "trojan-out",
TrojanOptions: option.TrojanOutboundOptions{
ServerOptions: option.ServerOptions{
Server: "127.0.0.1",
ServerPort: otherPort,
},
Password: userUUID.String(),
TLS: &option.OutboundTLSOptions{
Enabled: true,
ServerName: "example.org",
CertificatePath: certPem,
},
DialerOptions: option.DialerOptions{
Detour: "vless-out",
},
},
},
{ {
Type: C.TypeVLESS, Type: C.TypeVLESS,
Tag: "vless-out", Tag: "vless-out",
@@ -328,7 +282,7 @@ func testVLESSRealityTransport(t *testing.T, transport *option.V2RayTransportOpt
{ {
DefaultOptions: option.DefaultRule{ DefaultOptions: option.DefaultRule{
Inbound: []string{"mixed-in"}, Inbound: []string{"mixed-in"},
Outbound: "vless-out", Outbound: "trojan-out",
}, },
}, },
}, },

View File

@@ -1,59 +0,0 @@
package main
import (
"context"
"net"
"testing"
"time"
"github.com/sagernet/sing/common"
"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/udpnat"
)
func TestUDPNatClose(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
connCtx, connCancel := common.ContextWithCancelCause(context.Background())
defer connCancel(net.ErrClosed)
service := udpnat.New[int](1, &testUDPNatCloseHandler{connCancel})
service.NewPacket(ctx, 0, buf.As([]byte("Hello")), M.Metadata{}, func(natConn N.PacketConn) N.PacketWriter {
return &testPacketWriter{}
})
select {
case <-connCtx.Done():
if E.IsClosed(connCtx.Err()) {
t.Fatal(E.New("conn closed unexpectedly: ", connCtx.Err()))
}
case <-time.After(2 * time.Second):
t.Fatal("conn not closed")
}
}
type testUDPNatCloseHandler struct {
done common.ContextCancelCauseFunc
}
func (h *testUDPNatCloseHandler) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
for {
buffer := buf.NewPacket()
_, err := conn.ReadPacket(buffer)
buffer.Release()
if err != nil {
h.done(err)
return err
}
}
}
func (h *testUDPNatCloseHandler) NewError(ctx context.Context, err error) {
}
type testPacketWriter struct{}
func (t *testPacketWriter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
return nil
}

View File

@@ -119,7 +119,7 @@ func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg,
func (t *Transport) fetchInterface() (*net.Interface, error) { func (t *Transport) fetchInterface() (*net.Interface, error) {
interfaceName := t.interfaceName interfaceName := t.interfaceName
if t.autoInterface { if t.autoInterface {
if t.router.InterfaceMonitor() == nil { if t.router.NetworkMonitor() == nil {
return nil, E.New("missing monitor for auto DHCP, set route.auto_detect_interface") return nil, E.New("missing monitor for auto DHCP, set route.auto_detect_interface")
} }
interfaceName = t.router.InterfaceMonitor().DefaultInterfaceName(netip.Addr{}) interfaceName = t.router.InterfaceMonitor().DefaultInterfaceName(netip.Addr{})

View File

@@ -535,10 +535,6 @@ func (c *PacketConn) SetWriteDeadline(t time.Time) error {
return os.ErrInvalid return os.ErrInvalid
} }
func (c *PacketConn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *PacketConn) Read(b []byte) (n int, err error) { func (c *PacketConn) Read(b []byte) (n int, err error) {
return 0, os.ErrInvalid return 0, os.ErrInvalid
} }

View File

@@ -45,7 +45,6 @@ func newV2RayPlugin(pluginOpts Args, router adapter.Router, dialer N.Dialer, ser
if hostOpt, loaded := pluginOpts.Get("host"); loaded { if hostOpt, loaded := pluginOpts.Get("host"); loaded {
host = hostOpt host = hostOpt
tlsOptions.ServerName = hostOpt
} }
if pathOpt, loaded := pluginOpts.Get("path"); loaded { if pathOpt, loaded := pluginOpts.Get("path"); loaded {
path = pathOpt path = pathOpt

View File

@@ -4,6 +4,8 @@ import (
"context" "context"
"net" "net"
"github.com/sagernet/sing/common/bufio"
"github.com/sagernet/sing/common/bufio/deadline"
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"
"github.com/sagernet/sing/common/rw" "github.com/sagernet/sing/common/rw"
@@ -53,7 +55,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, deadline.NewPacketConn(bufio.NewNetPacketConn(&PacketConn{stream})), metadata)
default: default:
return E.New("unknown command ", command) return E.New("unknown command ", command)
} }

View File

@@ -8,6 +8,7 @@ import (
"github.com/sagernet/sing/common/auth" "github.com/sagernet/sing/common/auth"
"github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio" "github.com/sagernet/sing/common/bufio"
"github.com/sagernet/sing/common/bufio/deadline"
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"
@@ -106,7 +107,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, deadline.NewPacketConn(bufio.NewNetPacketConn(&PacketConn{conn})), metadata)
// case CommandMux: // case CommandMux:
default: default:
return HandleMuxConnection(ctx, conn, metadata, s.handler) return HandleMuxConnection(ctx, conn, metadata, s.handler)
@@ -136,11 +137,3 @@ func (c *PacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) er
func (c *PacketConn) FrontHeadroom() int { func (c *PacketConn) FrontHeadroom() int {
return M.MaxSocksaddrLength + 4 return M.MaxSocksaddrLength + 4
} }
func (c *PacketConn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *PacketConn) Upstream() any {
return c.Conn
}

View File

@@ -10,6 +10,7 @@ import (
"github.com/sagernet/sing-box/common/tls" "github.com/sagernet/sing-box/common/tls"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/bufio/deadline"
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"
@@ -109,5 +110,5 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
cancel(err) cancel(err)
return nil, err return nil, err
} }
return NewGRPCConn(stream, cancel), nil return deadline.NewConn(NewGRPCConn(stream, cancel)), nil
} }

View File

@@ -81,10 +81,6 @@ func (c *GRPCConn) SetWriteDeadline(t time.Time) error {
return os.ErrInvalid return os.ErrInvalid
} }
func (c *GRPCConn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *GRPCConn) Upstream() any { func (c *GRPCConn) Upstream() any {
return c.GunService return c.GunService
} }

View File

@@ -11,6 +11,7 @@ import (
"github.com/sagernet/sing-box/common/tls" "github.com/sagernet/sing-box/common/tls"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/bufio/deadline"
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"
@@ -66,7 +67,7 @@ func (s *Server) Tun(server GunService_TunServer) error {
} }
} }
} }
go s.handler.NewConnection(ctx, conn, metadata) go s.handler.NewConnection(ctx, deadline.NewConn(conn), metadata)
<-ctx.Done() <-ctx.Done()
return nil return nil
} }

View File

@@ -12,7 +12,7 @@ import (
"github.com/sagernet/sing-box/common/tls" "github.com/sagernet/sing-box/common/tls"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/transport/v2rayhttp" "github.com/sagernet/sing-box/transport/v2rayhttp"
E "github.com/sagernet/sing/common/exceptions" "github.com/sagernet/sing/common/bufio/deadline"
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"
@@ -34,16 +34,9 @@ type Client struct {
transport *http2.Transport transport *http2.Transport
options option.V2RayGRPCOptions options option.V2RayGRPCOptions
url *url.URL url *url.URL
host string
} }
func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayGRPCOptions, tlsConfig tls.Config) adapter.V2RayClientTransport { func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayGRPCOptions, tlsConfig tls.Config) adapter.V2RayClientTransport {
var host string
if tlsConfig != nil && tlsConfig.ServerName() != "" {
host = M.ParseSocksaddrHostPort(tlsConfig.ServerName(), serverAddr.Port).String()
} else {
host = serverAddr.String()
}
client := &Client{ client := &Client{
ctx: ctx, ctx: ctx,
dialer: dialer, dialer: dialer,
@@ -60,7 +53,6 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
Path: "/" + options.ServiceName + "/Tun", Path: "/" + options.ServiceName + "/Tun",
RawPath: "/" + url.PathEscape(options.ServiceName) + "/Tun", RawPath: "/" + url.PathEscape(options.ServiceName) + "/Tun",
}, },
host: host,
} }
if tlsConfig == nil { if tlsConfig == nil {
@@ -90,22 +82,18 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
Body: pipeInReader, Body: pipeInReader,
URL: c.url, URL: c.url,
Header: defaultClientHeader, Header: defaultClientHeader,
Host: c.host,
} }
request = request.WithContext(ctx) request = request.WithContext(ctx)
conn := newLateGunConn(pipeInWriter) conn := newLateGunConn(pipeInWriter)
go func() { go func() {
response, err := c.transport.RoundTrip(request) response, err := c.transport.RoundTrip(request)
if err != nil { if err == nil {
conn.setup(nil, err)
} else if response.StatusCode != 200 {
response.Body.Close()
conn.setup(nil, E.New("unexpected status: ", response.StatusCode, " ", response.Status))
} else {
conn.setup(response.Body, nil) conn.setup(response.Body, nil)
} else {
conn.setup(nil, err)
} }
}() }()
return conn, nil return deadline.NewConn(conn), nil
} }
func (c *Client) Close() error { func (c *Client) Close() error {

View File

@@ -156,7 +156,3 @@ func (c *GunConn) SetReadDeadline(t time.Time) error {
func (c *GunConn) SetWriteDeadline(t time.Time) error { func (c *GunConn) SetWriteDeadline(t time.Time) error {
return os.ErrInvalid return os.ErrInvalid
} }
func (c *GunConn) NeedAdditionalReadDeadline() bool {
return true
}

View File

@@ -13,6 +13,7 @@ import (
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/transport/v2rayhttp" "github.com/sagernet/sing-box/transport/v2rayhttp"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/bufio/deadline"
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"
@@ -81,7 +82,7 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
var metadata M.Metadata var metadata M.Metadata
metadata.Source = sHttp.SourceAddress(request) metadata.Source = sHttp.SourceAddress(request)
conn := v2rayhttp.NewHTTP2Wrapper(newGunConn(request.Body, writer, writer.(http.Flusher))) conn := v2rayhttp.NewHTTP2Wrapper(newGunConn(request.Body, writer, writer.(http.Flusher)))
s.handler.NewConnection(request.Context(), conn, metadata) s.handler.NewConnection(request.Context(), deadline.NewConn(conn), metadata)
conn.CloseWrapper() conn.CloseWrapper()
} }

View File

@@ -139,16 +139,15 @@ func (c *Client) dialHTTP2(ctx context.Context) (net.Conn, error) {
default: default:
request.Host = c.host[rand.Intn(hostLen)] request.Host = c.host[rand.Intn(hostLen)]
} }
conn := NewLateHTTPConn(pipeInWriter) conn := newLateHTTPConn(pipeInWriter)
go func() { go func() {
response, err := c.transport.RoundTrip(request) response, err := c.transport.RoundTrip(request)
if err != nil { if err != nil {
conn.Setup(nil, err) conn.setup(nil, err)
} else if response.StatusCode != 200 { } else if response.StatusCode != 200 {
response.Body.Close() conn.setup(nil, E.New("unexpected status: ", response.StatusCode, " ", response.Status))
conn.Setup(nil, E.New("unexpected status: ", response.StatusCode, " ", response.Status))
} else { } else {
conn.Setup(response.Body, nil) conn.setup(response.Body, nil)
} }
}() }()
return conn, nil return conn, nil

View File

@@ -140,14 +140,14 @@ func NewHTTPConn(reader io.Reader, writer io.Writer) HTTP2Conn {
} }
} }
func NewLateHTTPConn(writer io.Writer) *HTTP2Conn { func newLateHTTPConn(writer io.Writer) *HTTP2Conn {
return &HTTP2Conn{ return &HTTP2Conn{
create: make(chan struct{}), create: make(chan struct{}),
writer: writer, writer: writer,
} }
} }
func (c *HTTP2Conn) Setup(reader io.Reader, err error) { func (c *HTTP2Conn) setup(reader io.Reader, err error) {
c.reader = reader c.reader = reader
c.err = err c.err = err
close(c.create) close(c.create)
@@ -182,30 +182,41 @@ func (c *HTTP2Conn) RemoteAddr() net.Addr {
} }
func (c *HTTP2Conn) SetDeadline(t time.Time) error { func (c *HTTP2Conn) SetDeadline(t time.Time) error {
if responseWriter, loaded := c.writer.(interface {
SetWriteDeadline(time.Time) error
}); loaded {
return responseWriter.SetWriteDeadline(t)
}
return os.ErrInvalid return os.ErrInvalid
} }
func (c *HTTP2Conn) SetReadDeadline(t time.Time) error { func (c *HTTP2Conn) SetReadDeadline(t time.Time) error {
if responseWriter, loaded := c.writer.(interface {
SetReadDeadline(time.Time) error
}); loaded {
return responseWriter.SetReadDeadline(t)
}
return os.ErrInvalid return os.ErrInvalid
} }
func (c *HTTP2Conn) SetWriteDeadline(t time.Time) error { func (c *HTTP2Conn) SetWriteDeadline(t time.Time) error {
if responseWriter, loaded := c.writer.(interface {
SetWriteDeadline(time.Time) error
}); loaded {
return responseWriter.SetWriteDeadline(t)
}
return os.ErrInvalid return os.ErrInvalid
} }
func (c *HTTP2Conn) NeedAdditionalReadDeadline() bool {
return true
}
type ServerHTTPConn struct { type ServerHTTPConn struct {
HTTP2Conn HTTP2Conn
Flusher http.Flusher flusher http.Flusher
} }
func (c *ServerHTTPConn) Write(b []byte) (n int, err error) { func (c *ServerHTTPConn) Write(b []byte) (n int, err error) {
n, err = c.writer.Write(b) n, err = c.writer.Write(b)
if err == nil { if err == nil {
c.Flusher.Flush() c.flusher.Flush()
} }
return return
} }
@@ -246,11 +257,6 @@ func (w *HTTP2ConnWrapper) CloseWrapper() {
w.closed = true w.closed = true
} }
func (w *HTTP2ConnWrapper) Close() error {
w.CloseWrapper()
return w.ExtendedConn.Close()
}
func (w *HTTP2ConnWrapper) Upstream() any { func (w *HTTP2ConnWrapper) Upstream() any {
return w.ExtendedConn return w.ExtendedConn
} }

View File

@@ -10,6 +10,7 @@ import (
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/tls" "github.com/sagernet/sing-box/common/tls"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common/bufio/deadline"
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"
@@ -78,11 +79,11 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
if c.maxEarlyData <= 0 { if c.maxEarlyData <= 0 {
conn, response, err := c.dialer.DialContext(ctx, c.uri, c.headers) conn, response, err := c.dialer.DialContext(ctx, c.uri, c.headers)
if err == nil { if err == nil {
return &WebsocketConn{Conn: conn, Writer: NewWriter(conn, false)}, nil return deadline.NewConn(&WebsocketConn{Conn: conn, Writer: NewWriter(conn, false)}), nil
} }
return nil, wrapDialError(response, err) return nil, wrapDialError(response, err)
} else { } else {
return &EarlyWebsocketConn{Client: c, ctx: ctx, create: make(chan struct{})}, nil return deadline.NewConn(&EarlyWebsocketConn{Client: c, ctx: ctx, create: make(chan struct{})}), nil
} }
} }

View File

@@ -77,10 +77,6 @@ func (c *WebsocketConn) SetWriteDeadline(t time.Time) error {
return os.ErrInvalid return os.ErrInvalid
} }
func (c *WebsocketConn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *WebsocketConn) Upstream() any { func (c *WebsocketConn) Upstream() any {
return c.Conn.NetConn() return c.Conn.NetConn()
} }
@@ -218,10 +214,6 @@ func (c *EarlyWebsocketConn) SetWriteDeadline(t time.Time) error {
return os.ErrInvalid return os.ErrInvalid
} }
func (c *EarlyWebsocketConn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *EarlyWebsocketConn) Upstream() any { func (c *EarlyWebsocketConn) Upstream() any {
return common.PtrOrNil(c.conn) return common.PtrOrNil(c.conn)
} }

View File

@@ -16,6 +16,7 @@ import (
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio" "github.com/sagernet/sing/common/bufio"
"github.com/sagernet/sing/common/bufio/deadline"
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"
@@ -107,7 +108,7 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
if len(earlyData) > 0 { if len(earlyData) > 0 {
conn = bufio.NewCachedConn(conn, buf.As(earlyData)) conn = bufio.NewCachedConn(conn, buf.As(earlyData))
} }
s.handler.NewConnection(request.Context(), conn, metadata) s.handler.NewConnection(request.Context(), deadline.NewConn(conn), metadata)
} }
func (s *Server) fallbackRequest(ctx context.Context, writer http.ResponseWriter, request *http.Request, statusCode int, err error) { func (s *Server) fallbackRequest(ctx context.Context, writer http.ResponseWriter, request *http.Request, statusCode int, err error) {

View File

@@ -9,6 +9,7 @@ import (
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio" "github.com/sagernet/sing/common/bufio"
"github.com/sagernet/sing/common/bufio/deadline"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger" "github.com/sagernet/sing/common/logger"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
@@ -36,156 +37,105 @@ func NewClient(userId string, flow string, logger logger.Logger) (*Client, error
return &Client{user, flow, logger}, nil return &Client{user, flow, logger}, nil
} }
func (c *Client) prepareConn(conn net.Conn, tlsConn net.Conn) (net.Conn, error) { func (c *Client) prepareConn(conn net.Conn) (net.Conn, error) {
if c.flow == FlowVision { if c.flow == FlowVision {
protocolConn, err := NewVisionConn(conn, tlsConn, c.key, c.logger) vConn, err := NewVisionConn(conn, c.key, c.logger)
if err != nil { if err != nil {
return nil, E.Cause(err, "initialize vision") return nil, E.Cause(err, "initialize vision")
} }
conn = protocolConn conn = vConn
} }
return conn, nil return conn, nil
} }
func (c *Client) DialConn(conn net.Conn, destination M.Socksaddr) (net.Conn, error) { func (c *Client) DialConn(conn net.Conn, destination M.Socksaddr) (net.Conn, error) {
remoteConn := NewConn(conn, c.key, vmess.CommandTCP, destination, c.flow) vConn, err := c.prepareConn(conn)
protocolConn, err := c.prepareConn(remoteConn, conn)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return protocolConn, common.Error(remoteConn.Write(nil)) serverConn := &Conn{Conn: conn, protocolConn: vConn, key: c.key, command: vmess.CommandTCP, destination: destination, flow: c.flow}
return deadline.NewConn(serverConn), common.Error(serverConn.Write(nil))
} }
func (c *Client) DialEarlyConn(conn net.Conn, destination M.Socksaddr) (net.Conn, error) { func (c *Client) DialEarlyConn(conn net.Conn, destination M.Socksaddr) (net.Conn, error) {
return c.prepareConn(NewConn(conn, c.key, vmess.CommandTCP, destination, c.flow), conn) vConn, err := c.prepareConn(conn)
if err != nil {
return nil, err
}
return deadline.NewConn(&Conn{Conn: conn, protocolConn: vConn, key: c.key, command: vmess.CommandTCP, destination: destination, flow: c.flow}), nil
} }
func (c *Client) DialPacketConn(conn net.Conn, destination M.Socksaddr) (*PacketConn, error) { func (c *Client) DialPacketConn(conn net.Conn, destination M.Socksaddr) (vmess.PacketConn, error) {
serverConn := &PacketConn{Conn: conn, key: c.key, destination: destination, flow: c.flow} serverConn := &PacketConn{Conn: conn, key: c.key, destination: destination, flow: c.flow}
return serverConn, common.Error(serverConn.Write(nil)) return bufio.NewBindPacketConn(deadline.NewPacketConn(bufio.NewPacketConn(serverConn)), destination), common.Error(serverConn.Write(nil))
} }
func (c *Client) DialEarlyPacketConn(conn net.Conn, destination M.Socksaddr) (*PacketConn, error) { func (c *Client) DialEarlyPacketConn(conn net.Conn, destination M.Socksaddr) (vmess.PacketConn, error) {
return &PacketConn{Conn: conn, key: c.key, destination: destination, flow: c.flow}, nil return bufio.NewBindPacketConn(deadline.NewPacketConn(bufio.NewPacketConn(&PacketConn{Conn: conn, key: c.key, destination: destination, flow: c.flow})), destination), nil
} }
func (c *Client) DialXUDPPacketConn(conn net.Conn, destination M.Socksaddr) (vmess.PacketConn, error) { func (c *Client) DialXUDPPacketConn(conn net.Conn, destination M.Socksaddr) (vmess.PacketConn, error) {
remoteConn := NewConn(conn, c.key, vmess.CommandTCP, destination, c.flow) serverConn := &Conn{Conn: conn, protocolConn: conn, key: c.key, command: vmess.CommandMux, destination: destination, flow: c.flow}
protocolConn, err := c.prepareConn(remoteConn, conn) err := common.Error(serverConn.Write(nil))
if err != nil { if err != nil {
return nil, err return nil, err
} }
return vmess.NewXUDPConn(protocolConn, destination), common.Error(remoteConn.Write(nil)) return bufio.NewBindPacketConn(deadline.NewPacketConn(vmess.NewXUDPConn(serverConn, destination)), destination), nil
} }
func (c *Client) DialEarlyXUDPPacketConn(conn net.Conn, destination M.Socksaddr) (vmess.PacketConn, error) { func (c *Client) DialEarlyXUDPPacketConn(conn net.Conn, destination M.Socksaddr) (vmess.PacketConn, error) {
remoteConn := NewConn(conn, c.key, vmess.CommandMux, destination, c.flow) return bufio.NewBindPacketConn(deadline.NewPacketConn(vmess.NewXUDPConn(&Conn{Conn: conn, protocolConn: conn, key: c.key, command: vmess.CommandMux, destination: destination, flow: c.flow}, destination)), destination), nil
protocolConn, err := c.prepareConn(remoteConn, conn)
if err != nil {
return nil, err
}
return vmess.NewXUDPConn(protocolConn, destination), common.Error(remoteConn.Write(nil))
} }
var ( var _ N.EarlyConn = (*Conn)(nil)
_ N.EarlyConn = (*Conn)(nil)
_ N.VectorisedWriter = (*Conn)(nil)
)
type Conn struct { type Conn struct {
N.ExtendedConn net.Conn
writer N.VectorisedWriter protocolConn net.Conn
request Request key [16]byte
command byte
destination M.Socksaddr
flow string
requestWritten bool requestWritten bool
responseRead bool responseRead bool
} }
func NewConn(conn net.Conn, uuid [16]byte, command byte, destination M.Socksaddr, flow string) *Conn {
return &Conn{
ExtendedConn: bufio.NewExtendedConn(conn),
writer: bufio.NewVectorisedWriter(conn),
request: Request{
UUID: uuid,
Command: command,
Destination: destination,
Flow: flow,
},
}
}
func (c *Conn) Read(b []byte) (n int, err error) {
if !c.responseRead {
err = ReadResponse(c.ExtendedConn)
if err != nil {
return
}
c.responseRead = true
}
return c.ExtendedConn.Read(b)
}
func (c *Conn) ReadBuffer(buffer *buf.Buffer) error {
if !c.responseRead {
err := ReadResponse(c.ExtendedConn)
if err != nil {
return err
}
c.responseRead = true
}
return c.ExtendedConn.ReadBuffer(buffer)
}
func (c *Conn) Write(b []byte) (n int, err error) {
if !c.requestWritten {
err = WriteRequest(c.ExtendedConn, c.request, b)
if err == nil {
n = len(b)
}
c.requestWritten = true
return
}
return c.ExtendedConn.Write(b)
}
func (c *Conn) WriteBuffer(buffer *buf.Buffer) error {
if !c.requestWritten {
EncodeRequest(c.request, buf.With(buffer.ExtendHeader(RequestLen(c.request))))
c.requestWritten = true
}
return c.ExtendedConn.WriteBuffer(buffer)
}
func (c *Conn) WriteVectorised(buffers []*buf.Buffer) error {
if !c.requestWritten {
buffer := buf.NewSize(RequestLen(c.request))
EncodeRequest(c.request, buffer)
c.requestWritten = true
return c.writer.WriteVectorised(append([]*buf.Buffer{buffer}, buffers...))
}
return c.writer.WriteVectorised(buffers)
}
func (c *Conn) ReaderReplaceable() bool {
return c.responseRead
}
func (c *Conn) WriterReplaceable() bool {
return c.requestWritten
}
func (c *Conn) NeedHandshake() bool { func (c *Conn) NeedHandshake() bool {
return !c.requestWritten return !c.requestWritten
} }
func (c *Conn) FrontHeadroom() int { func (c *Conn) Read(b []byte) (n int, err error) {
if c.requestWritten { if !c.responseRead {
return 0 err = ReadResponse(c.Conn)
if err != nil {
return
}
c.responseRead = true
} }
return RequestLen(c.request) return c.protocolConn.Read(b)
}
func (c *Conn) Write(b []byte) (n int, err error) {
if !c.requestWritten {
request := Request{c.key, c.command, c.destination, c.flow}
if c.protocolConn != nil {
err = WriteRequest(c.Conn, request, nil)
} else {
err = WriteRequest(c.Conn, request, b)
}
if err == nil {
n = len(b)
}
c.requestWritten = true
if c.protocolConn == nil {
return
}
}
return c.protocolConn.Write(b)
} }
func (c *Conn) Upstream() any { func (c *Conn) Upstream() any {
return c.ExtendedConn return c.Conn
} }
type PacketConn struct { type PacketConn struct {
@@ -264,10 +214,6 @@ func (c *PacketConn) FrontHeadroom() int {
return 2 return 2
} }
func (c *PacketConn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *PacketConn) Upstream() any { func (c *PacketConn) Upstream() any {
return c.Conn return c.Conn
} }

View File

@@ -11,12 +11,10 @@ var (
tlsClientHandShakeStart = []byte{0x16, 0x03} tlsClientHandShakeStart = []byte{0x16, 0x03}
tlsServerHandShakeStart = []byte{0x16, 0x03, 0x03} tlsServerHandShakeStart = []byte{0x16, 0x03, 0x03}
tlsApplicationDataStart = []byte{0x17, 0x03, 0x03} tlsApplicationDataStart = []byte{0x17, 0x03, 0x03}
)
const ( commandPaddingContinue byte = 0
commandPaddingContinue byte = iota commandPaddingEnd byte = 1
commandPaddingEnd commandPaddingDirect byte = 2
commandPaddingDirect
) )
var tls13CipherSuiteDic = map[uint16]string{ var tls13CipherSuiteDic = map[uint16]string{

View File

@@ -128,7 +128,7 @@ func WriteRequest(writer io.Writer, request Request, payload []byte) error {
requestLen += 1 // protobuf length requestLen += 1 // protobuf length
var addonsLen int var addonsLen int
if request.Flow != "" { if request.Command == vmess.CommandTCP && request.Flow != "" {
addonsLen += 1 // protobuf header addonsLen += 1 // protobuf header
addonsLen += UvarintLen(uint64(len(request.Flow))) addonsLen += UvarintLen(uint64(len(request.Flow)))
addonsLen += len(request.Flow) addonsLen += len(request.Flow)
@@ -165,62 +165,6 @@ func WriteRequest(writer io.Writer, request Request, payload []byte) error {
return common.Error(writer.Write(buffer.Bytes())) return common.Error(writer.Write(buffer.Bytes()))
} }
func EncodeRequest(request Request, buffer *buf.Buffer) {
var requestLen int
requestLen += 1 // version
requestLen += 16 // uuid
requestLen += 1 // protobuf length
var addonsLen int
if request.Flow != "" {
addonsLen += 1 // protobuf header
addonsLen += UvarintLen(uint64(len(request.Flow)))
addonsLen += len(request.Flow)
requestLen += addonsLen
}
requestLen += 1 // command
if request.Command != vmess.CommandMux {
requestLen += vmess.AddressSerializer.AddrPortLen(request.Destination)
}
common.Must(
buffer.WriteByte(Version),
common.Error(buffer.Write(request.UUID[:])),
buffer.WriteByte(byte(addonsLen)),
)
if addonsLen > 0 {
common.Must(buffer.WriteByte(10))
binary.PutUvarint(buffer.Extend(UvarintLen(uint64(len(request.Flow)))), uint64(len(request.Flow)))
common.Must(common.Error(buffer.Write([]byte(request.Flow))))
}
common.Must(
buffer.WriteByte(request.Command),
)
if request.Command != vmess.CommandMux {
common.Must(vmess.AddressSerializer.WriteAddrPort(buffer, request.Destination))
}
}
func RequestLen(request Request) int {
var requestLen int
requestLen += 1 // version
requestLen += 16 // uuid
requestLen += 1 // protobuf length
var addonsLen int
if request.Flow != "" {
addonsLen += 1 // protobuf header
addonsLen += UvarintLen(uint64(len(request.Flow)))
addonsLen += len(request.Flow)
requestLen += addonsLen
}
requestLen += 1 // command
if request.Command != vmess.CommandMux {
requestLen += vmess.AddressSerializer.AddrPortLen(request.Destination)
}
return requestLen
}
func WritePacketRequest(writer io.Writer, request Request, payload []byte) error { func WritePacketRequest(writer io.Writer, request Request, payload []byte) error {
var requestLen int var requestLen int
requestLen += 1 // version requestLen += 1 // version

View File

@@ -10,6 +10,7 @@ import (
"github.com/sagernet/sing/common/auth" "github.com/sagernet/sing/common/auth"
"github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio" "github.com/sagernet/sing/common/bufio"
"github.com/sagernet/sing/common/bufio/deadline"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger" "github.com/sagernet/sing/common/logger"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
@@ -68,32 +69,30 @@ func (s *Service[T]) NewConnection(ctx context.Context, conn net.Conn, metadata
metadata.Destination = request.Destination metadata.Destination = request.Destination
userFlow := s.userFlow[user] userFlow := s.userFlow[user]
if request.Flow == FlowVision && request.Command == vmess.NetworkUDP {
return E.New(FlowVision, " flow does not support UDP") var responseWriter io.Writer
} else if request.Flow != userFlow { if request.Command == vmess.CommandTCP {
return E.New("flow mismatch: expected ", flowName(userFlow), ", but got ", flowName(request.Flow)) if request.Flow != userFlow {
return E.New("flow mismatch: expected ", flowName(userFlow), ", but got ", flowName(request.Flow))
}
switch userFlow {
case "":
case FlowVision:
responseWriter = conn
conn, err = NewVisionConn(conn, request.UUID, s.logger)
if err != nil {
return E.Cause(err, "initialize vision")
}
}
} }
if request.Command == vmess.CommandUDP {
return s.handler.NewPacketConnection(ctx, &serverPacketConn{ExtendedConn: bufio.NewExtendedConn(conn), destination: request.Destination}, metadata)
}
responseConn := &serverConn{ExtendedConn: bufio.NewExtendedConn(conn), writer: bufio.NewVectorisedWriter(conn)}
switch userFlow {
case FlowVision:
conn, err = NewVisionConn(responseConn, conn, request.UUID, s.logger)
if err != nil {
return E.Cause(err, "initialize vision")
}
case "":
conn = responseConn
default:
return E.New("unknown flow: ", userFlow)
}
switch request.Command { switch request.Command {
case vmess.CommandTCP: case vmess.CommandTCP:
return s.handler.NewConnection(ctx, conn, metadata) return s.handler.NewConnection(ctx, &serverConn{Conn: conn, responseWriter: responseWriter}, metadata)
case vmess.CommandUDP:
return s.handler.NewPacketConnection(ctx, deadline.NewPacketConn(&serverPacketConn{ExtendedConn: bufio.NewExtendedConn(conn), destination: request.Destination}), metadata)
case vmess.CommandMux: case vmess.CommandMux:
return vmess.HandleMuxConnection(ctx, conn, s.handler) return vmess.HandleMuxConnection(ctx, &serverConn{Conn: conn, responseWriter: responseWriter}, s.handler)
default: default:
return E.New("unknown command: ", request.Command) return E.New("unknown command: ", request.Command)
} }
@@ -106,70 +105,34 @@ func flowName(value string) string {
return value return value
} }
var _ N.VectorisedWriter = (*serverConn)(nil)
type serverConn struct { type serverConn struct {
N.ExtendedConn net.Conn
writer N.VectorisedWriter responseWriter io.Writer
responseWritten bool responseWritten bool
} }
func (c *serverConn) Read(b []byte) (n int, err error) { func (c *serverConn) Read(b []byte) (n int, err error) {
return c.ExtendedConn.Read(b) return c.Conn.Read(b)
} }
func (c *serverConn) Write(b []byte) (n int, err error) { func (c *serverConn) Write(b []byte) (n int, err error) {
if !c.responseWritten { if !c.responseWritten {
_, err = bufio.WriteVectorised(c.writer, [][]byte{{Version, 0}, b}) if c.responseWriter == nil {
if err == nil { _, err = bufio.WriteVectorised(bufio.NewVectorisedWriter(c.Conn), [][]byte{{Version, 0}, b})
n = len(b) if err == nil {
n = len(b)
}
c.responseWritten = true
return
} else {
_, err = c.responseWriter.Write([]byte{Version, 0})
if err != nil {
return
}
c.responseWritten = true
} }
c.responseWritten = true
return
} }
return c.ExtendedConn.Write(b) return c.Conn.Write(b)
}
func (c *serverConn) WriteBuffer(buffer *buf.Buffer) error {
if !c.responseWritten {
header := buffer.ExtendHeader(2)
header[0] = Version
header[1] = 0
c.responseWritten = true
}
return c.ExtendedConn.WriteBuffer(buffer)
}
func (c *serverConn) WriteVectorised(buffers []*buf.Buffer) error {
if !c.responseWritten {
err := c.writer.WriteVectorised(append([]*buf.Buffer{buf.As([]byte{Version, 0})}, buffers...))
c.responseWritten = true
return err
}
return c.writer.WriteVectorised(buffers)
}
func (c *serverConn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *serverConn) FrontHeadroom() int {
if c.responseWritten {
return 0
}
return 2
}
func (c *serverConn) ReaderReplaceable() bool {
return true
}
func (c *serverConn) WriterReplaceable() bool {
return c.responseWritten
}
func (c *serverConn) Upstream() any {
return c.ExtendedConn
} }
type serverPacketConn struct { type serverPacketConn struct {

View File

@@ -56,12 +56,12 @@ type VisionConn struct {
withinPaddingBuffers bool withinPaddingBuffers bool
remainingContent int remainingContent int
remainingPadding int remainingPadding int
currentCommand byte currentCommand int
directRead bool directRead bool
remainingReader io.Reader remainingReader io.Reader
} }
func NewVisionConn(conn net.Conn, tlsConn net.Conn, userUUID [16]byte, logger logger.Logger) (*VisionConn, error) { func NewVisionConn(conn net.Conn, userUUID [16]byte, logger logger.Logger) (*VisionConn, error) {
var ( var (
loaded bool loaded bool
reflectType reflect.Type reflectType reflect.Type
@@ -69,7 +69,7 @@ func NewVisionConn(conn net.Conn, tlsConn net.Conn, userUUID [16]byte, logger lo
netConn net.Conn netConn net.Conn
) )
for _, tlsCreator := range tlsRegistry { for _, tlsCreator := range tlsRegistry {
loaded, netConn, reflectType, reflectPointer = tlsCreator(tlsConn) loaded, netConn, reflectType, reflectPointer = tlsCreator(conn)
if loaded { if loaded {
break break
} }
@@ -103,7 +103,6 @@ func (c *VisionConn) Read(p []byte) (n int, err error) {
if c.remainingReader != nil { if c.remainingReader != nil {
n, err = c.remainingReader.Read(p) n, err = c.remainingReader.Read(p)
if err == io.EOF { if err == io.EOF {
err = nil
c.remainingReader = nil c.remainingReader = nil
} }
if n > 0 { if n > 0 {
@@ -114,7 +113,6 @@ func (c *VisionConn) Read(p []byte) (n int, err error) {
return c.netConn.Read(p) return c.netConn.Read(p)
} }
var bufferBytes []byte var bufferBytes []byte
var chunkBuffer *buf.Buffer
if len(p) > xrayChunkSize { if len(p) > xrayChunkSize {
n, err = c.Conn.Read(p) n, err = c.Conn.Read(p)
if err != nil { if err != nil {
@@ -122,26 +120,21 @@ func (c *VisionConn) Read(p []byte) (n int, err error) {
} }
bufferBytes = p[:n] bufferBytes = p[:n]
} else { } else {
chunkBuffer, err = c.reader.ReadChunk() buffer, err := c.reader.ReadChunk()
if err != nil { if err != nil {
return 0, err return 0, err
} }
bufferBytes = chunkBuffer.Bytes() defer buffer.FullReset()
bufferBytes = buffer.Bytes()
} }
if c.withinPaddingBuffers || c.numberOfPacketToFilter > 0 { if c.withinPaddingBuffers || c.numberOfPacketToFilter > 0 {
buffers := c.unPadding(bufferBytes) buffers := c.unPadding(bufferBytes)
if chunkBuffer != nil {
buffers = common.Map(buffers, func(it *buf.Buffer) *buf.Buffer {
return it.ToOwned()
})
chunkBuffer.FullReset()
}
if c.remainingContent == 0 && c.remainingPadding == 0 { if c.remainingContent == 0 && c.remainingPadding == 0 {
if c.currentCommand == commandPaddingEnd { if c.currentCommand == 1 {
c.withinPaddingBuffers = false c.withinPaddingBuffers = false
c.remainingContent = -1 c.remainingContent = -1
c.remainingPadding = -1 c.remainingPadding = -1
} else if c.currentCommand == commandPaddingDirect { } else if c.currentCommand == 2 {
c.withinPaddingBuffers = false c.withinPaddingBuffers = false
c.directRead = true c.directRead = true
@@ -149,17 +142,17 @@ func (c *VisionConn) Read(p []byte) (n int, err error) {
if err != nil { if err != nil {
return 0, err return 0, err
} }
buffers = append(buffers, buf.As(inputBuffer)) buffers = append(buffers, inputBuffer)
rawInputBuffer, err := io.ReadAll(c.rawInput) rawInputBuffer, err := io.ReadAll(c.rawInput)
if err != nil { if err != nil {
return 0, err return 0, err
} }
buffers = append(buffers, buf.As(rawInputBuffer)) buffers = append(buffers, rawInputBuffer)
c.logger.Trace("XtlsRead readV") c.logger.Trace("XtlsRead readV")
} else if c.currentCommand == commandPaddingContinue { } else if c.currentCommand == 0 {
c.withinPaddingBuffers = true c.withinPaddingBuffers = true
} else { } else {
return 0, E.New("unknown command ", c.currentCommand) return 0, E.New("unknown command ", c.currentCommand)
@@ -170,18 +163,14 @@ func (c *VisionConn) Read(p []byte) (n int, err error) {
c.withinPaddingBuffers = false c.withinPaddingBuffers = false
} }
if c.numberOfPacketToFilter > 0 { if c.numberOfPacketToFilter > 0 {
c.filterTLS(buf.ToSliceMulti(buffers)) c.filterTLS(buffers)
} }
c.remainingReader = io.MultiReader(common.Map(buffers, func(it *buf.Buffer) io.Reader { return it })...) c.remainingReader = io.MultiReader(common.Map(buffers, func(it []byte) io.Reader { return bytes.NewReader(it) })...)
return c.Read(p) return c.Read(p)
} else { } else {
if c.numberOfPacketToFilter > 0 { if c.numberOfPacketToFilter > 0 {
c.filterTLS([][]byte{bufferBytes}) c.filterTLS([][]byte{bufferBytes})
} }
if chunkBuffer != nil {
n = copy(p, bufferBytes)
chunkBuffer.Advance(n)
}
return return
} }
} }
@@ -321,7 +310,7 @@ func (c *VisionConn) padding(buffer *buf.Buffer, command byte) *buf.Buffer {
return newBuffer return newBuffer
} }
func (c *VisionConn) unPadding(buffer []byte) []*buf.Buffer { func (c *VisionConn) unPadding(buffer []byte) [][]byte {
var bufferIndex int var bufferIndex int
if c.remainingContent == -1 && c.remainingPadding == -1 { if c.remainingContent == -1 && c.remainingPadding == -1 {
if len(buffer) >= 21 && bytes.Equal(c.userUUID[:], buffer[:16]) { if len(buffer) >= 21 && bytes.Equal(c.userUUID[:], buffer[:16]) {
@@ -332,17 +321,17 @@ func (c *VisionConn) unPadding(buffer []byte) []*buf.Buffer {
} }
} }
if c.remainingContent == -1 && c.remainingPadding == -1 { if c.remainingContent == -1 && c.remainingPadding == -1 {
return []*buf.Buffer{buf.As(buffer)} return [][]byte{buffer}
} }
var buffers []*buf.Buffer var buffers [][]byte
for bufferIndex < len(buffer) { for bufferIndex < len(buffer) {
if c.remainingContent <= 0 && c.remainingPadding <= 0 { if c.remainingContent <= 0 && c.remainingPadding <= 0 {
if c.currentCommand == 1 { if c.currentCommand == 1 {
buffers = append(buffers, buf.As(buffer[bufferIndex:])) buffers = append(buffers, buffer[bufferIndex:])
break break
} else { } else {
paddingInfo := buffer[bufferIndex : bufferIndex+5] paddingInfo := buffer[bufferIndex : bufferIndex+5]
c.currentCommand = paddingInfo[0] c.currentCommand = int(paddingInfo[0])
c.remainingContent = int(paddingInfo[1])<<8 | int(paddingInfo[2]) c.remainingContent = int(paddingInfo[1])<<8 | int(paddingInfo[2])
c.remainingPadding = int(paddingInfo[3])<<8 | int(paddingInfo[4]) c.remainingPadding = int(paddingInfo[3])<<8 | int(paddingInfo[4])
bufferIndex += 5 bufferIndex += 5
@@ -353,7 +342,7 @@ func (c *VisionConn) unPadding(buffer []byte) []*buf.Buffer {
if end > len(buffer)-bufferIndex { if end > len(buffer)-bufferIndex {
end = len(buffer) - bufferIndex end = len(buffer) - bufferIndex
} }
buffers = append(buffers, buf.As(buffer[bufferIndex:bufferIndex+end])) buffers = append(buffers, buffer[bufferIndex:bufferIndex+end])
c.remainingContent -= end c.remainingContent -= end
bufferIndex += end bufferIndex += end
} else { } else {
@@ -371,10 +360,6 @@ func (c *VisionConn) unPadding(buffer []byte) []*buf.Buffer {
return buffers return buffers
} }
func (c *VisionConn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *VisionConn) Upstream() any { func (c *VisionConn) Upstream() any {
return c.Conn return c.Conn
} }

View File

@@ -101,7 +101,7 @@ func (c *ClientBind) Open(port uint16) (fns []conn.ReceiveFunc, actualPort uint1
return []conn.ReceiveFunc{c.receive}, 0, nil return []conn.ReceiveFunc{c.receive}, 0, nil
} }
func (c *ClientBind) receive(packets [][]byte, sizes []int, eps []conn.Endpoint) (count int, err error) { func (c *ClientBind) receive(b []byte) (n int, ep conn.Endpoint, err error) {
udpConn, err := c.connect() udpConn, err := c.connect()
if err != nil { if err != nil {
select { select {
@@ -113,26 +113,22 @@ func (c *ClientBind) receive(packets [][]byte, sizes []int, eps []conn.Endpoint)
err = nil err = nil
return return
} }
n, addr, err := udpConn.ReadFrom(packets[0]) n, addr, err := udpConn.ReadFrom(b)
if err != nil { if err != nil {
udpConn.Close() udpConn.Close()
select { select {
case <-c.done: case <-c.done:
default: default:
c.errorHandler.NewError(context.Background(), E.Cause(err, "read packet")) c.errorHandler.NewError(context.Background(), E.Cause(err, "read packet"))
err = nil
} }
return return
} }
sizes[0] = n
if n > 3 { if n > 3 {
b := packets[0]
b[1] = 0 b[1] = 0
b[2] = 0 b[2] = 0
b[3] = 0 b[3] = 0
} }
eps[0] = Endpoint(M.SocksaddrFromNet(addr)) ep = Endpoint(M.SocksaddrFromNet(addr))
count = 1
return return
} }
@@ -159,39 +155,32 @@ func (c *ClientBind) SetMark(mark uint32) error {
return nil return nil
} }
func (c *ClientBind) Send(bufs [][]byte, ep conn.Endpoint) error { func (c *ClientBind) Send(b []byte, ep conn.Endpoint) error {
udpConn, err := c.connect() udpConn, err := c.connect()
if err != nil { if err != nil {
return err return err
} }
destination := M.Socksaddr(ep.(Endpoint)) destination := M.Socksaddr(ep.(Endpoint))
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]
b[2] = reserved[1]
b[3] = reserved[2]
}
_, err = udpConn.WriteTo(b, destination)
if err != nil {
udpConn.Close()
return err
} }
b[1] = reserved[0]
b[2] = reserved[1]
b[3] = reserved[2]
} }
return nil _, err = udpConn.WriteTo(b, destination)
if err != nil {
udpConn.Close()
}
return err
} }
func (c *ClientBind) ParseEndpoint(s string) (conn.Endpoint, error) { func (c *ClientBind) ParseEndpoint(s string) (conn.Endpoint, error) {
return Endpoint(M.ParseSocksaddr(s)), nil return Endpoint(M.ParseSocksaddr(s)), nil
} }
func (c *ClientBind) BatchSize() int {
return 1
}
type wireConn struct { type wireConn struct {
net.PacketConn net.PacketConn
access sync.Mutex access sync.Mutex

View File

@@ -26,24 +26,23 @@ func NewNATDevice(upstream Device, ipRewrite bool) NatDevice {
return wrapper return wrapper
} }
func (d *natDeviceWrapper) Read(bufs [][]byte, sizes []int, offset int) (count int, err error) { func (d *natDeviceWrapper) Read(p []byte, offset int) (int, error) {
select { select {
case packet := <-d.outbound: case packet := <-d.outbound:
defer packet.Release() defer packet.Release()
sizes[0] = copy(bufs[0][offset:], packet.Bytes()) return copy(p[offset:], packet.Bytes()), nil
return 1, nil
default: default:
} }
return d.Device.Read(bufs, sizes, offset) return d.Device.Read(p, offset)
} }
func (d *natDeviceWrapper) Write(bufs [][]byte, offset int) (count int, err error) { func (d *natDeviceWrapper) Write(p []byte, offset int) (int, error) {
packet := bufs[0][offset:] packet := p[offset:]
handled, err := d.mapping.WritePacket(packet) handled, err := d.mapping.WritePacket(packet)
if handled { if handled {
return 1, err return len(packet), err
} }
return d.Device.Write(bufs, offset) return d.Device.Write(p, offset)
} }
func (d *natDeviceWrapper) CreateDestination(session tun.RouteSession, conn tun.RouteContext) tun.DirectDestination { func (d *natDeviceWrapper) CreateDestination(session tun.RouteSession, conn tun.RouteContext) tun.DirectDestination {

View File

@@ -9,7 +9,7 @@ import (
"gvisor.dev/gvisor/pkg/tcpip/stack" "gvisor.dev/gvisor/pkg/tcpip/stack"
) )
func (d *natDestinationWrapper) WritePacketBuffer(buffer stack.PacketBufferPtr) error { func (d *natDestinationWrapper) WritePacketBuffer(buffer *stack.PacketBuffer) error {
defer buffer.DecRef() defer buffer.DecRef()
if d.device.writer != nil { if d.device.writer != nil {
d.device.writer.RewritePacketBuffer(buffer) d.device.writer.RewritePacketBuffer(buffer)

View File

@@ -35,7 +35,7 @@ type StackDevice struct {
stack *stack.Stack stack *stack.Stack
mtu uint32 mtu uint32
events chan wgTun.Event events chan wgTun.Event
outbound chan stack.PacketBufferPtr outbound chan *stack.PacketBuffer
packetOutbound chan *buf.Buffer packetOutbound chan *buf.Buffer
done chan struct{} done chan struct{}
dispatcher stack.NetworkDispatcher dispatcher stack.NetworkDispatcher
@@ -55,7 +55,7 @@ func NewStackDevice(localAddresses []netip.Prefix, mtu uint32, ipRewrite bool) (
stack: ipStack, stack: ipStack,
mtu: mtu, mtu: mtu,
events: make(chan wgTun.Event, 1), events: make(chan wgTun.Event, 1),
outbound: make(chan stack.PacketBufferPtr, 256), outbound: make(chan *stack.PacketBuffer, 256),
packetOutbound: make(chan *buf.Buffer, 256), packetOutbound: make(chan *buf.Buffer, 256),
done: make(chan struct{}), done: make(chan struct{}),
mapping: tun.NewNatMapping(ipRewrite), mapping: tun.NewNatMapping(ipRewrite),
@@ -171,60 +171,49 @@ func (w *StackDevice) File() *os.File {
return nil return nil
} }
func (w *StackDevice) Read(bufs [][]byte, sizes []int, offset int) (count int, err error) { func (w *StackDevice) Read(p []byte, offset int) (n int, err error) {
select { select {
case packetBuffer, ok := <-w.outbound: case packetBuffer, ok := <-w.outbound:
if !ok { if !ok {
return 0, os.ErrClosed return 0, os.ErrClosed
} }
defer packetBuffer.DecRef() defer packetBuffer.DecRef()
p := bufs[0]
p = p[offset:] p = p[offset:]
n := 0
for _, slice := range packetBuffer.AsSlices() { for _, slice := range packetBuffer.AsSlices() {
n += copy(p[n:], slice) n += copy(p[n:], slice)
} }
sizes[0] = n
count = 1
return return
case packet := <-w.packetOutbound: case packet := <-w.packetOutbound:
defer packet.Release() defer packet.Release()
sizes[0] = copy(bufs[0][offset:], packet.Bytes()) n = copy(p[offset:], packet.Bytes())
count = 1
return return
case <-w.done: case <-w.done:
return 0, os.ErrClosed return 0, os.ErrClosed
} }
} }
func (w *StackDevice) Write(bufs [][]byte, offset int) (count int, err error) { func (w *StackDevice) Write(p []byte, offset int) (n int, err error) {
for _, b := range bufs { p = p[offset:]
b = b[offset:] if len(p) == 0 {
if len(b) == 0 { return
continue
}
handled, err := w.mapping.WritePacket(b)
if handled {
count++
if err != nil {
return count, err
}
continue
}
var networkProtocol tcpip.NetworkProtocolNumber
switch header.IPVersion(b) {
case header.IPv4Version:
networkProtocol = header.IPv4ProtocolNumber
case header.IPv6Version:
networkProtocol = header.IPv6ProtocolNumber
}
packetBuffer := stack.NewPacketBuffer(stack.PacketBufferOptions{
Payload: bufferv2.MakeWithData(b),
})
w.dispatcher.DeliverNetworkPacket(networkProtocol, packetBuffer)
packetBuffer.DecRef()
count++
} }
handled, err := w.mapping.WritePacket(p)
if handled {
return len(p), err
}
var networkProtocol tcpip.NetworkProtocolNumber
switch header.IPVersion(p) {
case header.IPv4Version:
networkProtocol = header.IPv4ProtocolNumber
case header.IPv6Version:
networkProtocol = header.IPv6ProtocolNumber
}
packetBuffer := stack.NewPacketBuffer(stack.PacketBufferOptions{
Payload: bufferv2.MakeWithData(p),
})
defer packetBuffer.DecRef()
w.dispatcher.DeliverNetworkPacket(networkProtocol, packetBuffer)
n = len(p)
return return
} }
@@ -240,7 +229,7 @@ func (w *StackDevice) Name() (string, error) {
return "sing-box", nil return "sing-box", nil
} }
func (w *StackDevice) Events() <-chan wgTun.Event { func (w *StackDevice) Events() chan wgTun.Event {
return w.events return w.events
} }
@@ -259,10 +248,6 @@ func (w *StackDevice) Close() error {
return nil return nil
} }
func (w *StackDevice) BatchSize() int {
return 1
}
func (w *StackDevice) CreateDestination(session tun.RouteSession, conn tun.RouteContext) tun.DirectDestination { func (w *StackDevice) CreateDestination(session tun.RouteSession, conn tun.RouteContext) tun.DirectDestination {
w.mapping.CreateSession(session, conn) w.mapping.CreateSession(session, conn)
return &stackNatDestination{ return &stackNatDestination{
@@ -284,7 +269,7 @@ func (d *stackNatDestination) WritePacket(buffer *buf.Buffer) error {
return nil return nil
} }
func (d *stackNatDestination) WritePacketBuffer(buffer stack.PacketBufferPtr) error { func (d *stackNatDestination) WritePacketBuffer(buffer *stack.PacketBuffer) error {
if d.device.writer != nil { if d.device.writer != nil {
d.device.writer.RewritePacketBuffer(buffer) d.device.writer.RewritePacketBuffer(buffer)
} }
@@ -336,7 +321,7 @@ func (ep *wireEndpoint) ARPHardwareType() header.ARPHardwareType {
return header.ARPHardwareNone return header.ARPHardwareNone
} }
func (ep *wireEndpoint) AddHeader(buffer stack.PacketBufferPtr) { func (ep *wireEndpoint) AddHeader(buffer *stack.PacketBuffer) {
} }
func (ep *wireEndpoint) WritePackets(list stack.PacketBufferList) (int, tcpip.Error) { func (ep *wireEndpoint) WritePackets(list stack.PacketBufferList) (int, tcpip.Error) {

Some files were not shown because too many files have changed in this diff Show More