mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-12 18:17:18 +10:00
Compare commits
36 Commits
v1.3-beta1
...
v1.3-beta5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32875e7cbc | ||
|
|
3de56620ce | ||
|
|
3651605d3b | ||
|
|
7d1174e545 | ||
|
|
bd9e6e5cd9 | ||
|
|
ed37cb858b | ||
|
|
62bcf22c26 | ||
|
|
84bd997742 | ||
|
|
a548e45ad7 | ||
|
|
5c1de2bb06 | ||
|
|
e5f0add1ab | ||
|
|
70e47df295 | ||
|
|
f20642d6fd | ||
|
|
73fa926b48 | ||
|
|
5d9dce8078 | ||
|
|
e20e2d57c9 | ||
|
|
25f31890ed | ||
|
|
194b36b987 | ||
|
|
1e39196bc9 | ||
|
|
da82a41697 | ||
|
|
aceb82a75e | ||
|
|
f2749bc29d | ||
|
|
55afaa87da | ||
|
|
d77940ab39 | ||
|
|
1eea446e45 | ||
|
|
19c6241e10 | ||
|
|
b290d0ed32 | ||
|
|
2afe662646 | ||
|
|
107a9a3b51 | ||
|
|
3d0c64f523 | ||
|
|
422ca34ac2 | ||
|
|
6d63f9255f | ||
|
|
6f2cc9761d | ||
|
|
b484d9bca6 | ||
|
|
58c4fd745a | ||
|
|
7d1e6affb3 |
2
Makefile
2
Makefile
@@ -22,7 +22,7 @@ install:
|
||||
fmt:
|
||||
@gofumpt -l -w .
|
||||
@gofmt -s -w .
|
||||
@gci write --custom-order -s standard -s "prefix(github.com/sagernet/)" -s "default" .
|
||||
@gci write --custom-order -s "standard,prefix(github.com/sagernet/),default" .
|
||||
|
||||
fmt_install:
|
||||
go install -v mvdan.cc/gofumpt@latest
|
||||
|
||||
@@ -8,10 +8,6 @@ The universal proxy platform.
|
||||
|
||||
https://sing-box.sagernet.org
|
||||
|
||||
## Support
|
||||
|
||||
https://community.sagernet.org/c/sing-box/
|
||||
|
||||
## License
|
||||
|
||||
```
|
||||
|
||||
@@ -37,7 +37,6 @@ type Router interface {
|
||||
LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error)
|
||||
|
||||
InterfaceFinder() control.InterfaceFinder
|
||||
UpdateInterfaces() error
|
||||
DefaultInterface() string
|
||||
AutoDetectInterface() bool
|
||||
AutoDetectInterfaceFunc() control.Func
|
||||
@@ -56,8 +55,6 @@ type Router interface {
|
||||
|
||||
V2RayServer() V2RayServer
|
||||
SetV2RayServer(server V2RayServer)
|
||||
|
||||
ResetNetwork() error
|
||||
}
|
||||
|
||||
type routerContextKey struct{}
|
||||
|
||||
9
box.go
9
box.go
@@ -62,7 +62,6 @@ func New(options Options) (*Box, error) {
|
||||
defaultLogWriter = io.Discard
|
||||
}
|
||||
logFactory, err := log.New(log.Options{
|
||||
Context: ctx,
|
||||
Options: common.PtrValueOrDefault(options.Log),
|
||||
Observable: needClashAPI,
|
||||
DefaultWriter: defaultLogWriter,
|
||||
@@ -134,16 +133,10 @@ func New(options Options) (*Box, error) {
|
||||
if err != nil {
|
||||
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)
|
||||
postServices := make(map[string]adapter.Service)
|
||||
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 {
|
||||
return nil, E.Cause(err, "create clash api server")
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
_ "github.com/sagernet/gomobile/event/key"
|
||||
"github.com/sagernet/sing-box/cmd/internal/build_shared"
|
||||
@@ -39,23 +38,18 @@ func main() {
|
||||
var (
|
||||
sharedFlags []string
|
||||
debugFlags []string
|
||||
sharedTags []string
|
||||
debugTags []string
|
||||
)
|
||||
|
||||
func init() {
|
||||
sharedFlags = append(sharedFlags, "-trimpath")
|
||||
sharedFlags = append(sharedFlags, "-ldflags")
|
||||
|
||||
currentTag, err := build_shared.ReadTag()
|
||||
if err != nil {
|
||||
currentTag = "unknown"
|
||||
}
|
||||
sharedFlags = append(sharedFlags, "-X github.com/sagernet/sing-box/constant.Version="+currentTag+" -s -w -buildid=")
|
||||
debugFlags = append(debugFlags, "-X github.com/sagernet/sing-box/constant.Version="+currentTag)
|
||||
|
||||
sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_utls", "with_clash_api")
|
||||
sharedTags = append(sharedTags, "test_sing_shadowsocks2")
|
||||
debugTags = append(debugTags, "debug")
|
||||
}
|
||||
|
||||
func buildAndroid() {
|
||||
@@ -76,9 +70,9 @@ func buildAndroid() {
|
||||
|
||||
args = append(args, "-tags")
|
||||
if !debugEnabled {
|
||||
args = append(args, strings.Join(sharedTags, ","))
|
||||
args = append(args, "with_gvisor,with_quic,with_wireguard,with_utls,with_clash_api")
|
||||
} else {
|
||||
args = append(args, strings.Join(append(sharedTags, debugTags...), ","))
|
||||
args = append(args, "with_gvisor,with_quic,with_wireguard,with_utls,with_clash_api,debug")
|
||||
}
|
||||
args = append(args, "./experimental/libbox")
|
||||
|
||||
@@ -115,12 +109,11 @@ func buildiOS() {
|
||||
args = append(args, debugFlags...)
|
||||
}
|
||||
|
||||
tags := append(sharedTags, "with_low_memory", "with_conntrack")
|
||||
args = append(args, "-tags")
|
||||
if !debugEnabled {
|
||||
args = append(args, strings.Join(tags, ","))
|
||||
args = append(args, "with_gvisor,with_quic,with_utls,with_clash_api,with_low_memory,with_conntrack")
|
||||
} else {
|
||||
args = append(args, strings.Join(append(tags, debugTags...), ","))
|
||||
args = append(args, "with_gvisor,with_quic,with_utls,with_clash_api,with_low_memory,with_conntrack,debug")
|
||||
}
|
||||
args = append(args, "./experimental/libbox")
|
||||
|
||||
|
||||
44
cmd/sing-box/debug.go
Normal file
44
cmd/sing-box/debug.go
Normal 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)
|
||||
}
|
||||
}()
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
package box
|
||||
//go:build debug
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
@@ -1,6 +1,6 @@
|
||||
//go:build !linux
|
||||
//go:build debug && !linux
|
||||
|
||||
package box
|
||||
package main
|
||||
|
||||
func rusageMaxRSS() float64 {
|
||||
return -1
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build go1.20 && !go1.21
|
||||
//go:build go1.19 && !go1.20
|
||||
|
||||
package badtls
|
||||
|
||||
@@ -14,60 +14,39 @@ import (
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
aTLS "github.com/sagernet/sing/common/tls"
|
||||
)
|
||||
|
||||
type Conn struct {
|
||||
*tls.Conn
|
||||
writer N.ExtendedWriter
|
||||
isHandshakeComplete *atomic.Bool
|
||||
activeCall *atomic.Int32
|
||||
closeNotifySent *bool
|
||||
version *uint16
|
||||
rand io.Reader
|
||||
halfAccess *sync.Mutex
|
||||
halfError *error
|
||||
cipher cipher.AEAD
|
||||
explicitNonceLen int
|
||||
halfPtr uintptr
|
||||
halfSeq []byte
|
||||
halfScratchBuf []byte
|
||||
writer N.ExtendedWriter
|
||||
activeCall *int32
|
||||
closeNotifySent *bool
|
||||
version *uint16
|
||||
rand io.Reader
|
||||
halfAccess *sync.Mutex
|
||||
halfError *error
|
||||
cipher cipher.AEAD
|
||||
explicitNonceLen int
|
||||
halfPtr uintptr
|
||||
halfSeq []byte
|
||||
halfScratchBuf []byte
|
||||
}
|
||||
|
||||
func TryCreate(conn aTLS.Conn) aTLS.Conn {
|
||||
tlsConn, ok := conn.(*tls.Conn)
|
||||
if !ok {
|
||||
return conn
|
||||
}
|
||||
badConn, err := Create(tlsConn)
|
||||
if err != nil {
|
||||
log.Warn("initialize badtls: ", err)
|
||||
return conn
|
||||
}
|
||||
return badConn
|
||||
}
|
||||
|
||||
func Create(conn *tls.Conn) (aTLS.Conn, error) {
|
||||
rawConn := reflect.Indirect(reflect.ValueOf(conn))
|
||||
rawIsHandshakeComplete := rawConn.FieldByName("isHandshakeComplete")
|
||||
if !rawIsHandshakeComplete.IsValid() || rawIsHandshakeComplete.Kind() != reflect.Struct {
|
||||
return nil, E.New("badtls: invalid isHandshakeComplete")
|
||||
}
|
||||
isHandshakeComplete := (*atomic.Bool)(unsafe.Pointer(rawIsHandshakeComplete.UnsafeAddr()))
|
||||
if !isHandshakeComplete.Load() {
|
||||
func Create(conn *tls.Conn) (TLSConn, error) {
|
||||
if !handshakeComplete(conn) {
|
||||
return nil, E.New("handshake not finished")
|
||||
}
|
||||
rawConn := reflect.Indirect(reflect.ValueOf(conn))
|
||||
rawActiveCall := rawConn.FieldByName("activeCall")
|
||||
if !rawActiveCall.IsValid() || rawActiveCall.Kind() != reflect.Struct {
|
||||
if !rawActiveCall.IsValid() || rawActiveCall.Kind() != reflect.Int32 {
|
||||
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")
|
||||
if !rawHalfConn.IsValid() || rawHalfConn.Kind() != reflect.Struct {
|
||||
return nil, E.New("badtls: invalid half conn")
|
||||
@@ -129,20 +108,19 @@ func Create(conn *tls.Conn) (aTLS.Conn, error) {
|
||||
}
|
||||
halfScratchBuf := rawHalfScratchBuf.Bytes()
|
||||
return &Conn{
|
||||
Conn: conn,
|
||||
writer: bufio.NewExtendedWriter(conn.NetConn()),
|
||||
isHandshakeComplete: isHandshakeComplete,
|
||||
activeCall: activeCall,
|
||||
closeNotifySent: closeNotifySent,
|
||||
version: version,
|
||||
halfAccess: halfAccess,
|
||||
halfError: halfError,
|
||||
cipher: aeadCipher,
|
||||
explicitNonceLen: explicitNonceLen,
|
||||
rand: randReader,
|
||||
halfPtr: rawHalfConn.UnsafeAddr(),
|
||||
halfSeq: halfSeq,
|
||||
halfScratchBuf: halfScratchBuf,
|
||||
Conn: conn,
|
||||
writer: bufio.NewExtendedWriter(conn.NetConn()),
|
||||
activeCall: activeCall,
|
||||
closeNotifySent: closeNotifySent,
|
||||
version: version,
|
||||
halfAccess: halfAccess,
|
||||
halfError: halfError,
|
||||
cipher: aeadCipher,
|
||||
explicitNonceLen: explicitNonceLen,
|
||||
rand: randReader,
|
||||
halfPtr: rawHalfConn.UnsafeAddr(),
|
||||
halfSeq: halfSeq,
|
||||
halfScratchBuf: halfScratchBuf,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -152,15 +130,15 @@ func (c *Conn) WriteBuffer(buffer *buf.Buffer) error {
|
||||
return common.Error(c.Write(buffer.Bytes()))
|
||||
}
|
||||
for {
|
||||
x := c.activeCall.Load()
|
||||
x := atomic.LoadInt32(c.activeCall)
|
||||
if x&1 != 0 {
|
||||
return net.ErrClosed
|
||||
}
|
||||
if c.activeCall.CompareAndSwap(x, x+2) {
|
||||
if atomic.CompareAndSwapInt32(c.activeCall, x, x+2) {
|
||||
break
|
||||
}
|
||||
}
|
||||
defer c.activeCall.Add(-2)
|
||||
defer atomic.AddInt32(c.activeCall, -2)
|
||||
c.halfAccess.Lock()
|
||||
defer c.halfAccess.Unlock()
|
||||
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()))
|
||||
}
|
||||
incSeq(c.halfPtr)
|
||||
log.Trace("badtls write ", buffer.Len())
|
||||
return c.writer.WriteBuffer(buffer)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build !go1.19 || go1.21
|
||||
//go:build !go1.19 || go1.20
|
||||
|
||||
package badtls
|
||||
|
||||
|
||||
13
common/badtls/conn.go
Normal file
13
common/badtls/conn.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package badtls
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
)
|
||||
|
||||
type TLSConn interface {
|
||||
net.Conn
|
||||
HandshakeContext(ctx context.Context) error
|
||||
ConnectionState() tls.ConnectionState
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
//go:build go1.20 && !go.1.21
|
||||
//go:build go1.19 && !go.1.20
|
||||
|
||||
package badtls
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"reflect"
|
||||
_ "unsafe"
|
||||
)
|
||||
@@ -15,6 +16,9 @@ const (
|
||||
//go:linkname errShutdown crypto/tls.errShutdown
|
||||
var errShutdown error
|
||||
|
||||
//go:linkname handshakeComplete crypto/tls.(*Conn).handshakeComplete
|
||||
func handshakeComplete(conn *tls.Conn) bool
|
||||
|
||||
//go:linkname incSeq crypto/tls.(*halfConn).incSeq
|
||||
func incSeq(conn uintptr)
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
)
|
||||
|
||||
@@ -43,7 +42,7 @@ func (c *PacketConn) Close() error {
|
||||
}
|
||||
|
||||
func (c *PacketConn) Upstream() any {
|
||||
return bufio.NewPacketConn(c.PacketConn)
|
||||
return c.PacketConn
|
||||
}
|
||||
|
||||
func (c *PacketConn) ReaderReplaceable() bool {
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer/conntrack"
|
||||
"github.com/sagernet/sing-box/common/warning"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
@@ -16,6 +17,41 @@ import (
|
||||
"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 {
|
||||
dialer4 tfo.Dialer
|
||||
dialer6 tfo.Dialer
|
||||
@@ -30,6 +66,7 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
|
||||
var dialer net.Dialer
|
||||
var listener net.ListenConfig
|
||||
if options.BindInterface != "" {
|
||||
warnBindInterfaceOnUnsupportedPlatform.Check()
|
||||
bindFunc := control.BindToInterface(router.InterfaceFinder(), options.BindInterface, -1)
|
||||
dialer.Control = control.Append(dialer.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)
|
||||
}
|
||||
if options.RoutingMark != 0 {
|
||||
warnRoutingMarkOnUnsupportedPlatform.Check()
|
||||
dialer.Control = control.Append(dialer.Control, control.RoutingMark(options.RoutingMark))
|
||||
listener.Control = control.Append(listener.Control, control.RoutingMark(options.RoutingMark))
|
||||
} 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()))
|
||||
}
|
||||
if options.ReuseAddr {
|
||||
warnReuseAdderOnUnsupportedPlatform.Check()
|
||||
listener.Control = control.Append(listener.Control, control.ReuseAddr())
|
||||
}
|
||||
if options.ProtectPath != "" {
|
||||
warnProtectPathOnNonAndroid.Check()
|
||||
dialer.Control = control.Append(dialer.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 {
|
||||
dialer.Timeout = C.TCPTimeout
|
||||
}
|
||||
if options.TCPFastOpen {
|
||||
warnTFOOnUnsupportedPlatform.Check()
|
||||
}
|
||||
var udpFragment bool
|
||||
if options.UDPFragment != nil {
|
||||
udpFragment = *options.UDPFragment
|
||||
|
||||
@@ -1,21 +1,524 @@
|
||||
package mux
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-mux"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
"github.com/sagernet/sing/common/bufio/deadline"
|
||||
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/x/list"
|
||||
)
|
||||
|
||||
func NewClientWithOptions(dialer N.Dialer, options option.MultiplexOptions) (*Client, error) {
|
||||
var _ N.Dialer = (*Client)(nil)
|
||||
|
||||
type Client struct {
|
||||
access sync.Mutex
|
||||
connections list.List[abstractSession]
|
||||
ctx context.Context
|
||||
dialer N.Dialer
|
||||
protocol Protocol
|
||||
maxConnections int
|
||||
minStreams int
|
||||
maxStreams int
|
||||
}
|
||||
|
||||
func NewClient(ctx context.Context, dialer N.Dialer, protocol Protocol, maxConnections int, minStreams int, maxStreams int) *Client {
|
||||
return &Client{
|
||||
ctx: ctx,
|
||||
dialer: dialer,
|
||||
protocol: protocol,
|
||||
maxConnections: maxConnections,
|
||||
minStreams: minStreams,
|
||||
maxStreams: maxStreams,
|
||||
}
|
||||
}
|
||||
|
||||
func NewClientWithOptions(ctx context.Context, dialer N.Dialer, options option.MultiplexOptions) (N.Dialer, error) {
|
||||
if !options.Enabled {
|
||||
return nil, nil
|
||||
}
|
||||
return mux.NewClient(mux.Options{
|
||||
Dialer: dialer,
|
||||
Protocol: options.Protocol,
|
||||
MaxConnections: options.MaxConnections,
|
||||
MinStreams: options.MinStreams,
|
||||
MaxStreams: options.MaxStreams,
|
||||
Padding: options.Padding,
|
||||
})
|
||||
if options.MaxConnections == 0 && options.MaxStreams == 0 {
|
||||
options.MinStreams = 8
|
||||
}
|
||||
protocol, err := ParseProtocol(options.Protocol)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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) {
|
||||
switch N.NetworkName(network) {
|
||||
case N.NetworkTCP:
|
||||
stream, err := c.openStream()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ClientConn{Conn: stream, destination: destination}, nil
|
||||
case N.NetworkUDP:
|
||||
stream, err := c.openStream()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bufio.NewBindPacketConn(deadline.NewPacketConn(bufio.NewNetPacketConn(&ClientPacketConn{ExtendedConn: bufio.NewExtendedConn(stream), destination: destination})), destination), nil
|
||||
default:
|
||||
return nil, E.Extend(N.ErrUnknownNetwork, network)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
stream, err := c.openStream()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return deadline.NewPacketConn(&ClientPacketAddrConn{ExtendedConn: bufio.NewExtendedConn(stream), destination: destination}), nil
|
||||
}
|
||||
|
||||
func (c *Client) openStream() (net.Conn, error) {
|
||||
var (
|
||||
session abstractSession
|
||||
stream net.Conn
|
||||
err error
|
||||
)
|
||||
for attempts := 0; attempts < 2; attempts++ {
|
||||
session, err = c.offer()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
stream, err = session.Open()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &wrapStream{stream}, nil
|
||||
}
|
||||
|
||||
func (c *Client) offer() (abstractSession, error) {
|
||||
c.access.Lock()
|
||||
defer c.access.Unlock()
|
||||
|
||||
sessions := make([]abstractSession, 0, c.maxConnections)
|
||||
for element := c.connections.Front(); element != nil; {
|
||||
if element.Value.IsClosed() {
|
||||
nextElement := element.Next()
|
||||
c.connections.Remove(element)
|
||||
element = nextElement
|
||||
continue
|
||||
}
|
||||
sessions = append(sessions, element.Value)
|
||||
element = element.Next()
|
||||
}
|
||||
sLen := len(sessions)
|
||||
if sLen == 0 {
|
||||
return c.offerNew()
|
||||
}
|
||||
session := common.MinBy(sessions, abstractSession.NumStreams)
|
||||
numStreams := session.NumStreams()
|
||||
if numStreams == 0 {
|
||||
return session, nil
|
||||
}
|
||||
if c.maxConnections > 0 {
|
||||
if sLen >= c.maxConnections || numStreams < c.minStreams {
|
||||
return session, nil
|
||||
}
|
||||
} else {
|
||||
if c.maxStreams > 0 && numStreams < c.maxStreams {
|
||||
return session, nil
|
||||
}
|
||||
}
|
||||
return c.offerNew()
|
||||
}
|
||||
|
||||
func (c *Client) offerNew() (abstractSession, error) {
|
||||
conn, err := c.dialer.DialContext(c.ctx, N.NetworkTCP, Destination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if vectorisedWriter, isVectorised := bufio.CreateVectorisedWriter(conn); isVectorised {
|
||||
conn = &vectorisedProtocolConn{protocolConn{Conn: conn, protocol: c.protocol}, vectorisedWriter}
|
||||
} else {
|
||||
conn = &protocolConn{Conn: conn, protocol: c.protocol}
|
||||
}
|
||||
session, err := c.protocol.newClient(conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.connections.PushBack(session)
|
||||
return session, nil
|
||||
}
|
||||
|
||||
func (c *Client) Close() error {
|
||||
c.access.Lock()
|
||||
defer c.access.Unlock()
|
||||
for _, session := range c.connections.Array() {
|
||||
session.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ClientConn struct {
|
||||
net.Conn
|
||||
destination M.Socksaddr
|
||||
requestWrite bool
|
||||
responseRead bool
|
||||
}
|
||||
|
||||
func (c *ClientConn) readResponse() error {
|
||||
response, err := ReadStreamResponse(c.Conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if response.Status == statusError {
|
||||
return E.New("remote error: ", response.Message)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ClientConn) Read(b []byte) (n int, err error) {
|
||||
if !c.responseRead {
|
||||
err = c.readResponse()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.responseRead = true
|
||||
}
|
||||
return c.Conn.Read(b)
|
||||
}
|
||||
|
||||
func (c *ClientConn) Write(b []byte) (n int, err error) {
|
||||
if c.requestWrite {
|
||||
return c.Conn.Write(b)
|
||||
}
|
||||
request := StreamRequest{
|
||||
Network: N.NetworkTCP,
|
||||
Destination: c.destination,
|
||||
}
|
||||
_buffer := buf.StackNewSize(requestLen(request) + len(b))
|
||||
defer common.KeepAlive(_buffer)
|
||||
buffer := common.Dup(_buffer)
|
||||
defer buffer.Release()
|
||||
EncodeStreamRequest(request, buffer)
|
||||
buffer.Write(b)
|
||||
_, err = c.Conn.Write(buffer.Bytes())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.requestWrite = true
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
func (c *ClientConn) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
if !c.requestWrite {
|
||||
return bufio.ReadFrom0(c, r)
|
||||
}
|
||||
return bufio.Copy(c.Conn, r)
|
||||
}
|
||||
|
||||
func (c *ClientConn) WriteTo(w io.Writer) (n int64, err error) {
|
||||
if !c.responseRead {
|
||||
return bufio.WriteTo0(c, w)
|
||||
}
|
||||
return bufio.Copy(w, c.Conn)
|
||||
}
|
||||
|
||||
func (c *ClientConn) LocalAddr() net.Addr {
|
||||
return c.Conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *ClientConn) RemoteAddr() net.Addr {
|
||||
return c.destination.TCPAddr()
|
||||
}
|
||||
|
||||
func (c *ClientConn) ReaderReplaceable() bool {
|
||||
return c.responseRead
|
||||
}
|
||||
|
||||
func (c *ClientConn) WriterReplaceable() bool {
|
||||
return c.requestWrite
|
||||
}
|
||||
|
||||
func (c *ClientConn) Upstream() any {
|
||||
return c.Conn
|
||||
}
|
||||
|
||||
type ClientPacketConn struct {
|
||||
N.ExtendedConn
|
||||
destination M.Socksaddr
|
||||
requestWrite bool
|
||||
responseRead bool
|
||||
}
|
||||
|
||||
func (c *ClientPacketConn) readResponse() error {
|
||||
response, err := ReadStreamResponse(c.ExtendedConn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if response.Status == statusError {
|
||||
return E.New("remote error: ", response.Message)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ClientPacketConn) Read(b []byte) (n int, err error) {
|
||||
if !c.responseRead {
|
||||
err = c.readResponse()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.responseRead = true
|
||||
}
|
||||
var length uint16
|
||||
err = binary.Read(c.ExtendedConn, binary.BigEndian, &length)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if cap(b) < int(length) {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
return io.ReadFull(c.ExtendedConn, b[:length])
|
||||
}
|
||||
|
||||
func (c *ClientPacketConn) writeRequest(payload []byte) (n int, err error) {
|
||||
request := StreamRequest{
|
||||
Network: N.NetworkUDP,
|
||||
Destination: c.destination,
|
||||
}
|
||||
rLen := requestLen(request)
|
||||
if len(payload) > 0 {
|
||||
rLen += 2 + len(payload)
|
||||
}
|
||||
_buffer := buf.StackNewSize(rLen)
|
||||
defer common.KeepAlive(_buffer)
|
||||
buffer := common.Dup(_buffer)
|
||||
defer buffer.Release()
|
||||
EncodeStreamRequest(request, buffer)
|
||||
if len(payload) > 0 {
|
||||
common.Must(
|
||||
binary.Write(buffer, binary.BigEndian, uint16(len(payload))),
|
||||
common.Error(buffer.Write(payload)),
|
||||
)
|
||||
}
|
||||
_, err = c.ExtendedConn.Write(buffer.Bytes())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.requestWrite = true
|
||||
return len(payload), nil
|
||||
}
|
||||
|
||||
func (c *ClientPacketConn) Write(b []byte) (n int, err error) {
|
||||
if !c.requestWrite {
|
||||
return c.writeRequest(b)
|
||||
}
|
||||
err = binary.Write(c.ExtendedConn, binary.BigEndian, uint16(len(b)))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return c.ExtendedConn.Write(b)
|
||||
}
|
||||
|
||||
func (c *ClientPacketConn) ReadBuffer(buffer *buf.Buffer) (err error) {
|
||||
if !c.responseRead {
|
||||
err = c.readResponse()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.responseRead = true
|
||||
}
|
||||
var length uint16
|
||||
err = binary.Read(c.ExtendedConn, binary.BigEndian, &length)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = buffer.ReadFullFrom(c.ExtendedConn, int(length))
|
||||
return
|
||||
}
|
||||
|
||||
func (c *ClientPacketConn) WriteBuffer(buffer *buf.Buffer) error {
|
||||
if !c.requestWrite {
|
||||
defer buffer.Release()
|
||||
return common.Error(c.writeRequest(buffer.Bytes()))
|
||||
}
|
||||
bLen := buffer.Len()
|
||||
binary.BigEndian.PutUint16(buffer.ExtendHeader(2), uint16(bLen))
|
||||
return c.ExtendedConn.WriteBuffer(buffer)
|
||||
}
|
||||
|
||||
func (c *ClientPacketConn) FrontHeadroom() int {
|
||||
return 2
|
||||
}
|
||||
|
||||
func (c *ClientPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
|
||||
err = c.ReadBuffer(buffer)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *ClientPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
return c.WriteBuffer(buffer)
|
||||
}
|
||||
|
||||
func (c *ClientPacketConn) LocalAddr() net.Addr {
|
||||
return c.ExtendedConn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *ClientPacketConn) RemoteAddr() net.Addr {
|
||||
return c.destination.UDPAddr()
|
||||
}
|
||||
|
||||
func (c *ClientPacketConn) Upstream() any {
|
||||
return c.ExtendedConn
|
||||
}
|
||||
|
||||
var _ N.NetPacketConn = (*ClientPacketAddrConn)(nil)
|
||||
|
||||
type ClientPacketAddrConn struct {
|
||||
N.ExtendedConn
|
||||
destination M.Socksaddr
|
||||
requestWrite bool
|
||||
responseRead bool
|
||||
}
|
||||
|
||||
func (c *ClientPacketAddrConn) readResponse() error {
|
||||
response, err := ReadStreamResponse(c.ExtendedConn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if response.Status == statusError {
|
||||
return E.New("remote error: ", response.Message)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ClientPacketAddrConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
if !c.responseRead {
|
||||
err = c.readResponse()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.responseRead = true
|
||||
}
|
||||
destination, err := M.SocksaddrSerializer.ReadAddrPort(c.ExtendedConn)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if destination.IsFqdn() {
|
||||
addr = destination
|
||||
} else {
|
||||
addr = destination.UDPAddr()
|
||||
}
|
||||
var length uint16
|
||||
err = binary.Read(c.ExtendedConn, binary.BigEndian, &length)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if cap(p) < int(length) {
|
||||
return 0, nil, io.ErrShortBuffer
|
||||
}
|
||||
n, err = io.ReadFull(c.ExtendedConn, p[:length])
|
||||
return
|
||||
}
|
||||
|
||||
func (c *ClientPacketAddrConn) writeRequest(payload []byte, destination M.Socksaddr) (n int, err error) {
|
||||
request := StreamRequest{
|
||||
Network: N.NetworkUDP,
|
||||
Destination: c.destination,
|
||||
PacketAddr: true,
|
||||
}
|
||||
rLen := requestLen(request)
|
||||
if len(payload) > 0 {
|
||||
rLen += M.SocksaddrSerializer.AddrPortLen(destination) + 2 + len(payload)
|
||||
}
|
||||
_buffer := buf.StackNewSize(rLen)
|
||||
defer common.KeepAlive(_buffer)
|
||||
buffer := common.Dup(_buffer)
|
||||
defer buffer.Release()
|
||||
EncodeStreamRequest(request, buffer)
|
||||
if len(payload) > 0 {
|
||||
common.Must(
|
||||
M.SocksaddrSerializer.WriteAddrPort(buffer, destination),
|
||||
binary.Write(buffer, binary.BigEndian, uint16(len(payload))),
|
||||
common.Error(buffer.Write(payload)),
|
||||
)
|
||||
}
|
||||
_, err = c.ExtendedConn.Write(buffer.Bytes())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.requestWrite = true
|
||||
return len(payload), nil
|
||||
}
|
||||
|
||||
func (c *ClientPacketAddrConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
if !c.requestWrite {
|
||||
return c.writeRequest(p, M.SocksaddrFromNet(addr))
|
||||
}
|
||||
err = M.SocksaddrSerializer.WriteAddrPort(c.ExtendedConn, M.SocksaddrFromNet(addr))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = binary.Write(c.ExtendedConn, binary.BigEndian, uint16(len(p)))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return c.ExtendedConn.Write(p)
|
||||
}
|
||||
|
||||
func (c *ClientPacketAddrConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
|
||||
if !c.responseRead {
|
||||
err = c.readResponse()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.responseRead = true
|
||||
}
|
||||
destination, err = M.SocksaddrSerializer.ReadAddrPort(c.ExtendedConn)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var length uint16
|
||||
err = binary.Read(c.ExtendedConn, binary.BigEndian, &length)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = buffer.ReadFullFrom(c.ExtendedConn, int(length))
|
||||
return
|
||||
}
|
||||
|
||||
func (c *ClientPacketAddrConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
if !c.requestWrite {
|
||||
defer buffer.Release()
|
||||
return common.Error(c.writeRequest(buffer.Bytes(), destination))
|
||||
}
|
||||
bLen := buffer.Len()
|
||||
header := buf.With(buffer.ExtendHeader(M.SocksaddrSerializer.AddrPortLen(destination) + 2))
|
||||
common.Must(
|
||||
M.SocksaddrSerializer.WriteAddrPort(header, destination),
|
||||
binary.Write(header, binary.BigEndian, uint16(bLen)),
|
||||
)
|
||||
return c.ExtendedConn.WriteBuffer(buffer)
|
||||
}
|
||||
|
||||
func (c *ClientPacketAddrConn) LocalAddr() net.Addr {
|
||||
return c.ExtendedConn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *ClientPacketAddrConn) FrontHeadroom() int {
|
||||
return 2 + M.MaxSocksaddrLength
|
||||
}
|
||||
|
||||
func (c *ClientPacketAddrConn) Upstream() any {
|
||||
return c.ExtendedConn
|
||||
}
|
||||
|
||||
@@ -1,14 +1,240 @@
|
||||
package mux
|
||||
|
||||
import (
|
||||
"github.com/sagernet/sing-mux"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"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/rw"
|
||||
"github.com/sagernet/smux"
|
||||
|
||||
"github.com/hashicorp/yamux"
|
||||
)
|
||||
|
||||
type (
|
||||
Client = mux.Client
|
||||
var Destination = M.Socksaddr{
|
||||
Fqdn: "sp.mux.sing-box.arpa",
|
||||
Port: 444,
|
||||
}
|
||||
|
||||
const (
|
||||
ProtocolSMux Protocol = iota
|
||||
ProtocolYAMux
|
||||
)
|
||||
|
||||
var (
|
||||
Destination = mux.Destination
|
||||
HandleConnection = mux.HandleConnection
|
||||
type Protocol byte
|
||||
|
||||
func ParseProtocol(name string) (Protocol, error) {
|
||||
switch name {
|
||||
case "", "smux":
|
||||
return ProtocolSMux, nil
|
||||
case "yamux":
|
||||
return ProtocolYAMux, nil
|
||||
default:
|
||||
return ProtocolYAMux, E.New("unknown multiplex protocol: ", name)
|
||||
}
|
||||
}
|
||||
|
||||
func (p Protocol) newServer(conn net.Conn) (abstractSession, error) {
|
||||
switch p {
|
||||
case ProtocolSMux:
|
||||
session, err := smux.Server(conn, smuxConfig())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &smuxSession{session}, nil
|
||||
case ProtocolYAMux:
|
||||
return yamux.Server(conn, yaMuxConfig())
|
||||
default:
|
||||
panic("unknown protocol")
|
||||
}
|
||||
}
|
||||
|
||||
func (p Protocol) newClient(conn net.Conn) (abstractSession, error) {
|
||||
switch p {
|
||||
case ProtocolSMux:
|
||||
session, err := smux.Client(conn, smuxConfig())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &smuxSession{session}, nil
|
||||
case ProtocolYAMux:
|
||||
return yamux.Client(conn, yaMuxConfig())
|
||||
default:
|
||||
panic("unknown protocol")
|
||||
}
|
||||
}
|
||||
|
||||
func smuxConfig() *smux.Config {
|
||||
config := smux.DefaultConfig()
|
||||
config.KeepAliveDisabled = true
|
||||
return config
|
||||
}
|
||||
|
||||
func yaMuxConfig() *yamux.Config {
|
||||
config := yamux.DefaultConfig()
|
||||
config.LogOutput = io.Discard
|
||||
config.StreamCloseTimeout = C.TCPTimeout
|
||||
config.StreamOpenTimeout = C.TCPTimeout
|
||||
return config
|
||||
}
|
||||
|
||||
func (p Protocol) String() string {
|
||||
switch p {
|
||||
case ProtocolSMux:
|
||||
return "smux"
|
||||
case ProtocolYAMux:
|
||||
return "yamux"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
version0 = 0
|
||||
)
|
||||
|
||||
type Request struct {
|
||||
Protocol Protocol
|
||||
}
|
||||
|
||||
func ReadRequest(reader io.Reader) (*Request, error) {
|
||||
version, err := rw.ReadByte(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if version != version0 {
|
||||
return nil, E.New("unsupported version: ", version)
|
||||
}
|
||||
protocol, err := rw.ReadByte(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if protocol > byte(ProtocolYAMux) {
|
||||
return nil, E.New("unsupported protocol: ", protocol)
|
||||
}
|
||||
return &Request{Protocol: Protocol(protocol)}, nil
|
||||
}
|
||||
|
||||
func EncodeRequest(buffer *buf.Buffer, request Request) {
|
||||
buffer.WriteByte(version0)
|
||||
buffer.WriteByte(byte(request.Protocol))
|
||||
}
|
||||
|
||||
const (
|
||||
flagUDP = 1
|
||||
flagAddr = 2
|
||||
statusSuccess = 0
|
||||
statusError = 1
|
||||
)
|
||||
|
||||
type StreamRequest struct {
|
||||
Network string
|
||||
Destination M.Socksaddr
|
||||
PacketAddr bool
|
||||
}
|
||||
|
||||
func ReadStreamRequest(reader io.Reader) (*StreamRequest, error) {
|
||||
var flags uint16
|
||||
err := binary.Read(reader, binary.BigEndian, &flags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
destination, err := M.SocksaddrSerializer.ReadAddrPort(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var network string
|
||||
var udpAddr bool
|
||||
if flags&flagUDP == 0 {
|
||||
network = N.NetworkTCP
|
||||
} else {
|
||||
network = N.NetworkUDP
|
||||
udpAddr = flags&flagAddr != 0
|
||||
}
|
||||
return &StreamRequest{network, destination, udpAddr}, nil
|
||||
}
|
||||
|
||||
func requestLen(request StreamRequest) int {
|
||||
var rLen int
|
||||
rLen += 1 // version
|
||||
rLen += 2 // flags
|
||||
rLen += M.SocksaddrSerializer.AddrPortLen(request.Destination)
|
||||
return rLen
|
||||
}
|
||||
|
||||
func EncodeStreamRequest(request StreamRequest, buffer *buf.Buffer) {
|
||||
destination := request.Destination
|
||||
var flags uint16
|
||||
if request.Network == N.NetworkUDP {
|
||||
flags |= flagUDP
|
||||
}
|
||||
if request.PacketAddr {
|
||||
flags |= flagAddr
|
||||
if !destination.IsValid() {
|
||||
destination = Destination
|
||||
}
|
||||
}
|
||||
common.Must(
|
||||
binary.Write(buffer, binary.BigEndian, flags),
|
||||
M.SocksaddrSerializer.WriteAddrPort(buffer, destination),
|
||||
)
|
||||
}
|
||||
|
||||
type StreamResponse struct {
|
||||
Status uint8
|
||||
Message string
|
||||
}
|
||||
|
||||
func ReadStreamResponse(reader io.Reader) (*StreamResponse, error) {
|
||||
var response StreamResponse
|
||||
status, err := rw.ReadByte(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.Status = status
|
||||
if status == statusError {
|
||||
response.Message, err = rw.ReadVString(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
type wrapStream struct {
|
||||
net.Conn
|
||||
}
|
||||
|
||||
func (w *wrapStream) Read(p []byte) (n int, err error) {
|
||||
n, err = w.Conn.Read(p)
|
||||
err = wrapError(err)
|
||||
return
|
||||
}
|
||||
|
||||
func (w *wrapStream) Write(p []byte) (n int, err error) {
|
||||
n, err = w.Conn.Write(p)
|
||||
err = wrapError(err)
|
||||
return
|
||||
}
|
||||
|
||||
func (w *wrapStream) WriteIsThreadUnsafe() {
|
||||
}
|
||||
|
||||
func (w *wrapStream) Upstream() any {
|
||||
return w.Conn
|
||||
}
|
||||
|
||||
func wrapError(err error) error {
|
||||
switch err {
|
||||
case yamux.ErrStreamClosed:
|
||||
return io.EOF
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
258
common/mux/service.go
Normal file
258
common/mux/service.go
Normal file
@@ -0,0 +1,258 @@
|
||||
package mux
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
"github.com/sagernet/sing/common/bufio/deadline"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
"github.com/sagernet/sing/common/task"
|
||||
)
|
||||
|
||||
func NewConnection(ctx context.Context, router adapter.Router, errorHandler E.Handler, logger log.ContextLogger, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
request, err := ReadRequest(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
session, err := request.Protocol.newServer(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var group task.Group
|
||||
group.Append0(func(ctx context.Context) error {
|
||||
var stream net.Conn
|
||||
for {
|
||||
stream, err = session.Accept()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go newConnection(ctx, router, errorHandler, logger, stream, metadata)
|
||||
}
|
||||
})
|
||||
group.Cleanup(func() {
|
||||
session.Close()
|
||||
})
|
||||
return group.Run(ctx)
|
||||
}
|
||||
|
||||
func newConnection(ctx context.Context, router adapter.Router, errorHandler E.Handler, logger log.ContextLogger, stream net.Conn, metadata adapter.InboundContext) {
|
||||
stream = &wrapStream{stream}
|
||||
request, err := ReadStreamRequest(stream)
|
||||
if err != nil {
|
||||
logger.ErrorContext(ctx, err)
|
||||
return
|
||||
}
|
||||
metadata.Destination = request.Destination
|
||||
if request.Network == N.NetworkTCP {
|
||||
logger.InfoContext(ctx, "inbound multiplex connection to ", metadata.Destination)
|
||||
hErr := router.RouteConnection(ctx, &ServerConn{ExtendedConn: bufio.NewExtendedConn(stream)}, metadata)
|
||||
stream.Close()
|
||||
if hErr != nil {
|
||||
errorHandler.NewError(ctx, hErr)
|
||||
}
|
||||
} else {
|
||||
var packetConn N.PacketConn
|
||||
if !request.PacketAddr {
|
||||
logger.InfoContext(ctx, "inbound multiplex packet connection to ", metadata.Destination)
|
||||
packetConn = &ServerPacketConn{ExtendedConn: bufio.NewExtendedConn(stream), destination: request.Destination}
|
||||
} else {
|
||||
logger.InfoContext(ctx, "inbound multiplex packet connection")
|
||||
packetConn = &ServerPacketAddrConn{ExtendedConn: bufio.NewExtendedConn(stream)}
|
||||
}
|
||||
hErr := router.RoutePacketConnection(ctx, deadline.NewPacketConn(bufio.NewNetPacketConn(packetConn)), metadata)
|
||||
stream.Close()
|
||||
if hErr != nil {
|
||||
errorHandler.NewError(ctx, hErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var _ N.HandshakeConn = (*ServerConn)(nil)
|
||||
|
||||
type ServerConn struct {
|
||||
N.ExtendedConn
|
||||
responseWrite bool
|
||||
}
|
||||
|
||||
func (c *ServerConn) HandshakeFailure(err error) error {
|
||||
errMessage := err.Error()
|
||||
_buffer := buf.StackNewSize(1 + rw.UVariantLen(uint64(len(errMessage))) + len(errMessage))
|
||||
defer common.KeepAlive(_buffer)
|
||||
buffer := common.Dup(_buffer)
|
||||
defer buffer.Release()
|
||||
common.Must(
|
||||
buffer.WriteByte(statusError),
|
||||
rw.WriteVString(_buffer, errMessage),
|
||||
)
|
||||
return c.ExtendedConn.WriteBuffer(buffer)
|
||||
}
|
||||
|
||||
func (c *ServerConn) Write(b []byte) (n int, err error) {
|
||||
if c.responseWrite {
|
||||
return c.ExtendedConn.Write(b)
|
||||
}
|
||||
_buffer := buf.StackNewSize(1 + len(b))
|
||||
defer common.KeepAlive(_buffer)
|
||||
buffer := common.Dup(_buffer)
|
||||
defer buffer.Release()
|
||||
common.Must(
|
||||
buffer.WriteByte(statusSuccess),
|
||||
common.Error(buffer.Write(b)),
|
||||
)
|
||||
_, err = c.ExtendedConn.Write(buffer.Bytes())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.responseWrite = true
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
func (c *ServerConn) WriteBuffer(buffer *buf.Buffer) error {
|
||||
if c.responseWrite {
|
||||
return c.ExtendedConn.WriteBuffer(buffer)
|
||||
}
|
||||
buffer.ExtendHeader(1)[0] = statusSuccess
|
||||
c.responseWrite = true
|
||||
return c.ExtendedConn.WriteBuffer(buffer)
|
||||
}
|
||||
|
||||
func (c *ServerConn) FrontHeadroom() int {
|
||||
if !c.responseWrite {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *ServerConn) Upstream() any {
|
||||
return c.ExtendedConn
|
||||
}
|
||||
|
||||
var (
|
||||
_ N.HandshakeConn = (*ServerPacketConn)(nil)
|
||||
_ N.PacketConn = (*ServerPacketConn)(nil)
|
||||
)
|
||||
|
||||
type ServerPacketConn struct {
|
||||
N.ExtendedConn
|
||||
destination M.Socksaddr
|
||||
responseWrite bool
|
||||
}
|
||||
|
||||
func (c *ServerPacketConn) HandshakeFailure(err error) error {
|
||||
errMessage := err.Error()
|
||||
_buffer := buf.StackNewSize(1 + rw.UVariantLen(uint64(len(errMessage))) + len(errMessage))
|
||||
defer common.KeepAlive(_buffer)
|
||||
buffer := common.Dup(_buffer)
|
||||
defer buffer.Release()
|
||||
common.Must(
|
||||
buffer.WriteByte(statusError),
|
||||
rw.WriteVString(_buffer, errMessage),
|
||||
)
|
||||
return c.ExtendedConn.WriteBuffer(buffer)
|
||||
}
|
||||
|
||||
func (c *ServerPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
|
||||
var length uint16
|
||||
err = binary.Read(c.ExtendedConn, binary.BigEndian, &length)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = buffer.ReadFullFrom(c.ExtendedConn, int(length))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
destination = c.destination
|
||||
return
|
||||
}
|
||||
|
||||
func (c *ServerPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
pLen := buffer.Len()
|
||||
common.Must(binary.Write(buf.With(buffer.ExtendHeader(2)), binary.BigEndian, uint16(pLen)))
|
||||
if !c.responseWrite {
|
||||
buffer.ExtendHeader(1)[0] = statusSuccess
|
||||
c.responseWrite = true
|
||||
}
|
||||
return c.ExtendedConn.WriteBuffer(buffer)
|
||||
}
|
||||
|
||||
func (c *ServerPacketConn) Upstream() any {
|
||||
return c.ExtendedConn
|
||||
}
|
||||
|
||||
func (c *ServerPacketConn) FrontHeadroom() int {
|
||||
if !c.responseWrite {
|
||||
return 3
|
||||
}
|
||||
return 2
|
||||
}
|
||||
|
||||
var (
|
||||
_ N.HandshakeConn = (*ServerPacketAddrConn)(nil)
|
||||
_ N.PacketConn = (*ServerPacketAddrConn)(nil)
|
||||
)
|
||||
|
||||
type ServerPacketAddrConn struct {
|
||||
N.ExtendedConn
|
||||
responseWrite bool
|
||||
}
|
||||
|
||||
func (c *ServerPacketAddrConn) HandshakeFailure(err error) error {
|
||||
errMessage := err.Error()
|
||||
_buffer := buf.StackNewSize(1 + rw.UVariantLen(uint64(len(errMessage))) + len(errMessage))
|
||||
defer common.KeepAlive(_buffer)
|
||||
buffer := common.Dup(_buffer)
|
||||
defer buffer.Release()
|
||||
common.Must(
|
||||
buffer.WriteByte(statusError),
|
||||
rw.WriteVString(_buffer, errMessage),
|
||||
)
|
||||
return c.ExtendedConn.WriteBuffer(buffer)
|
||||
}
|
||||
|
||||
func (c *ServerPacketAddrConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
|
||||
destination, err = M.SocksaddrSerializer.ReadAddrPort(c.ExtendedConn)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var length uint16
|
||||
err = binary.Read(c.ExtendedConn, binary.BigEndian, &length)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = buffer.ReadFullFrom(c.ExtendedConn, int(length))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *ServerPacketAddrConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
pLen := buffer.Len()
|
||||
common.Must(binary.Write(buf.With(buffer.ExtendHeader(2)), binary.BigEndian, uint16(pLen)))
|
||||
common.Must(M.SocksaddrSerializer.WriteAddrPort(buf.With(buffer.ExtendHeader(M.SocksaddrSerializer.AddrPortLen(destination))), destination))
|
||||
if !c.responseWrite {
|
||||
buffer.ExtendHeader(1)[0] = statusSuccess
|
||||
c.responseWrite = true
|
||||
}
|
||||
return c.ExtendedConn.WriteBuffer(buffer)
|
||||
}
|
||||
|
||||
func (c *ServerPacketAddrConn) Upstream() any {
|
||||
return c.ExtendedConn
|
||||
}
|
||||
|
||||
func (c *ServerPacketAddrConn) FrontHeadroom() int {
|
||||
if !c.responseWrite {
|
||||
return 3 + M.MaxSocksaddrLength
|
||||
}
|
||||
return 2 + M.MaxSocksaddrLength
|
||||
}
|
||||
91
common/mux/session.go
Normal file
91
common/mux/session.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package mux
|
||||
|
||||
import (
|
||||
"io"
|
||||
"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/smux"
|
||||
)
|
||||
|
||||
type abstractSession interface {
|
||||
Open() (net.Conn, error)
|
||||
Accept() (net.Conn, error)
|
||||
NumStreams() int
|
||||
Close() error
|
||||
IsClosed() bool
|
||||
}
|
||||
|
||||
var _ abstractSession = (*smuxSession)(nil)
|
||||
|
||||
type smuxSession struct {
|
||||
*smux.Session
|
||||
}
|
||||
|
||||
func (s *smuxSession) Open() (net.Conn, error) {
|
||||
return s.OpenStream()
|
||||
}
|
||||
|
||||
func (s *smuxSession) Accept() (net.Conn, error) {
|
||||
return s.AcceptStream()
|
||||
}
|
||||
|
||||
type protocolConn struct {
|
||||
net.Conn
|
||||
protocol Protocol
|
||||
protocolWritten bool
|
||||
}
|
||||
|
||||
func (c *protocolConn) Write(p []byte) (n int, err error) {
|
||||
if c.protocolWritten {
|
||||
return c.Conn.Write(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())
|
||||
if err == nil {
|
||||
n--
|
||||
}
|
||||
c.protocolWritten = true
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (c *protocolConn) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
if !c.protocolWritten {
|
||||
return bufio.ReadFrom0(c, r)
|
||||
}
|
||||
return bufio.Copy(c.Conn, r)
|
||||
}
|
||||
|
||||
func (c *protocolConn) Upstream() any {
|
||||
return c.Conn
|
||||
}
|
||||
|
||||
type vectorisedProtocolConn struct {
|
||||
protocolConn
|
||||
N.VectorisedWriter
|
||||
}
|
||||
|
||||
func (c *vectorisedProtocolConn) WriteVectorised(buffers []*buf.Buffer) error {
|
||||
if c.protocolWritten {
|
||||
return c.VectorisedWriter.WriteVectorised(buffers)
|
||||
}
|
||||
c.protocolWritten = true
|
||||
_buffer := buf.StackNewSize(2)
|
||||
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...))
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
"github.com/sagernet/sing/protocol/http"
|
||||
)
|
||||
|
||||
@@ -16,5 +15,5 @@ func HTTPHost(ctx context.Context, reader io.Reader) (*adapter.InboundContext, e
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
@@ -111,16 +111,6 @@ func (e *RealityClientConfig) ClientHandshake(ctx context.Context, conn net.Conn
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(uConfig.NextProtos) > 0 {
|
||||
for _, extension := range uConn.Extensions {
|
||||
if alpnExtension, isALPN := extension.(*utls.ALPNExtension); isALPN {
|
||||
alpnExtension.AlpnProtocols = uConfig.NextProtos
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hello := uConn.HandshakeState.Hello
|
||||
hello.SessionId = make([]byte, 32)
|
||||
copy(hello.Raw[39:], hello.SessionId)
|
||||
@@ -135,7 +125,7 @@ func (e *RealityClientConfig) ClientHandshake(ctx context.Context, conn net.Conn
|
||||
|
||||
hello.SessionId[0] = 1
|
||||
hello.SessionId[1] = 8
|
||||
hello.SessionId[2] = 1
|
||||
hello.SessionId[2] = 0
|
||||
binary.BigEndian.PutUint32(hello.SessionId[4:], uint32(time.Now().Unix()))
|
||||
copy(hello.SessionId[8:], e.shortID[:])
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
package tls
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"math/rand"
|
||||
@@ -48,7 +47,7 @@ func (e *UTLSClientConfig) Config() (*STDConfig, error) {
|
||||
}
|
||||
|
||||
func (e *UTLSClientConfig) Client(conn net.Conn) (Conn, error) {
|
||||
return &utlsALPNWrapper{utlsConnWrapper{utls.UClient(conn, e.config.Clone(), e.id)}, e.config.NextProtos}, nil
|
||||
return &utlsConnWrapper{utls.UClient(conn, e.config.Clone(), e.id)}, nil
|
||||
}
|
||||
|
||||
func (e *UTLSClientConfig) SetSessionIDGenerator(generator func(clientHello []byte, sessionID []byte) error) {
|
||||
@@ -88,31 +87,6 @@ func (c *utlsConnWrapper) Upstream() any {
|
||||
return c.UConn
|
||||
}
|
||||
|
||||
type utlsALPNWrapper struct {
|
||||
utlsConnWrapper
|
||||
nextProtocols []string
|
||||
}
|
||||
|
||||
func (c *utlsALPNWrapper) HandshakeContext(ctx context.Context) error {
|
||||
if len(c.nextProtocols) > 0 {
|
||||
err := c.BuildHandshakeState()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, extension := range c.Extensions {
|
||||
if alpnExtension, isALPN := extension.(*utls.ALPNExtension); isALPN {
|
||||
alpnExtension.AlpnProtocols = c.nextProtocols
|
||||
err = c.BuildHandshakeState()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return c.UConn.HandshakeContext(ctx)
|
||||
}
|
||||
|
||||
func NewUTLSClient(router adapter.Router, serverAddress string, options option.OutboundTLSOptions) (*UTLSClientConfig, error) {
|
||||
var serverName string
|
||||
if options.ServerName != "" {
|
||||
|
||||
31
common/warning/warning.go
Normal file
31
common/warning/warning.go
Normal 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -3,13 +3,40 @@ package constant
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
)
|
||||
|
||||
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) {
|
||||
name = os.ExpandEnv(name)
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
)
|
||||
|
||||
func applyDebugOptions(options option.DebugOptions) {
|
||||
applyDebugListenOption(options)
|
||||
if options.GCPercent != nil {
|
||||
debug.SetGCPercent(*options.GCPercent)
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
)
|
||||
|
||||
func applyDebugOptions(options option.DebugOptions) {
|
||||
applyDebugListenOption(options)
|
||||
if options.GCPercent != nil {
|
||||
debug.SetGCPercent(*options.GCPercent)
|
||||
}
|
||||
|
||||
@@ -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"))
|
||||
}
|
||||
}()
|
||||
}
|
||||
@@ -1,73 +1,3 @@
|
||||
#### 1.3-beta12
|
||||
|
||||
* Automatically add Windows firewall rules in order for the system tun stack to work
|
||||
* Fix TLS 1.2 support for shadow-tls client
|
||||
* Add `cache_id` [option](/configuration/experimental#cache_id) for Clash cache file
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.3-beta11
|
||||
|
||||
* Fix bugs and update dependencies
|
||||
|
||||
#### 1.3-beta10
|
||||
|
||||
* Improve direct copy **1**
|
||||
* Improve DNS caching
|
||||
* Add `independent_cache` [option](/configuration/dns#independent_cache) for DNS
|
||||
* Reimplemented shadowsocks client **2**
|
||||
* Add multiplex support for VLESS outbound
|
||||
* Set TCP keepalive for WireGuard gVisor TCP connections
|
||||
* Fixes and improvements
|
||||
|
||||
**1**:
|
||||
|
||||
* Make splice work with traffic statistics systems like Clash API
|
||||
* Significantly reduces memory usage of idle connections
|
||||
|
||||
**2**:
|
||||
|
||||
Improved performance and reduced memory usage.
|
||||
|
||||
#### 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
|
||||
|
||||
* Fix WireGuard reconnect
|
||||
* Perform URLTest recheck after network changes
|
||||
* Fix bugs and update dependencies
|
||||
|
||||
#### 1.3-beta5
|
||||
|
||||
* Add Clash.Meta API compatibility for Clash API
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
"strategy": "",
|
||||
"disable_cache": false,
|
||||
"disable_expire": false,
|
||||
"independent_cache": false,
|
||||
"reverse_mapping": false,
|
||||
"fakeip": {}
|
||||
}
|
||||
@@ -49,10 +48,6 @@ Disable dns cache.
|
||||
|
||||
Disable dns cache expire.
|
||||
|
||||
#### independent_cache
|
||||
|
||||
Make each DNS server's cache independent for special purposes. If enabled, will slightly degrade performance.
|
||||
|
||||
#### reverse_mapping
|
||||
|
||||
Stores a reverse mapping of IP addresses after responding to a DNS query in order to provide domain names when routing.
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
"strategy": "",
|
||||
"disable_cache": false,
|
||||
"disable_expire": false,
|
||||
"independent_cache": false,
|
||||
"reverse_mapping": false,
|
||||
"fakeip": {}
|
||||
}
|
||||
@@ -48,10 +47,6 @@
|
||||
|
||||
禁用 DNS 缓存过期。
|
||||
|
||||
#### independent_cache
|
||||
|
||||
使每个 DNS 服务器的缓存独立,以满足特殊目的。如果启用,将轻微降低性能。
|
||||
|
||||
#### reverse_mapping
|
||||
|
||||
在响应 DNS 查询后存储 IP 地址的反向映射以为路由目的提供域名。
|
||||
|
||||
@@ -30,18 +30,18 @@ The tag of the dns server.
|
||||
|
||||
The address of the dns server.
|
||||
|
||||
| Protocol | Format |
|
||||
|-------------------------------------|-------------------------------|
|
||||
| `System` | `local` |
|
||||
| `TCP` | `tcp://1.0.0.1` |
|
||||
| `UDP` | `8.8.8.8` `udp://8.8.4.4` |
|
||||
| `TLS` | `tls://dns.google` |
|
||||
| `HTTPS` | `https://1.1.1.1/dns-query` |
|
||||
| `QUIC` | `quic://dns.adguard.com` |
|
||||
| `HTTP3` | `h3://8.8.8.8/dns-query` |
|
||||
| `RCode` | `rcode://refused` |
|
||||
| `DHCP` | `dhcp://auto` or `dhcp://en0` |
|
||||
| [FakeIP](/configuration/dns/fakeip) | `fakeip` |
|
||||
| Protocol | Format |
|
||||
|---------------------|-------------------------------|
|
||||
| `System` | `local` |
|
||||
| `TCP` | `tcp://1.0.0.1` |
|
||||
| `UDP` | `8.8.8.8` `udp://8.8.4.4` |
|
||||
| `TLS` | `tls://dns.google` |
|
||||
| `HTTPS` | `https://1.1.1.1/dns-query` |
|
||||
| `QUIC` | `quic://dns.adguard.com` |
|
||||
| `HTTP3` | `h3://8.8.8.8/dns-query` |
|
||||
| `RCode` | `rcode://refused` |
|
||||
| `DHCP` | `dhcp://auto` or `dhcp://en0` |
|
||||
| [FakeIP](./fakeip) | `fakeip` |
|
||||
|
||||
!!! warning ""
|
||||
|
||||
@@ -94,4 +94,4 @@ Take no effect if override by other settings.
|
||||
|
||||
Tag of an outbound for connecting to the dns server.
|
||||
|
||||
Default outbound will be used if empty.
|
||||
Default outbound will be used if empty.
|
||||
@@ -30,18 +30,18 @@ DNS 服务器的标签。
|
||||
|
||||
DNS 服务器的地址。
|
||||
|
||||
| 协议 | 格式 |
|
||||
|-------------------------------------|------------------------------|
|
||||
| `System` | `local` |
|
||||
| `TCP` | `tcp://1.0.0.1` |
|
||||
| `UDP` | `8.8.8.8` `udp://8.8.4.4` |
|
||||
| `TLS` | `tls://dns.google` |
|
||||
| `HTTPS` | `https://1.1.1.1/dns-query` |
|
||||
| `QUIC` | `quic://dns.adguard.com` |
|
||||
| `HTTP3` | `h3://8.8.8.8/dns-query` |
|
||||
| `RCode` | `rcode://refused` |
|
||||
| `DHCP` | `dhcp://auto` 或 `dhcp://en0` |
|
||||
| [FakeIP](/configuration/dns/fakeip) | `fakeip` |
|
||||
| 协议 | 格式 |
|
||||
|--------------------|------------------------------|
|
||||
| `System` | `local` |
|
||||
| `TCP` | `tcp://1.0.0.1` |
|
||||
| `UDP` | `8.8.8.8` `udp://8.8.4.4` |
|
||||
| `TLS` | `tls://dns.google` |
|
||||
| `HTTPS` | `https://1.1.1.1/dns-query` |
|
||||
| `QUIC` | `quic://dns.adguard.com` |
|
||||
| `HTTP3` | `h3://8.8.8.8/dns-query` |
|
||||
| `RCode` | `rcode://refused` |
|
||||
| `DHCP` | `dhcp://auto` 或 `dhcp://en0` |
|
||||
| [FakeIP](./fakeip) | `fakeip` |
|
||||
|
||||
!!! warning ""
|
||||
|
||||
|
||||
@@ -7,14 +7,13 @@
|
||||
"experimental": {
|
||||
"clash_api": {
|
||||
"external_controller": "127.0.0.1:9090",
|
||||
"external_ui": "",
|
||||
"external_ui": "folder",
|
||||
"external_ui_download_url": "",
|
||||
"external_ui_download_detour": "",
|
||||
"secret": "",
|
||||
"default_mode": "",
|
||||
"default_mode": "rule",
|
||||
"store_selected": false,
|
||||
"cache_file": "",
|
||||
"cache_id": ""
|
||||
"cache_file": "cache.db"
|
||||
},
|
||||
"v2ray_api": {
|
||||
"listen": "127.0.0.1:8080",
|
||||
@@ -92,12 +91,6 @@ Store selected outbound for the `Selector` outbound in cache file.
|
||||
|
||||
Cache file path, `cache.db` will be used if empty.
|
||||
|
||||
#### cache_id
|
||||
|
||||
Cache ID.
|
||||
|
||||
If not empty, `store_selected` will use a separate store keyed by it.
|
||||
|
||||
### V2Ray API Fields
|
||||
|
||||
!!! error ""
|
||||
|
||||
@@ -7,14 +7,13 @@
|
||||
"experimental": {
|
||||
"clash_api": {
|
||||
"external_controller": "127.0.0.1:9090",
|
||||
"external_ui": "",
|
||||
"external_ui": "folder",
|
||||
"external_ui_download_url": "",
|
||||
"external_ui_download_detour": "",
|
||||
"secret": "",
|
||||
"default_mode": "",
|
||||
"default_mode": "rule",
|
||||
"store_selected": false,
|
||||
"cache_file": "",
|
||||
"cache_id": ""
|
||||
"cache_file": "cache.db"
|
||||
},
|
||||
"v2ray_api": {
|
||||
"listen": "127.0.0.1:8080",
|
||||
@@ -90,12 +89,6 @@ Clash 中的默认模式,默认使用 `rule`。
|
||||
|
||||
缓存文件路径,默认使用`cache.db`。
|
||||
|
||||
#### cache_id
|
||||
|
||||
缓存 ID。
|
||||
|
||||
如果不为空,`store_selected` 将会使用以此为键的独立存储。
|
||||
|
||||
### V2Ray API 字段
|
||||
|
||||
!!! error ""
|
||||
|
||||
@@ -10,8 +10,7 @@
|
||||
"protocol": "smux",
|
||||
"max_connections": 4,
|
||||
"min_streams": 4,
|
||||
"max_streams": 0,
|
||||
"padding": false
|
||||
"max_streams": 0
|
||||
}
|
||||
```
|
||||
|
||||
@@ -29,9 +28,8 @@ Multiplex protocol.
|
||||
|----------|------------------------------------|
|
||||
| smux | https://github.com/xtaci/smux |
|
||||
| yamux | https://github.com/hashicorp/yamux |
|
||||
| h2mux | https://golang.org/x/net/http2 |
|
||||
|
||||
h2mux is used by default.
|
||||
SMux is used by default.
|
||||
|
||||
#### max_connections
|
||||
|
||||
@@ -50,12 +48,3 @@ Conflict with `max_streams`.
|
||||
Maximum multiplexed streams in a connection before opening a new connection.
|
||||
|
||||
Conflict with `max_connections` and `min_streams`.
|
||||
|
||||
#### padding
|
||||
|
||||
!!! info
|
||||
|
||||
Requires sing-box server version 1.3-beta9 or later.
|
||||
|
||||
Enable padding.
|
||||
|
||||
|
||||
@@ -28,9 +28,8 @@
|
||||
|-------|------------------------------------|
|
||||
| smux | https://github.com/xtaci/smux |
|
||||
| yamux | https://github.com/hashicorp/yamux |
|
||||
| h2mux | https://golang.org/x/net/http2 |
|
||||
|
||||
默认使用 h2mux。
|
||||
默认使用 SMux。
|
||||
|
||||
#### max_connections
|
||||
|
||||
@@ -48,13 +47,4 @@
|
||||
|
||||
在打开新连接之前,连接中的最大多路复用流数量。
|
||||
|
||||
与 `max_connections` 和 `min_streams` 冲突。
|
||||
|
||||
#### padding
|
||||
|
||||
!!! info
|
||||
|
||||
需要 sing-box 服务器版本 1.3-beta9 或更高。
|
||||
|
||||
启用填充。
|
||||
|
||||
与 `max_connections` 和 `min_streams` 冲突。
|
||||
@@ -1,105 +0,0 @@
|
||||
```json
|
||||
{
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"tag": "google",
|
||||
"address": "tls://8.8.8.8"
|
||||
},
|
||||
{
|
||||
"tag": "local",
|
||||
"address": "223.5.5.5",
|
||||
"detour": "direct"
|
||||
},
|
||||
{
|
||||
"tag": "remote",
|
||||
"address": "fakeip"
|
||||
},
|
||||
{
|
||||
"tag": "block",
|
||||
"address": "rcode://success"
|
||||
}
|
||||
],
|
||||
"rules": [
|
||||
{
|
||||
"geosite": "category-ads-all",
|
||||
"server": "block",
|
||||
"disable_cache": true
|
||||
},
|
||||
{
|
||||
"outbound": "any",
|
||||
"server": "local"
|
||||
},
|
||||
{
|
||||
"geosite": "cn",
|
||||
"server": "local"
|
||||
},
|
||||
{
|
||||
"query_type": [
|
||||
"A",
|
||||
"AAAA"
|
||||
],
|
||||
"server": "remote"
|
||||
}
|
||||
],
|
||||
"fakeip": {
|
||||
"enabled": true,
|
||||
"inet4_range": "198.18.0.0/15",
|
||||
"inet6_range": "fc00::/18"
|
||||
},
|
||||
"strategy": "ipv4_only"
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "tun",
|
||||
"inet4_address": "172.19.0.1/30",
|
||||
"auto_route": true,
|
||||
"sniff": true,
|
||||
"domain_strategy": "ipv4_only" // remove this line if you want to resolve the domain remotely (if the server is not sing-box, UDP may not work due to wrong behavior).
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "shadowsocks",
|
||||
"tag": "proxy",
|
||||
"server": "mydomain.com",
|
||||
"server_port": 8080,
|
||||
"method": "2022-blake3-aes-128-gcm",
|
||||
"password": "8JCsPssfgS8tiRwiMlhARg=="
|
||||
},
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct"
|
||||
},
|
||||
{
|
||||
"type": "block",
|
||||
"tag": "block"
|
||||
},
|
||||
{
|
||||
"type": "dns",
|
||||
"tag": "dns-out"
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"rules": [
|
||||
{
|
||||
"protocol": "dns",
|
||||
"outbound": "dns-out"
|
||||
},
|
||||
{
|
||||
"geosite": "cn",
|
||||
"geoip": [
|
||||
"private",
|
||||
"cn"
|
||||
],
|
||||
"outbound": "direct"
|
||||
},
|
||||
{
|
||||
"geosite": "category-ads-all",
|
||||
"outbound": "block"
|
||||
}
|
||||
],
|
||||
"auto_detect_interface": true
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,105 +0,0 @@
|
||||
```json
|
||||
{
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"tag": "google",
|
||||
"address": "tls://8.8.8.8"
|
||||
},
|
||||
{
|
||||
"tag": "local",
|
||||
"address": "223.5.5.5",
|
||||
"detour": "direct"
|
||||
},
|
||||
{
|
||||
"tag": "remote",
|
||||
"address": "fakeip"
|
||||
},
|
||||
{
|
||||
"tag": "block",
|
||||
"address": "rcode://success"
|
||||
}
|
||||
],
|
||||
"rules": [
|
||||
{
|
||||
"geosite": "category-ads-all",
|
||||
"server": "block",
|
||||
"disable_cache": true
|
||||
},
|
||||
{
|
||||
"outbound": "any",
|
||||
"server": "local"
|
||||
},
|
||||
{
|
||||
"geosite": "cn",
|
||||
"server": "local"
|
||||
},
|
||||
{
|
||||
"query_type": [
|
||||
"A",
|
||||
"AAAA"
|
||||
],
|
||||
"server": "remote"
|
||||
}
|
||||
],
|
||||
"fakeip": {
|
||||
"enabled": true,
|
||||
"inet4_range": "198.18.0.0/15",
|
||||
"inet6_range": "fc00::/18"
|
||||
},
|
||||
"strategy": "ipv4_only"
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "tun",
|
||||
"inet4_address": "172.19.0.1/30",
|
||||
"auto_route": true,
|
||||
"sniff": true,
|
||||
"domain_strategy": "ipv4_only" // 如果您想在远程解析域,删除此行 (如果服务器程序不为 sing-box,可能由于错误的行为导致 UDP 无法使用)。
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "shadowsocks",
|
||||
"tag": "proxy",
|
||||
"server": "mydomain.com",
|
||||
"server_port": 8080,
|
||||
"method": "2022-blake3-aes-128-gcm",
|
||||
"password": "8JCsPssfgS8tiRwiMlhARg=="
|
||||
},
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct"
|
||||
},
|
||||
{
|
||||
"type": "block",
|
||||
"tag": "block"
|
||||
},
|
||||
{
|
||||
"type": "dns",
|
||||
"tag": "dns-out"
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"rules": [
|
||||
{
|
||||
"protocol": "dns",
|
||||
"outbound": "dns-out"
|
||||
},
|
||||
{
|
||||
"geosite": "cn",
|
||||
"geoip": [
|
||||
"private",
|
||||
"cn"
|
||||
],
|
||||
"outbound": "direct"
|
||||
},
|
||||
{
|
||||
"geosite": "category-ads-all",
|
||||
"outbound": "block"
|
||||
}
|
||||
],
|
||||
"auto_detect_interface": true
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -9,4 +9,3 @@ Configuration examples for sing-box.
|
||||
* [ShadowTLS](./shadowtls)
|
||||
* [Clash API](./clash-api)
|
||||
* [WireGuard Direct](./wireguard-direct)
|
||||
* [FakeIP](./fakeip)
|
||||
|
||||
@@ -9,4 +9,3 @@ sing-box 的配置示例。
|
||||
* [ShadowTLS](./shadowtls)
|
||||
* [Clash API](./clash-api)
|
||||
* [WireGuard Direct](./wireguard-direct)
|
||||
* [FakeIP](./fakeip)
|
||||
|
||||
@@ -12,6 +12,5 @@ Experimental Android client for sing-box.
|
||||
|
||||
#### Note
|
||||
|
||||
* User Agent in remote profile request is `SFA/$version ($version_code; sing-box $sing_box_version)`
|
||||
* The working directory is located at `/sdcard/Android/data/io.nekohasekai.sfa/files` (External files directory)
|
||||
* Crash logs is located in `$working_directory/stderr.log`
|
||||
* Working directory is 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
|
||||
|
||||
@@ -12,6 +12,5 @@
|
||||
|
||||
#### 注意事项
|
||||
|
||||
* 远程配置文件请求中的 User Agent 为 `SFA/$version ($version_code; sing-box $sing_box_version)`
|
||||
* 工作目录位于 `/sdcard/Android/data/io.nekohasekai.sfa/files` (外部文件目录)
|
||||
* 崩溃日志位于 `$working_directory/stderr.log`
|
||||
* 远程配置文件请求中的 User Agent 为 `SFA/$version ($version_code; sing-box $sing_box_version)`
|
||||
|
||||
@@ -13,8 +13,8 @@ Experimental iOS client for sing-box.
|
||||
|
||||
#### Note
|
||||
|
||||
* User Agent in remote profile request is `SFI/$version ($version_code; sing-box $sing_box_version)`
|
||||
* Crash logs is located in `Settings` -> `View Service Log`
|
||||
* `system` tun stack not working on iOS
|
||||
* User Agent is `SFI/$version ($version_code; sing-box $sing_box_version)` in the remote profile request
|
||||
|
||||
#### Privacy policy
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
|
||||
#### 注意事项
|
||||
|
||||
* `system` tun stack 在 iOS 不工作
|
||||
* 远程配置文件请求中的 User Agent 为 `SFI/$version ($version_code; sing-box $sing_box_version)`
|
||||
* 崩溃日志位于 `设置` -> `查看服务日志`
|
||||
|
||||
#### 隐私政策
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package experimental
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
@@ -9,7 +8,7 @@ import (
|
||||
"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
|
||||
|
||||
@@ -17,9 +16,9 @@ func RegisterClashServerConstructor(constructor ClashServerConstructor) {
|
||||
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 {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
return clashServerConstructor(ctx, router, logFactory, options)
|
||||
return clashServerConstructor(router, logFactory, options)
|
||||
}
|
||||
|
||||
@@ -14,11 +14,10 @@ var bucketSelected = []byte("selected")
|
||||
var _ adapter.ClashCacheFile = (*CacheFile)(nil)
|
||||
|
||||
type CacheFile struct {
|
||||
DB *bbolt.DB
|
||||
cacheID []byte
|
||||
DB *bbolt.DB
|
||||
}
|
||||
|
||||
func Open(path string, cacheID string) (*CacheFile, error) {
|
||||
func Open(path string) (*CacheFile, error) {
|
||||
const fileMode = 0o666
|
||||
options := bbolt.Options{Timeout: time.Second}
|
||||
db, err := bbolt.Open(path, fileMode, &options)
|
||||
@@ -32,39 +31,13 @@ func Open(path string, cacheID string) (*CacheFile, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var cacheIDBytes []byte
|
||||
if cacheID != "" {
|
||||
cacheIDBytes = append([]byte{0}, []byte(cacheID)...)
|
||||
}
|
||||
return &CacheFile{db, cacheIDBytes}, nil
|
||||
}
|
||||
|
||||
func (c *CacheFile) bucket(t *bbolt.Tx, key []byte) *bbolt.Bucket {
|
||||
if c.cacheID == nil {
|
||||
return t.Bucket(key)
|
||||
}
|
||||
bucket := t.Bucket(c.cacheID)
|
||||
if bucket == nil {
|
||||
return nil
|
||||
}
|
||||
return bucket.Bucket(key)
|
||||
}
|
||||
|
||||
func (c *CacheFile) createBucket(t *bbolt.Tx, key []byte) (*bbolt.Bucket, error) {
|
||||
if c.cacheID == nil {
|
||||
return t.CreateBucketIfNotExists(key)
|
||||
}
|
||||
bucket, err := t.CreateBucketIfNotExists(c.cacheID)
|
||||
if bucket == nil {
|
||||
return nil, err
|
||||
}
|
||||
return bucket.CreateBucketIfNotExists(key)
|
||||
return &CacheFile{db}, nil
|
||||
}
|
||||
|
||||
func (c *CacheFile) LoadSelected(group string) string {
|
||||
var selected string
|
||||
c.DB.View(func(t *bbolt.Tx) error {
|
||||
bucket := c.bucket(t, bucketSelected)
|
||||
bucket := t.Bucket(bucketSelected)
|
||||
if bucket == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -79,7 +52,7 @@ func (c *CacheFile) LoadSelected(group string) string {
|
||||
|
||||
func (c *CacheFile) StoreSelected(group, selected string) error {
|
||||
return c.DB.Batch(func(t *bbolt.Tx) error {
|
||||
bucket, err := c.createBucket(t, bucketSelected)
|
||||
bucket, err := t.CreateBucketIfNotExists(bucketSelected)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/json"
|
||||
"github.com/sagernet/sing-box/experimental/clashapi/trafficontrol"
|
||||
"github.com/sagernet/websocket"
|
||||
@@ -15,10 +14,10 @@ import (
|
||||
"github.com/go-chi/render"
|
||||
)
|
||||
|
||||
func connectionRouter(router adapter.Router, trafficManager *trafficontrol.Manager) http.Handler {
|
||||
func connectionRouter(trafficManager *trafficontrol.Manager) http.Handler {
|
||||
r := chi.NewRouter()
|
||||
r.Get("/", getConnections(trafficManager))
|
||||
r.Delete("/", closeAllConnections(router, trafficManager))
|
||||
r.Delete("/", closeAllConnections(trafficManager))
|
||||
r.Delete("/{id}", closeConnection(trafficManager))
|
||||
return r
|
||||
}
|
||||
@@ -87,13 +86,12 @@ func closeConnection(trafficManager *trafficontrol.Manager) func(w http.Response
|
||||
}
|
||||
}
|
||||
|
||||
func closeAllConnections(router adapter.Router, trafficManager *trafficontrol.Manager) func(w http.ResponseWriter, r *http.Request) {
|
||||
func closeAllConnections(trafficManager *trafficontrol.Manager) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
snapshot := trafficManager.Snapshot()
|
||||
for _, c := range snapshot.Connections {
|
||||
c.Close()
|
||||
}
|
||||
router.ResetNetwork()
|
||||
render.NoContent(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ func getProxies(server *Server, router adapter.Router) func(w http.ResponseWrite
|
||||
defaultTag = allProxies[0]
|
||||
}
|
||||
|
||||
sort.SliceStable(allProxies, func(i, j int) bool {
|
||||
sort.Slice(allProxies, func(i, j int) bool {
|
||||
return allProxies[i] == defaultTag
|
||||
})
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/service/filemanager"
|
||||
"github.com/sagernet/websocket"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
@@ -38,7 +37,6 @@ func init() {
|
||||
var _ adapter.ClashServer = (*Server)(nil)
|
||||
|
||||
type Server struct {
|
||||
ctx context.Context
|
||||
router adapter.Router
|
||||
logger log.Logger
|
||||
httpServer *http.Server
|
||||
@@ -48,7 +46,6 @@ type Server struct {
|
||||
storeSelected bool
|
||||
storeFakeIP bool
|
||||
cacheFilePath string
|
||||
cacheID string
|
||||
cacheFile adapter.ClashCacheFile
|
||||
|
||||
externalUI string
|
||||
@@ -56,11 +53,10 @@ type Server struct {
|
||||
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()
|
||||
chiRouter := chi.NewRouter()
|
||||
server := &Server{
|
||||
ctx: ctx,
|
||||
router: router,
|
||||
logger: logFactory.NewLogger("clash-api"),
|
||||
httpServer: &http.Server{
|
||||
@@ -86,10 +82,9 @@ func NewServer(ctx context.Context, router adapter.Router, logFactory log.Observ
|
||||
if foundPath, loaded := C.FindPath(cachePath); loaded {
|
||||
cachePath = foundPath
|
||||
} else {
|
||||
cachePath = filemanager.BasePath(ctx, cachePath)
|
||||
cachePath = C.BasePath(cachePath)
|
||||
}
|
||||
server.cacheFilePath = cachePath
|
||||
server.cacheID = options.CacheID
|
||||
}
|
||||
cors := cors.New(cors.Options{
|
||||
AllowedOrigins: []string{"*"},
|
||||
@@ -107,7 +102,7 @@ func NewServer(ctx context.Context, router adapter.Router, logFactory log.Observ
|
||||
r.Mount("/configs", configRouter(server, logFactory, server.logger))
|
||||
r.Mount("/proxies", proxyRouter(server, router))
|
||||
r.Mount("/rules", ruleRouter(router))
|
||||
r.Mount("/connections", connectionRouter(router, trafficManager))
|
||||
r.Mount("/connections", connectionRouter(trafficManager))
|
||||
r.Mount("/providers/proxies", proxyProviderRouter())
|
||||
r.Mount("/providers/rules", ruleProviderRouter())
|
||||
r.Mount("/script", scriptRouter())
|
||||
@@ -118,7 +113,7 @@ func NewServer(ctx context.Context, router adapter.Router, logFactory log.Observ
|
||||
server.setupMetaAPI(r)
|
||||
})
|
||||
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) {
|
||||
fs := http.StripPrefix("/ui", http.FileServer(http.Dir(server.externalUI)))
|
||||
r.Get("/ui", http.RedirectHandler("/ui/", http.StatusTemporaryRedirect).ServeHTTP)
|
||||
@@ -132,7 +127,7 @@ func NewServer(ctx context.Context, router adapter.Router, logFactory log.Observ
|
||||
|
||||
func (s *Server) PreStart() error {
|
||||
if s.cacheFilePath != "" {
|
||||
cacheFile, err := cachefile.Open(s.cacheFilePath, s.cacheID)
|
||||
cacheFile, err := cachefile.Open(s.cacheFilePath)
|
||||
if err != nil {
|
||||
return E.Cause(err, "open cache file")
|
||||
}
|
||||
|
||||
@@ -12,11 +12,11 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing/common"
|
||||
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/service/filemanager"
|
||||
)
|
||||
|
||||
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 {
|
||||
tempFile, err := filemanager.CreateTemp(s.ctx, name)
|
||||
tempFile, err := C.CreateTemp(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -112,7 +112,7 @@ func (s *Server) downloadZIP(name string, body io.Reader, output string) error {
|
||||
return err
|
||||
}
|
||||
savePath := filepath.Join(saveDirectory, pathElements[len(pathElements)-1])
|
||||
err = downloadZIPEntry(s.ctx, file, savePath)
|
||||
err = downloadZIPEntry(file, savePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -120,8 +120,8 @@ func (s *Server) downloadZIP(name string, body io.Reader, output string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func downloadZIPEntry(ctx context.Context, zipFile *zip.File, savePath string) error {
|
||||
saveFile, err := filemanager.Create(ctx, savePath)
|
||||
func downloadZIPEntry(zipFile *zip.File, savePath string) error {
|
||||
saveFile, err := os.Create(savePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/experimental/trackerconn"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/atomic"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
"github.com/gofrs/uuid/v5"
|
||||
@@ -115,13 +115,13 @@ func NewTCPTracker(conn net.Conn, manager *Manager, metadata Metadata, router ad
|
||||
download := new(atomic.Int64)
|
||||
|
||||
t := &tcpTracker{
|
||||
ExtendedConn: bufio.NewCounterConn(conn, []N.CountFunc{func(n int64) {
|
||||
ExtendedConn: trackerconn.NewHook(conn, func(n int64) {
|
||||
upload.Add(n)
|
||||
manager.PushUploaded(n)
|
||||
}}, []N.CountFunc{func(n int64) {
|
||||
}, func(n int64) {
|
||||
download.Add(n)
|
||||
manager.PushDownloaded(n)
|
||||
}}),
|
||||
}),
|
||||
manager: manager,
|
||||
trackerInfo: &trackerInfo{
|
||||
UUID: uuid,
|
||||
@@ -202,13 +202,13 @@ func NewUDPTracker(conn N.PacketConn, manager *Manager, metadata Metadata, route
|
||||
download := new(atomic.Int64)
|
||||
|
||||
ut := &udpTracker{
|
||||
PacketConn: bufio.NewCounterPacketConn(conn, []N.CountFunc{func(n int64) {
|
||||
PacketConn: trackerconn.NewHookPacket(conn, func(n int64) {
|
||||
upload.Add(n)
|
||||
manager.PushUploaded(n)
|
||||
}}, []N.CountFunc{func(n int64) {
|
||||
}, func(n int64) {
|
||||
download.Add(n)
|
||||
manager.PushDownloaded(n)
|
||||
}}),
|
||||
}),
|
||||
manager: manager,
|
||||
trackerInfo: &trackerInfo{
|
||||
UUID: uuid,
|
||||
|
||||
@@ -29,19 +29,3 @@ func (i *iterator[T]) Next() T {
|
||||
func (i *iterator[T]) HasNext() bool {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -27,27 +27,3 @@ func RedirectStderr(path string) error {
|
||||
stderrFile = outputFile
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -1,11 +1,8 @@
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"github.com/sagernet/sing-box/option"
|
||||
)
|
||||
import "github.com/sagernet/sing-box/option"
|
||||
|
||||
type PlatformInterface interface {
|
||||
UsePlatformAutoDetectInterfaceControl() bool
|
||||
AutoDetectInterfaceControl(fd int32) error
|
||||
OpenTun(options TunOptions) (int32, error)
|
||||
WriteLog(message string)
|
||||
@@ -13,12 +10,6 @@ type PlatformInterface interface {
|
||||
FindConnectionOwner(ipProtocol int32, sourceAddress string, sourcePort int32, destinationAddress string, destinationPort int32) (int32, error)
|
||||
PackageNameByUid(uid int32) (string, error)
|
||||
UIDByPackageName(packageName string) (int32, error)
|
||||
UsePlatformDefaultInterfaceMonitor() bool
|
||||
StartDefaultInterfaceMonitor(listener InterfaceUpdateListener) error
|
||||
CloseDefaultInterfaceMonitor(listener InterfaceUpdateListener) error
|
||||
UsePlatformInterfaceGetter() bool
|
||||
GetInterfaces() (NetworkInterfaceIterator, error)
|
||||
UnderNetworkExtension() bool
|
||||
}
|
||||
|
||||
type TunInterface interface {
|
||||
@@ -26,19 +17,8 @@ type TunInterface interface {
|
||||
Close() error
|
||||
}
|
||||
|
||||
type InterfaceUpdateListener interface {
|
||||
UpdateDefaultInterface(interfaceName string, interfaceIndex int32)
|
||||
}
|
||||
|
||||
type NetworkInterface struct {
|
||||
Index int32
|
||||
MTU int32
|
||||
Name string
|
||||
Addresses StringIterator
|
||||
}
|
||||
|
||||
type NetworkInterfaceIterator interface {
|
||||
Next() *NetworkInterface
|
||||
type OnDemandRuleIterator interface {
|
||||
Next() OnDemandRule
|
||||
HasNext() bool
|
||||
}
|
||||
|
||||
@@ -51,11 +31,6 @@ type OnDemandRule interface {
|
||||
ProbeURL() string
|
||||
}
|
||||
|
||||
type OnDemandRuleIterator interface {
|
||||
Next() OnDemandRule
|
||||
HasNext() bool
|
||||
}
|
||||
|
||||
type onDemandRule struct {
|
||||
option.OnDemandRule
|
||||
}
|
||||
|
||||
@@ -1,35 +1,17 @@
|
||||
package platform
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/netip"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/process"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
type Interface interface {
|
||||
Initialize(ctx context.Context, router adapter.Router) error
|
||||
UsePlatformAutoDetectInterfaceControl() bool
|
||||
AutoDetectInterfaceControl() control.Func
|
||||
OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error)
|
||||
UsePlatformDefaultInterfaceMonitor() bool
|
||||
CreateDefaultInterfaceMonitor(errorHandler E.Handler) tun.DefaultInterfaceMonitor
|
||||
UsePlatformInterfaceGetter() bool
|
||||
Interfaces() ([]NetworkInterface, error)
|
||||
UnderNetworkExtension() bool
|
||||
OpenTun(options tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error)
|
||||
process.Searcher
|
||||
io.Writer
|
||||
}
|
||||
|
||||
type NetworkInterface struct {
|
||||
Index int
|
||||
MTU int
|
||||
Name string
|
||||
Addresses []netip.Prefix
|
||||
}
|
||||
|
||||
@@ -6,17 +6,14 @@ import (
|
||||
"syscall"
|
||||
|
||||
"github.com/sagernet/sing-box"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/process"
|
||||
"github.com/sagernet/sing-box/experimental/libbox/internal/procfs"
|
||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/service/filemanager"
|
||||
)
|
||||
|
||||
type BoxService struct {
|
||||
@@ -31,11 +28,10 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box
|
||||
return nil, err
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx = filemanager.WithDefault(ctx, sBasePath, sTempPath, sUserID, sGroupID)
|
||||
instance, err := box.New(box.Options{
|
||||
Context: ctx,
|
||||
Options: options,
|
||||
PlatformInterface: &platformInterfaceWrapper{iif: platformInterface, useProcFS: platformInterface.UseProcFS()},
|
||||
PlatformInterface: &platformInterfaceWrapper{platformInterface, platformInterface.UseProcFS()},
|
||||
})
|
||||
if err != nil {
|
||||
cancel()
|
||||
@@ -62,16 +58,6 @@ var _ platform.Interface = (*platformInterfaceWrapper)(nil)
|
||||
type platformInterfaceWrapper struct {
|
||||
iif PlatformInterface
|
||||
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 {
|
||||
@@ -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 {
|
||||
return nil, E.New("android: unsupported uid options")
|
||||
}
|
||||
@@ -93,16 +79,12 @@ func (w *platformInterfaceWrapper) OpenTun(options *tun.Options, platformOptions
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options.Name, err = getTunnelName(tunFd)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "query tun name")
|
||||
}
|
||||
dupFd, err := dup(int(tunFd))
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "dup tun file descriptor")
|
||||
}
|
||||
options.FileDescriptor = dupFd
|
||||
return tun.New(*options)
|
||||
return tun.New(options)
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) Write(p []byte) (n int, err error) {
|
||||
@@ -136,40 +118,3 @@ func (w *platformInterfaceWrapper) FindProcessInfo(ctx context.Context, network
|
||||
packageName, _ := w.iif.PackageNameByUid(uid)
|
||||
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
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) UnderNetworkExtension() bool {
|
||||
return w.iif.UnderNetworkExtension()
|
||||
}
|
||||
|
||||
@@ -1,31 +1,17 @@
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
)
|
||||
|
||||
var (
|
||||
sBasePath string
|
||||
sTempPath string
|
||||
sUserID int
|
||||
sGroupID int
|
||||
)
|
||||
func SetBasePath(path string) {
|
||||
C.SetBasePath(path)
|
||||
}
|
||||
|
||||
func Setup(basePath string, tempPath string, userID int, groupID int) {
|
||||
sBasePath = basePath
|
||||
sTempPath = tempPath
|
||||
sUserID = userID
|
||||
sGroupID = groupID
|
||||
if sUserID == -1 {
|
||||
sUserID = os.Getuid()
|
||||
}
|
||||
if sGroupID == -1 {
|
||||
sGroupID = os.Getgid()
|
||||
}
|
||||
func SetTempPath(path string) {
|
||||
C.SetTempPath(path)
|
||||
}
|
||||
|
||||
func Version() string {
|
||||
|
||||
@@ -59,7 +59,7 @@ func mapRoutePrefix(prefixes []netip.Prefix) RoutePrefixIterator {
|
||||
var _ TunOptions = (*tunOptions)(nil)
|
||||
|
||||
type tunOptions struct {
|
||||
*tun.Options
|
||||
tun.Options
|
||||
option.TunPlatformOptions
|
||||
}
|
||||
|
||||
|
||||
@@ -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 */
|
||||
)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
//go:build !(darwin || linux)
|
||||
|
||||
package libbox
|
||||
|
||||
import "os"
|
||||
|
||||
func getTunnelName(fd int32) (string, error) {
|
||||
return "", os.ErrInvalid
|
||||
}
|
||||
108
experimental/trackerconn/conn.go
Normal file
108
experimental/trackerconn/conn.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package trackerconn
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing/common/atomic"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
func New(conn net.Conn, readCounter []*atomic.Int64, writeCounter []*atomic.Int64) *Conn {
|
||||
return &Conn{bufio.NewExtendedConn(conn), readCounter, writeCounter}
|
||||
}
|
||||
|
||||
func NewHook(conn net.Conn, readCounter func(n int64), writeCounter func(n int64)) *HookConn {
|
||||
return &HookConn{bufio.NewExtendedConn(conn), readCounter, writeCounter}
|
||||
}
|
||||
|
||||
type Conn struct {
|
||||
N.ExtendedConn
|
||||
readCounter []*atomic.Int64
|
||||
writeCounter []*atomic.Int64
|
||||
}
|
||||
|
||||
func (c *Conn) Read(p []byte) (n int, err error) {
|
||||
n, err = c.ExtendedConn.Read(p)
|
||||
for _, counter := range c.readCounter {
|
||||
counter.Add(int64(n))
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (c *Conn) ReadBuffer(buffer *buf.Buffer) error {
|
||||
err := c.ExtendedConn.ReadBuffer(buffer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, counter := range c.readCounter {
|
||||
counter.Add(int64(buffer.Len()))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) Write(p []byte) (n int, err error) {
|
||||
n, err = c.ExtendedConn.Write(p)
|
||||
for _, counter := range c.writeCounter {
|
||||
counter.Add(int64(n))
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (c *Conn) WriteBuffer(buffer *buf.Buffer) error {
|
||||
dataLen := int64(buffer.Len())
|
||||
err := c.ExtendedConn.WriteBuffer(buffer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, counter := range c.writeCounter {
|
||||
counter.Add(dataLen)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) Upstream() any {
|
||||
return c.ExtendedConn
|
||||
}
|
||||
|
||||
type HookConn struct {
|
||||
N.ExtendedConn
|
||||
readCounter func(n int64)
|
||||
writeCounter func(n int64)
|
||||
}
|
||||
|
||||
func (c *HookConn) Read(p []byte) (n int, err error) {
|
||||
n, err = c.ExtendedConn.Read(p)
|
||||
c.readCounter(int64(n))
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (c *HookConn) ReadBuffer(buffer *buf.Buffer) error {
|
||||
err := c.ExtendedConn.ReadBuffer(buffer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.readCounter(int64(buffer.Len()))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *HookConn) Write(p []byte) (n int, err error) {
|
||||
n, err = c.ExtendedConn.Write(p)
|
||||
c.writeCounter(int64(n))
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (c *HookConn) WriteBuffer(buffer *buf.Buffer) error {
|
||||
dataLen := int64(buffer.Len())
|
||||
err := c.ExtendedConn.WriteBuffer(buffer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.writeCounter(dataLen)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *HookConn) Upstream() any {
|
||||
return c.ExtendedConn
|
||||
}
|
||||
76
experimental/trackerconn/packet_conn.go
Normal file
76
experimental/trackerconn/packet_conn.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package trackerconn
|
||||
|
||||
import (
|
||||
"github.com/sagernet/sing/common/atomic"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
func NewPacket(conn N.PacketConn, readCounter []*atomic.Int64, writeCounter []*atomic.Int64) *PacketConn {
|
||||
return &PacketConn{conn, readCounter, writeCounter}
|
||||
}
|
||||
|
||||
func NewHookPacket(conn N.PacketConn, readCounter func(n int64), writeCounter func(n int64)) *HookPacketConn {
|
||||
return &HookPacketConn{conn, readCounter, writeCounter}
|
||||
}
|
||||
|
||||
type PacketConn struct {
|
||||
N.PacketConn
|
||||
readCounter []*atomic.Int64
|
||||
writeCounter []*atomic.Int64
|
||||
}
|
||||
|
||||
func (c *PacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
|
||||
destination, err = c.PacketConn.ReadPacket(buffer)
|
||||
if err == nil {
|
||||
for _, counter := range c.readCounter {
|
||||
counter.Add(int64(buffer.Len()))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *PacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
dataLen := int64(buffer.Len())
|
||||
err := c.PacketConn.WritePacket(buffer, destination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, counter := range c.writeCounter {
|
||||
counter.Add(dataLen)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *PacketConn) Upstream() any {
|
||||
return c.PacketConn
|
||||
}
|
||||
|
||||
type HookPacketConn struct {
|
||||
N.PacketConn
|
||||
readCounter func(n int64)
|
||||
writeCounter func(n int64)
|
||||
}
|
||||
|
||||
func (c *HookPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
|
||||
destination, err = c.PacketConn.ReadPacket(buffer)
|
||||
if err == nil {
|
||||
c.readCounter(int64(buffer.Len()))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *HookPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
dataLen := int64(buffer.Len())
|
||||
err := c.PacketConn.WritePacket(buffer, destination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.writeCounter(dataLen)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *HookPacketConn) Upstream() any {
|
||||
return c.PacketConn
|
||||
}
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/experimental/trackerconn"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common/atomic"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
@@ -83,7 +83,7 @@ func (s *StatsService) RoutedConnection(inbound string, outbound string, user st
|
||||
writeCounter = append(writeCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>downlink"))
|
||||
}
|
||||
s.access.Unlock()
|
||||
return bufio.NewInt64CounterConn(conn, readCounter, writeCounter)
|
||||
return trackerconn.New(conn, readCounter, writeCounter)
|
||||
}
|
||||
|
||||
func (s *StatsService) RoutedPacketConnection(inbound string, outbound string, user string, conn N.PacketConn) N.PacketConn {
|
||||
@@ -109,7 +109,7 @@ func (s *StatsService) RoutedPacketConnection(inbound string, outbound string, u
|
||||
writeCounter = append(writeCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>downlink"))
|
||||
}
|
||||
s.access.Unlock()
|
||||
return bufio.NewInt64CounterPacketConn(conn, readCounter, writeCounter)
|
||||
return trackerconn.NewPacket(conn, readCounter, writeCounter)
|
||||
}
|
||||
|
||||
func (s *StatsService) GetStats(ctx context.Context, request *GetStatsRequest) (*GetStatsResponse, error) {
|
||||
|
||||
32
go.mod
32
go.mod
@@ -4,7 +4,7 @@ go 1.18
|
||||
|
||||
require (
|
||||
berty.tech/go-libtor v1.0.385
|
||||
github.com/Dreamacro/clash v1.15.1
|
||||
github.com/Dreamacro/clash v1.14.0
|
||||
github.com/caddyserver/certmagic v0.17.2
|
||||
github.com/cretz/bine v0.2.0
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
@@ -13,10 +13,11 @@ require (
|
||||
github.com/go-chi/cors v1.2.1
|
||||
github.com/go-chi/render v1.0.2
|
||||
github.com/gofrs/uuid/v5 v5.0.0
|
||||
github.com/hashicorp/yamux v0.1.1
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||
github.com/mholt/acmez v1.1.0
|
||||
github.com/miekg/dns v1.1.54
|
||||
github.com/miekg/dns v1.1.53
|
||||
github.com/ooni/go-libtor v1.1.7
|
||||
github.com/oschwald/maxminddb-golang v1.10.0
|
||||
github.com/pires/go-proxyproto v0.7.0
|
||||
@@ -24,32 +25,30 @@ require (
|
||||
github.com/sagernet/gomobile v0.0.0-20230413023804-244d7ff07035
|
||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
||||
github.com/sagernet/sing v0.2.5-0.20230517124404-be6013893626
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230426113254-25d948c44223
|
||||
github.com/sagernet/sing-mux v0.0.0-20230517134606-1ebe6bb26646
|
||||
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230509053848-d83f8fe1194c
|
||||
github.com/sagernet/sing-shadowsocks2 v0.0.0-20230605050515-45409ce8283d
|
||||
github.com/sagernet/sing-shadowtls v0.1.2-0.20230531025805-ebadc7615da3
|
||||
github.com/sagernet/sing-tun v0.1.5-0.20230520041100-b02f2529160e
|
||||
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3
|
||||
github.com/sagernet/sing v0.2.3-0.20230413023204-48b019b13e78
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230408004833-5adaf486d440
|
||||
github.com/sagernet/sing-shadowsocks v0.2.1-0.20230412123110-1a7c32b4e2e7
|
||||
github.com/sagernet/sing-shadowtls v0.1.1-0.20230409094821-9abef019436f
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab
|
||||
github.com/sagernet/sing-vmess v0.1.4-0.20230412122845-9470e68f5e45
|
||||
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37
|
||||
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9
|
||||
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2
|
||||
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/stretchr/testify v1.8.2
|
||||
go.etcd.io/bbolt v1.3.7
|
||||
go.uber.org/zap v1.24.0
|
||||
go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35
|
||||
golang.org/x/crypto v0.8.0
|
||||
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29
|
||||
golang.org/x/net v0.9.0
|
||||
golang.org/x/sys v0.7.0
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230215201556-9c5414ab4bde
|
||||
google.golang.org/grpc v1.54.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
|
||||
@@ -59,13 +58,11 @@ require (
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
github.com/cloudflare/circl v1.2.1-0.20221019164342-6ab4dfed8f3c // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/josharian/native v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.15.15 // indirect
|
||||
@@ -81,7 +78,6 @@ require (
|
||||
github.com/quic-go/qtls-go1-20 v0.1.0 // indirect
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
|
||||
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||
@@ -89,7 +85,7 @@ require (
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
golang.org/x/mod v0.8.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
|
||||
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||
|
||||
63
go.sum
63
go.sum
@@ -1,7 +1,7 @@
|
||||
berty.tech/go-libtor v1.0.385 h1:RWK94C3hZj6Z2GdvePpHJLnWYobFr3bY/OdUJ5aoEXw=
|
||||
berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+fJEw=
|
||||
github.com/Dreamacro/clash v1.15.1 h1:AwwtrVYbeXg9ZCxpPxAurGXwaR3FxDdHv3rB/vAGXj4=
|
||||
github.com/Dreamacro/clash v1.15.1/go.mod h1:WNH69bN11LiAdgdSr4hpkEuXVMfBbWyhEKMCTx9BtNE=
|
||||
github.com/Dreamacro/clash v1.14.0 h1:ehJ/C/1m9LEjmME72WSE/Y2YqbR3Q54AbjqiRCvtyW4=
|
||||
github.com/Dreamacro/clash v1.14.0/go.mod h1:ia2CU7V713H1QdCqMwOLK9U9V5Ay8X0voj3yQr2tk+I=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||
@@ -31,8 +31,6 @@ github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
|
||||
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||
github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg=
|
||||
github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
|
||||
@@ -72,8 +70,8 @@ github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczG
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
github.com/mholt/acmez v1.1.0 h1:IQ9CGHKOHokorxnffsqDvmmE30mDenO1lptYZ1AYkHY=
|
||||
github.com/mholt/acmez v1.1.0/go.mod h1:zwo5+fbLLTowAX8o8ETfQzbDtwGEXnPhkmGdKIP+bgs=
|
||||
github.com/miekg/dns v1.1.54 h1:5jon9mWcb0sFJGpnI99tOMhCPyJ+RPVz5b63MQG0VWI=
|
||||
github.com/miekg/dns v1.1.54/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
|
||||
github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw=
|
||||
github.com/miekg/dns v1.1.53/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI=
|
||||
@@ -103,6 +101,8 @@ github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0 h1:KyhtFFt
|
||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0/go.mod h1:D4SFEOkJK+4W1v86ZhX0jPM0rAL498fyQAChqMtes/I=
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
|
||||
github.com/sagernet/gomobile v0.0.0-20230413023437-ec061884b992 h1:WkcHhOX3ce9ElLKDUQKJrAt7SjpKNnASsPbMfqfZEPc=
|
||||
github.com/sagernet/gomobile v0.0.0-20230413023437-ec061884b992/go.mod h1:5YE39YkJkCcMsfq1jMKkjsrM2GfBoF9JVWnvU89hmvU=
|
||||
github.com/sagernet/gomobile v0.0.0-20230413023804-244d7ff07035 h1:KttYh6bBhIw8Y6/Ljn7CGwC3CKZn788rzMJmeAKjY+8=
|
||||
github.com/sagernet/gomobile v0.0.0-20230413023804-244d7ff07035/go.mod h1:5YE39YkJkCcMsfq1jMKkjsrM2GfBoF9JVWnvU89hmvU=
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
||||
@@ -113,22 +113,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/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.2.5-0.20230517124404-be6013893626 h1:2TlOqs05+PXadWOubVBhXM27/UaI1If2k/ISjtYZiPE=
|
||||
github.com/sagernet/sing v0.2.5-0.20230517124404-be6013893626/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230426113254-25d948c44223 h1:L4eMuM07iSHY3UCknFnuFuHoe5clZuF2Xnf2wwA6Lwc=
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230426113254-25d948c44223/go.mod h1:ZKuuqgsHRxDahYrzgSgy4vIAGGuKPlIf4hLcNzYzLkY=
|
||||
github.com/sagernet/sing-mux v0.0.0-20230517134606-1ebe6bb26646 h1:X3ADfMqeGns1Q1FlXc9kaL9FwW1UM6D6tEQo8jFstpc=
|
||||
github.com/sagernet/sing-mux v0.0.0-20230517134606-1ebe6bb26646/go.mod h1:pF+RnLvCAOhECrvauy6LYOpBakJ/vuaF1Wm4lPsWryI=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230509053848-d83f8fe1194c h1:EiQ+i4gdPpSI8D2YUlOeBZA3R1ZGi0ShSLSXoSd/13A=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230509053848-d83f8fe1194c/go.mod h1:UJjvQGw0lyYaDGIDvUraL16fwaAEH1WFw1Y6sUcMPog=
|
||||
github.com/sagernet/sing-shadowsocks2 v0.0.0-20230605050515-45409ce8283d h1:obf7pm6aRav465lIGPGN8fx8pD8iWg15RQhwGwkyNNc=
|
||||
github.com/sagernet/sing-shadowsocks2 v0.0.0-20230605050515-45409ce8283d/go.mod h1:i6Eoor37Cy4JKBcelMMFfUVBNjqRYuvcqOBjUI3Zw80=
|
||||
github.com/sagernet/sing-shadowtls v0.1.2-0.20230531025805-ebadc7615da3 h1:PNwJs1F+3e/iZguYQR7YzxsH8Sm0Eu7vVuHawD89r34=
|
||||
github.com/sagernet/sing-shadowtls v0.1.2-0.20230531025805-ebadc7615da3/go.mod h1:oG8bPerYI6cZ74KquY3DvA7ynECyrILPBnce6wtBqeI=
|
||||
github.com/sagernet/sing-tun v0.1.5-0.20230520041100-b02f2529160e h1:fivkzQowiOPBafklJmNePBu/sr83rsY5SuSakvoJ2A0=
|
||||
github.com/sagernet/sing-tun v0.1.5-0.20230520041100-b02f2529160e/go.mod h1:6yju6cNdTow38IZHwcbFA3lHnHeSd5HG/zCzHyaxScI=
|
||||
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 h1:BHOnxrbC929JonuKqFdJ7ZbDp7zs4oTlH5KFvKtWu9U=
|
||||
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3/go.mod h1:yKrAr+dqZd64DxBXCHWrYicp+n4qbqO73mtwv3dck8U=
|
||||
github.com/sagernet/sing v0.2.3-0.20230413023204-48b019b13e78 h1:bTE9RgURmiFiTjXaYN6q9BYPTBPKIzlFYNy2p+WOubI=
|
||||
github.com/sagernet/sing v0.2.3-0.20230413023204-48b019b13e78/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
|
||||
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.20230408004833-5adaf486d440/go.mod h1:69PNSHyEmXdjf6C+bXBOdr2GQnPeEyWjIzo/MV8gmz8=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.1-0.20230412123110-1a7c32b4e2e7 h1:3WDMIF1aE/twc5gJ+9PF2ZJqUxwZ80MPtNBKE3yBevU=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.1-0.20230412123110-1a7c32b4e2e7/go.mod h1:WoVjGUvRqsx5yhYeDAB5CijCHpNDi0LUPHl3cf7u8Lc=
|
||||
github.com/sagernet/sing-shadowtls v0.1.1-0.20230409094821-9abef019436f h1:qzQvpcDm60zPW8UlZa8UEaBoFORFeGAnhDncPc3VWT4=
|
||||
github.com/sagernet/sing-shadowtls v0.1.1-0.20230409094821-9abef019436f/go.mod h1:MxB+Q9H0pAHcrlvNmwSs1crljRwHFFVhtXyOMBy44Nw=
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab h1:a9oeWuPBuIZ70qMhIIH6RrYhp886xN9jJIwsuu4ZFUo=
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab/go.mod h1:4YxIDEkkCjGXDOTMPw1SXpLmCQUFAWuaQN250oo+928=
|
||||
github.com/sagernet/sing-vmess v0.1.4-0.20230412122845-9470e68f5e45 h1:QqYhWah3u+o2tvLRuTfEu3BwsGpf/wNnVK/VNQV2YBM=
|
||||
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/go.mod h1:3skNSftZDJWTGVtVaM2jfbce8qHnmH/AGDRe62iNOg0=
|
||||
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE=
|
||||
@@ -137,10 +133,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/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/wireguard-go v0.0.0-20230420044414-a7bac1754e77 h1:g6QtRWQ2dKX7EQP++1JLNtw4C2TNxd4/ov8YUpOPOSo=
|
||||
github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77/go.mod h1:pJDdXzZIwJ+2vmnT0TKzmf8meeum+e2mTDSehw79eE0=
|
||||
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh8n+csdOuDfP+NuykE0U6AeYSJJHKDgSg=
|
||||
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9/go.mod h1:a/83NAfUXvEuLpmxDssAXxgUgrEy12MId3Wd7OTs76s=
|
||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo=
|
||||
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/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
@@ -178,8 +172,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
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/exp v0.0.0-20230425010034-47ecfdc1ba53 h1:5llv2sWeaMSnA3w2kS57ouQQ4pudlXrR0dCgw51QK9o=
|
||||
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
|
||||
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/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
@@ -196,7 +190,6 @@ 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-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-20190916202348-b4ddaad3f8a3/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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -217,8 +210,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.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
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-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
||||
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-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
@@ -228,8 +221,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230215201556-9c5414ab4bde h1:ybF7AMzIUikL9x4LgwEmzhXtzRpKNqngme1VGDWz+Nk=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230215201556-9c5414ab4bde/go.mod h1:mQqgjkW8GQQcJQsbBvK890TKqUK1DfKWkuBGbOkuMHQ=
|
||||
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w=
|
||||
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
|
||||
google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag=
|
||||
@@ -245,7 +238,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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
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-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/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=
|
||||
|
||||
@@ -609,10 +609,6 @@ func (c *naiveH2Conn) SetWriteDeadline(t time.Time) error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (c *naiveH2Conn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *naiveH2Conn) UpstreamReader() any {
|
||||
return c.reader
|
||||
}
|
||||
|
||||
@@ -9,8 +9,6 @@ import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"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/common"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
@@ -27,7 +25,7 @@ var (
|
||||
|
||||
type ShadowsocksMulti struct {
|
||||
myInboundAdapter
|
||||
service shadowsocks.MultiService[int]
|
||||
service *shadowaead_2022.MultiService[int]
|
||||
users []option.ShadowsocksUser
|
||||
}
|
||||
|
||||
@@ -51,26 +49,16 @@ func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.
|
||||
} else {
|
||||
udpTimeout = int64(C.UDPTimeout.Seconds())
|
||||
}
|
||||
var (
|
||||
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 {
|
||||
if !common.Contains(shadowaead_2022.List, 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 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ func (t *Tun) Start() error {
|
||||
)
|
||||
t.logger.Trace("opening interface")
|
||||
if t.platformInterface != nil {
|
||||
tunInterface, err = t.platformInterface.OpenTun(&t.tunOptions, t.platformOptions)
|
||||
tunInterface, err = t.platformInterface.OpenTun(t.tunOptions, t.platformOptions)
|
||||
} else {
|
||||
tunInterface, err = tun.New(t.tunOptions)
|
||||
}
|
||||
@@ -177,8 +177,7 @@ func (t *Tun) Start() error {
|
||||
Router: tunRouter,
|
||||
Handler: t,
|
||||
Logger: t.logger,
|
||||
ForwarderBindInterface: t.platformInterface != nil,
|
||||
InterfaceFinder: t.router.InterfaceFinder(),
|
||||
UnderPlatform: t.platformInterface != nil,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/sagernet/sing-vmess/packetaddr"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
"github.com/sagernet/sing/common/bufio/deadline"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
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 {
|
||||
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")
|
||||
} else {
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/sagernet/sing-vmess/packetaddr"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
"github.com/sagernet/sing/common/bufio/deadline"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
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 {
|
||||
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")
|
||||
} else {
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
package include
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/experimental"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
@@ -13,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
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`)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/service/filemanager"
|
||||
)
|
||||
|
||||
type factoryWithFile struct {
|
||||
@@ -37,7 +36,6 @@ func (f *observableFactoryWithFile) Close() error {
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
Context context.Context
|
||||
Options option.LogOptions
|
||||
Observable bool
|
||||
DefaultWriter io.Writer
|
||||
@@ -67,7 +65,7 @@ func New(options Options) (Factory, error) {
|
||||
logWriter = os.Stdout
|
||||
default:
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -115,7 +115,6 @@ nav:
|
||||
- ShadowTLS: examples/shadowtls.md
|
||||
- Clash API: examples/clash-api.md
|
||||
- WireGuard Direct: examples/wireguard-direct.md
|
||||
- FakeIP: examples/fakeip.md
|
||||
- Contributing:
|
||||
- contributing/index.md
|
||||
- Developing:
|
||||
@@ -148,7 +147,6 @@ extra:
|
||||
link: https://github.com/SagerNet/sing-box
|
||||
generator: false
|
||||
plugins:
|
||||
- search
|
||||
- i18n:
|
||||
default_language: en
|
||||
languages:
|
||||
@@ -194,4 +192,4 @@ plugins:
|
||||
Known Issues: 已知问题
|
||||
Examples: 示例
|
||||
Linux Server Installation: Linux 服务器安装
|
||||
DNS Hijack: DNS 劫持
|
||||
DNS Hijack: DNS 劫持
|
||||
@@ -10,7 +10,6 @@ type ClashAPIOptions struct {
|
||||
StoreSelected bool `json:"store_selected,omitempty"`
|
||||
StoreFakeIP bool `json:"store_fakeip,omitempty"`
|
||||
CacheFile string `json:"cache_file,omitempty"`
|
||||
CacheID string `json:"cache_id,omitempty"`
|
||||
}
|
||||
|
||||
type SelectorOutboundOptions struct {
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
)
|
||||
|
||||
type DebugOptions struct {
|
||||
Listen string `json:"listen,omitempty"`
|
||||
GCPercent *int `json:"gc_percent,omitempty"`
|
||||
MaxStack *int `json:"max_stack,omitempty"`
|
||||
MaxThreads *int `json:"max_threads,omitempty"`
|
||||
|
||||
@@ -20,10 +20,9 @@ type DNSServerOptions struct {
|
||||
}
|
||||
|
||||
type DNSClientOptions struct {
|
||||
Strategy DomainStrategy `json:"strategy,omitempty"`
|
||||
DisableCache bool `json:"disable_cache,omitempty"`
|
||||
DisableExpire bool `json:"disable_expire,omitempty"`
|
||||
IndependentCache bool `json:"independent_cache,omitempty"`
|
||||
Strategy DomainStrategy `json:"strategy,omitempty"`
|
||||
DisableCache bool `json:"disable_cache,omitempty"`
|
||||
DisableExpire bool `json:"disable_expire,omitempty"`
|
||||
}
|
||||
|
||||
type DNSFakeIPOptions struct {
|
||||
|
||||
@@ -150,5 +150,4 @@ type MultiplexOptions struct {
|
||||
MaxConnections int `json:"max_connections,omitempty"`
|
||||
MinStreams int `json:"min_streams,omitempty"`
|
||||
MaxStreams int `json:"max_streams,omitempty"`
|
||||
Padding bool `json:"padding,omitempty"`
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ type ShadowsocksInboundOptions struct {
|
||||
ListenOptions
|
||||
Network NetworkList `json:"network,omitempty"`
|
||||
Method string `json:"method"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Password string `json:"password"`
|
||||
Users []ShadowsocksUser `json:"users,omitempty"`
|
||||
Destinations []ShadowsocksDestination `json:"destinations,omitempty"`
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ type VLESSOutboundOptions struct {
|
||||
Flow string `json:"flow,omitempty"`
|
||||
Network NetworkList `json:"network,omitempty"`
|
||||
TLS *OutboundTLSOptions `json:"tls,omitempty"`
|
||||
Multiplex *MultiplexOptions `json:"multiplex,omitempty"`
|
||||
Transport *V2RayTransportOptions `json:"transport,omitempty"`
|
||||
PacketEncoding *string `json:"packet_encoding,omitempty"`
|
||||
}
|
||||
|
||||
@@ -39,10 +39,6 @@ func (a *myOutboundAdapter) Network() []string {
|
||||
return a.network
|
||||
}
|
||||
|
||||
func (a *myOutboundAdapter) NewError(ctx context.Context, err error) {
|
||||
NewError(a.logger, ctx, err)
|
||||
}
|
||||
|
||||
func NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
ctx = adapter.WithContext(ctx, &metadata)
|
||||
var outConn net.Conn
|
||||
@@ -125,12 +121,3 @@ func CopyEarlyConn(ctx context.Context, conn net.Conn, serverConn net.Conn) erro
|
||||
}
|
||||
return bufio.CopyConn(ctx, conn, serverConn)
|
||||
}
|
||||
|
||||
func NewError(logger log.ContextLogger, ctx context.Context, err error) {
|
||||
common.Close(err)
|
||||
if E.IsClosedOrCanceled(err) {
|
||||
logger.DebugContext(ctx, "connection closed: ", err)
|
||||
return
|
||||
}
|
||||
logger.ErrorContext(ctx, err)
|
||||
}
|
||||
|
||||
144
outbound/dns.go
144
outbound/dns.go
@@ -11,7 +11,6 @@ import (
|
||||
"github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
"github.com/sagernet/sing/common/canceler"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
@@ -102,142 +101,29 @@ func (d *DNS) handleConnection(ctx context.Context, conn net.Conn, metadata adap
|
||||
}
|
||||
|
||||
func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
var reader N.PacketReader = conn
|
||||
var counters []N.CountFunc
|
||||
var cachedPackets []*N.PacketBuffer
|
||||
for {
|
||||
reader, counters = N.UnwrapCountPacketReader(reader, counters)
|
||||
if cachedReader, isCached := reader.(N.CachedPacketReader); isCached {
|
||||
packet := cachedReader.ReadCachedPacket()
|
||||
if packet != nil {
|
||||
cachedPackets = append(cachedPackets, packet)
|
||||
continue
|
||||
}
|
||||
}
|
||||
if readWaiter, created := bufio.CreatePacketReadWaiter(reader); created {
|
||||
return d.newPacketConnection(ctx, conn, readWaiter, counters, cachedPackets, metadata)
|
||||
}
|
||||
break
|
||||
}
|
||||
ctx = adapter.WithContext(ctx, &metadata)
|
||||
fastClose, cancel := common.ContextWithCancelCause(ctx)
|
||||
timeout := canceler.New(fastClose, cancel, C.DNSTimeout)
|
||||
var group task.Group
|
||||
group.Append0(func(ctx context.Context) error {
|
||||
_buffer := buf.StackNewSize(dns.FixedPacketSize)
|
||||
defer common.KeepAlive(_buffer)
|
||||
buffer := common.Dup(_buffer)
|
||||
defer buffer.Release()
|
||||
for {
|
||||
var message mDNS.Msg
|
||||
var destination M.Socksaddr
|
||||
var err error
|
||||
if len(cachedPackets) > 0 {
|
||||
packet := cachedPackets[0]
|
||||
cachedPackets = cachedPackets[1:]
|
||||
for _, counter := range counters {
|
||||
counter(int64(packet.Buffer.Len()))
|
||||
}
|
||||
err = message.Unpack(packet.Buffer.Bytes())
|
||||
packet.Buffer.Release()
|
||||
if err != nil {
|
||||
cancel(err)
|
||||
return err
|
||||
}
|
||||
destination = packet.Destination
|
||||
} else {
|
||||
buffer := buf.NewPacket()
|
||||
destination, err = conn.ReadPacket(buffer)
|
||||
if err != nil {
|
||||
buffer.Release()
|
||||
cancel(err)
|
||||
return err
|
||||
}
|
||||
for _, counter := range counters {
|
||||
counter(int64(buffer.Len()))
|
||||
}
|
||||
err = message.Unpack(buffer.Bytes())
|
||||
buffer.Release()
|
||||
if err != nil {
|
||||
cancel(err)
|
||||
return err
|
||||
}
|
||||
timeout.Update()
|
||||
}
|
||||
metadataInQuery := metadata
|
||||
go func() error {
|
||||
response, err := d.router.Exchange(adapter.WithContext(ctx, &metadataInQuery), &message)
|
||||
if err != nil {
|
||||
cancel(err)
|
||||
return err
|
||||
}
|
||||
timeout.Update()
|
||||
responseBuffer := buf.NewPacket()
|
||||
n, err := response.PackBuffer(responseBuffer.FreeBytes())
|
||||
if err != nil {
|
||||
cancel(err)
|
||||
responseBuffer.Release()
|
||||
return err
|
||||
}
|
||||
responseBuffer.Truncate(len(n))
|
||||
err = conn.WritePacket(responseBuffer, destination)
|
||||
if err != nil {
|
||||
cancel(err)
|
||||
}
|
||||
return err
|
||||
}()
|
||||
}
|
||||
})
|
||||
group.Cleanup(func() {
|
||||
conn.Close()
|
||||
})
|
||||
return group.Run(fastClose)
|
||||
}
|
||||
|
||||
func (d *DNS) newPacketConnection(ctx context.Context, conn N.PacketConn, readWaiter N.PacketReadWaiter, readCounters []N.CountFunc, cached []*N.PacketBuffer, metadata adapter.InboundContext) error {
|
||||
ctx = adapter.WithContext(ctx, &metadata)
|
||||
fastClose, cancel := common.ContextWithCancelCause(ctx)
|
||||
timeout := canceler.New(fastClose, cancel, C.DNSTimeout)
|
||||
var group task.Group
|
||||
group.Append0(func(ctx context.Context) error {
|
||||
var buffer *buf.Buffer
|
||||
readWaiter.InitializeReadWaiter(func() *buf.Buffer {
|
||||
buffer = buf.NewSize(dns.FixedPacketSize)
|
||||
buffer.FullReset()
|
||||
return buffer
|
||||
})
|
||||
defer readWaiter.InitializeReadWaiter(nil)
|
||||
for {
|
||||
var message mDNS.Msg
|
||||
var destination M.Socksaddr
|
||||
var err error
|
||||
if len(cached) > 0 {
|
||||
packet := cached[0]
|
||||
cached = cached[1:]
|
||||
for _, counter := range readCounters {
|
||||
counter(int64(packet.Buffer.Len()))
|
||||
}
|
||||
err = message.Unpack(packet.Buffer.Bytes())
|
||||
packet.Buffer.Release()
|
||||
if err != nil {
|
||||
cancel(err)
|
||||
return err
|
||||
}
|
||||
destination = packet.Destination
|
||||
} else {
|
||||
destination, err = readWaiter.WaitReadPacket()
|
||||
if err != nil {
|
||||
buffer.Release()
|
||||
cancel(err)
|
||||
return err
|
||||
}
|
||||
for _, counter := range readCounters {
|
||||
counter(int64(buffer.Len()))
|
||||
}
|
||||
err = message.Unpack(buffer.Bytes())
|
||||
buffer.Release()
|
||||
if err != nil {
|
||||
cancel(err)
|
||||
return err
|
||||
}
|
||||
timeout.Update()
|
||||
destination, err := conn.ReadPacket(buffer)
|
||||
if err != nil {
|
||||
cancel(err)
|
||||
return err
|
||||
}
|
||||
var message mDNS.Msg
|
||||
err = message.Unpack(buffer.Bytes())
|
||||
if err != nil {
|
||||
cancel(err)
|
||||
return err
|
||||
}
|
||||
timeout.Update()
|
||||
metadataInQuery := metadata
|
||||
go func() error {
|
||||
response, err := d.router.Exchange(adapter.WithContext(ctx, &metadataInQuery), &message)
|
||||
|
||||
@@ -11,7 +11,8 @@ import (
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/sip003"
|
||||
"github.com/sagernet/sing-shadowsocks2"
|
||||
"github.com/sagernet/sing-shadowsocks"
|
||||
"github.com/sagernet/sing-shadowsocks/shadowimpl"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
@@ -29,13 +30,11 @@ type Shadowsocks struct {
|
||||
serverAddr M.Socksaddr
|
||||
plugin sip003.Plugin
|
||||
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) {
|
||||
method, err := shadowsocks.CreateMethod(ctx, options.Method, shadowsocks.MethodOptions{
|
||||
Password: options.Password,
|
||||
})
|
||||
method, err := shadowimpl.FetchMethod(options.Method, options.Password, router.TimeFunc())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -59,7 +58,7 @@ func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
|
||||
}
|
||||
uotOptions := common.PtrValueOrDefault(options.UDPOverTCPOptions)
|
||||
if !uotOptions.Enabled {
|
||||
outbound.multiplexDialer, err = mux.NewClientWithOptions((*shadowsocksDialer)(outbound), common.PtrValueOrDefault(options.MultiplexOptions))
|
||||
outbound.multiplexDialer, err = mux.NewClientWithOptions(ctx, (*shadowsocksDialer)(outbound), common.PtrValueOrDefault(options.MultiplexOptions))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -128,15 +127,8 @@ func (h *Shadowsocks) NewPacketConnection(ctx context.Context, conn N.PacketConn
|
||||
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 {
|
||||
return common.Close(common.PtrOrNil(h.multiplexDialer))
|
||||
return common.Close(h.multiplexDialer)
|
||||
}
|
||||
|
||||
var _ N.Dialer = (*shadowsocksDialer)(nil)
|
||||
|
||||
@@ -27,7 +27,7 @@ type Trojan struct {
|
||||
dialer N.Dialer
|
||||
serverAddr M.Socksaddr
|
||||
key [56]byte
|
||||
multiplexDialer *mux.Client
|
||||
multiplexDialer N.Dialer
|
||||
tlsConfig tls.Config
|
||||
transport adapter.V2RayClientTransport
|
||||
}
|
||||
@@ -58,7 +58,7 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog
|
||||
return nil, E.Cause(err, "create client transport: ", options.Transport.Type)
|
||||
}
|
||||
}
|
||||
outbound.multiplexDialer, err = mux.NewClientWithOptions((*trojanDialer)(outbound), common.PtrValueOrDefault(options.Multiplex))
|
||||
outbound.multiplexDialer, err = mux.NewClientWithOptions(ctx, (*trojanDialer)(outbound), common.PtrValueOrDefault(options.Multiplex))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -103,15 +103,8 @@ func (h *Trojan) NewPacketConnection(ctx context.Context, conn N.PacketConn, met
|
||||
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 {
|
||||
return common.Close(common.PtrOrNil(h.multiplexDialer), h.transport)
|
||||
return common.Close(h.multiplexDialer, h.transport)
|
||||
}
|
||||
|
||||
type trojanDialer Trojan
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/atomic"
|
||||
"github.com/sagernet/sing/common/batch"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
@@ -21,9 +20,8 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
_ adapter.Outbound = (*URLTest)(nil)
|
||||
_ adapter.OutboundGroup = (*URLTest)(nil)
|
||||
_ adapter.InterfaceUpdateListener = (*URLTest)(nil)
|
||||
_ adapter.Outbound = (*URLTest)(nil)
|
||||
_ adapter.OutboundGroup = (*URLTest)(nil)
|
||||
)
|
||||
|
||||
type URLTest struct {
|
||||
@@ -73,8 +71,7 @@ func (s *URLTest) Start() error {
|
||||
outbounds = append(outbounds, detour)
|
||||
}
|
||||
s.group = NewURLTestGroup(s.ctx, s.router, s.logger, outbounds, s.link, s.interval, s.tolerance)
|
||||
go s.group.CheckOutbounds(false)
|
||||
return nil
|
||||
return s.group.Start()
|
||||
}
|
||||
|
||||
func (s *URLTest) Close() error {
|
||||
@@ -96,7 +93,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) {
|
||||
s.group.Start()
|
||||
outbound := s.group.Select(network)
|
||||
conn, err := outbound.DialContext(ctx, network, destination)
|
||||
if err == nil {
|
||||
@@ -108,7 +104,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) {
|
||||
s.group.Start()
|
||||
outbound := s.group.Select(N.NetworkUDP)
|
||||
conn, err := outbound.ListenPacket(ctx, destination)
|
||||
if err == nil {
|
||||
@@ -127,11 +122,6 @@ func (s *URLTest) NewPacketConnection(ctx context.Context, conn N.PacketConn, me
|
||||
return NewPacketConnection(ctx, s, conn, metadata)
|
||||
}
|
||||
|
||||
func (s *URLTest) InterfaceUpdated() error {
|
||||
go s.group.CheckOutbounds(true)
|
||||
return nil
|
||||
}
|
||||
|
||||
type URLTestGroup struct {
|
||||
ctx context.Context
|
||||
router adapter.Router
|
||||
@@ -141,9 +131,7 @@ type URLTestGroup struct {
|
||||
interval time.Duration
|
||||
tolerance uint16
|
||||
history *urltest.HistoryStorage
|
||||
checking atomic.Bool
|
||||
|
||||
access sync.Mutex
|
||||
ticker *time.Ticker
|
||||
close chan struct{}
|
||||
}
|
||||
@@ -174,23 +162,13 @@ func NewURLTestGroup(ctx context.Context, router adapter.Router, logger log.Logg
|
||||
}
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) Start() {
|
||||
if g.ticker != nil {
|
||||
return
|
||||
}
|
||||
g.access.Lock()
|
||||
defer g.access.Unlock()
|
||||
if g.ticker != nil {
|
||||
return
|
||||
}
|
||||
func (g *URLTestGroup) Start() error {
|
||||
g.ticker = time.NewTicker(g.interval)
|
||||
go g.loopCheck()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) Close() error {
|
||||
if g.ticker == nil {
|
||||
return nil
|
||||
}
|
||||
g.ticker.Stop()
|
||||
close(g.close)
|
||||
return nil
|
||||
@@ -233,7 +211,7 @@ func (g *URLTestGroup) Fallback(used adapter.Outbound) []adapter.Outbound {
|
||||
outbounds = append(outbounds, detour)
|
||||
}
|
||||
}
|
||||
sort.SliceStable(outbounds, func(i, j int) bool {
|
||||
sort.Slice(outbounds, func(i, j int) bool {
|
||||
oi := outbounds[i]
|
||||
oj := outbounds[j]
|
||||
hi := g.history.LoadURLTestHistory(RealTag(oi))
|
||||
@@ -250,33 +228,25 @@ func (g *URLTestGroup) Fallback(used adapter.Outbound) []adapter.Outbound {
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) loopCheck() {
|
||||
go g.CheckOutbounds(true)
|
||||
go g.checkOutbounds()
|
||||
for {
|
||||
select {
|
||||
case <-g.close:
|
||||
return
|
||||
case <-g.ticker.C:
|
||||
g.CheckOutbounds(false)
|
||||
g.checkOutbounds()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) CheckOutbounds(force bool) {
|
||||
_, _ = g.urlTest(g.ctx, g.link, force)
|
||||
func (g *URLTestGroup) checkOutbounds() {
|
||||
_, _ = g.URLTest(g.ctx, g.link)
|
||||
}
|
||||
|
||||
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))
|
||||
checked := make(map[string]bool)
|
||||
result := make(map[string]uint16)
|
||||
var resultAccess sync.Mutex
|
||||
for _, detour := range g.outbounds {
|
||||
tag := detour.Tag()
|
||||
@@ -285,7 +255,7 @@ func (g *URLTestGroup) urlTest(ctx context.Context, link string, force bool) (ma
|
||||
continue
|
||||
}
|
||||
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
|
||||
}
|
||||
checked[realTag] = true
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
"github.com/sagernet/sing-box/common/mux"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
@@ -25,14 +24,13 @@ var _ adapter.Outbound = (*VLESS)(nil)
|
||||
|
||||
type VLESS struct {
|
||||
myOutboundAdapter
|
||||
dialer N.Dialer
|
||||
client *vless.Client
|
||||
serverAddr M.Socksaddr
|
||||
multiplexDialer *mux.Client
|
||||
tlsConfig tls.Config
|
||||
transport adapter.V2RayClientTransport
|
||||
packetAddr bool
|
||||
xudp bool
|
||||
dialer N.Dialer
|
||||
client *vless.Client
|
||||
serverAddr M.Socksaddr
|
||||
tlsConfig tls.Config
|
||||
transport adapter.V2RayClientTransport
|
||||
packetAddr bool
|
||||
xudp bool
|
||||
}
|
||||
|
||||
func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VLESSOutboundOptions) (*VLESS, error) {
|
||||
@@ -77,65 +75,10 @@ func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outbound.multiplexDialer, err = mux.NewClientWithOptions((*vlessDialer)(outbound), common.PtrValueOrDefault(options.Multiplex))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return outbound, nil
|
||||
}
|
||||
|
||||
func (h *VLESS) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
if h.multiplexDialer == nil {
|
||||
switch N.NetworkName(network) {
|
||||
case N.NetworkTCP:
|
||||
h.logger.InfoContext(ctx, "outbound connection to ", destination)
|
||||
case N.NetworkUDP:
|
||||
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||
}
|
||||
return (*vlessDialer)(h).DialContext(ctx, network, destination)
|
||||
} else {
|
||||
switch N.NetworkName(network) {
|
||||
case N.NetworkTCP:
|
||||
h.logger.InfoContext(ctx, "outbound multiplex connection to ", destination)
|
||||
case N.NetworkUDP:
|
||||
h.logger.InfoContext(ctx, "outbound multiplex packet connection to ", destination)
|
||||
}
|
||||
return h.multiplexDialer.DialContext(ctx, network, destination)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *VLESS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
if h.multiplexDialer == nil {
|
||||
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||
return (*vlessDialer)(h).ListenPacket(ctx, destination)
|
||||
} else {
|
||||
h.logger.InfoContext(ctx, "outbound multiplex packet connection to ", destination)
|
||||
return h.multiplexDialer.ListenPacket(ctx, destination)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *VLESS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
return NewConnection(ctx, h, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *VLESS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
return NewPacketConnection(ctx, h, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *VLESS) InterfaceUpdated() error {
|
||||
if h.multiplexDialer != nil {
|
||||
h.multiplexDialer.Reset()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *VLESS) Close() error {
|
||||
return common.Close(common.PtrOrNil(h.multiplexDialer), h.transport)
|
||||
}
|
||||
|
||||
type vlessDialer VLESS
|
||||
|
||||
func (h *vlessDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
ctx, metadata := adapter.AppendContext(ctx)
|
||||
metadata.Outbound = h.tag
|
||||
metadata.Destination = destination
|
||||
@@ -177,7 +120,7 @@ func (h *vlessDialer) DialContext(ctx context.Context, network string, destinati
|
||||
}
|
||||
}
|
||||
|
||||
func (h *vlessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
func (h *VLESS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||
ctx, metadata := adapter.AppendContext(ctx)
|
||||
metadata.Outbound = h.tag
|
||||
@@ -211,3 +154,15 @@ func (h *vlessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr)
|
||||
return h.client.DialEarlyPacketConn(conn, destination)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *VLESS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
return NewConnection(ctx, h, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *VLESS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
return NewPacketConnection(ctx, h, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *VLESS) Close() error {
|
||||
return common.Close(h.transport)
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ type VMess struct {
|
||||
dialer N.Dialer
|
||||
client *vmess.Client
|
||||
serverAddr M.Socksaddr
|
||||
multiplexDialer *mux.Client
|
||||
multiplexDialer N.Dialer
|
||||
tlsConfig tls.Config
|
||||
transport adapter.V2RayClientTransport
|
||||
packetAddr bool
|
||||
@@ -59,7 +59,7 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
||||
return nil, E.Cause(err, "create client transport: ", options.Transport.Type)
|
||||
}
|
||||
}
|
||||
outbound.multiplexDialer, err = mux.NewClientWithOptions((*vmessDialer)(outbound), common.PtrValueOrDefault(options.Multiplex))
|
||||
outbound.multiplexDialer, err = mux.NewClientWithOptions(ctx, (*vmessDialer)(outbound), common.PtrValueOrDefault(options.Multiplex))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -97,15 +97,8 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
||||
return outbound, nil
|
||||
}
|
||||
|
||||
func (h *VMess) InterfaceUpdated() error {
|
||||
if h.multiplexDialer != nil {
|
||||
h.multiplexDialer.Reset()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@@ -67,7 +67,7 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
|
||||
connectAddr = options.ServerOptions.Build()
|
||||
}
|
||||
}
|
||||
outbound.bind = wireguard.NewClientBind(ctx, outbound, dialer.New(router, options.DialerOptions), isConnect, connectAddr, reserved)
|
||||
outbound.bind = wireguard.NewClientBind(ctx, dialer.New(router, options.DialerOptions), isConnect, connectAddr, reserved)
|
||||
localPrefixes := common.Map(options.LocalAddress, option.ListenPrefix.Build)
|
||||
if len(localPrefixes) == 0 {
|
||||
return nil, E.New("missing local address")
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
var _ control.InterfaceFinder = (*myInterfaceFinder)(nil)
|
||||
|
||||
type myInterfaceFinder struct {
|
||||
interfaces []net.Interface
|
||||
ifs []net.Interface
|
||||
}
|
||||
|
||||
func (f *myInterfaceFinder) update() error {
|
||||
@@ -17,16 +17,12 @@ func (f *myInterfaceFinder) update() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.interfaces = ifs
|
||||
f.ifs = ifs
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *myInterfaceFinder) updateInterfaces(interfaces []net.Interface) {
|
||||
f.interfaces = interfaces
|
||||
}
|
||||
|
||||
func (f *myInterfaceFinder) InterfaceIndexByName(name string) (interfaceIndex int, err error) {
|
||||
for _, netInterface := range f.interfaces {
|
||||
for _, netInterface := range f.ifs {
|
||||
if netInterface.Name == name {
|
||||
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) {
|
||||
for _, netInterface := range f.interfaces {
|
||||
for _, netInterface := range f.ifs {
|
||||
if netInterface.Index == index {
|
||||
return netInterface.Name, nil
|
||||
}
|
||||
|
||||
135
route/router.go
135
route/router.go
@@ -12,12 +12,12 @@ import (
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"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/geosite"
|
||||
"github.com/sagernet/sing-box/common/mux"
|
||||
"github.com/sagernet/sing-box/common/process"
|
||||
"github.com/sagernet/sing-box/common/sniff"
|
||||
"github.com/sagernet/sing-box/common/warning"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
@@ -31,7 +31,6 @@ import (
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
"github.com/sagernet/sing/common/bufio/deadline"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
@@ -40,6 +39,27 @@ import (
|
||||
"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)
|
||||
|
||||
type Router struct {
|
||||
@@ -93,6 +113,16 @@ func NewRouter(
|
||||
inbounds []option.Inbound,
|
||||
platformInterface platform.Interface,
|
||||
) (*Router, error) {
|
||||
if options.DefaultInterface != "" {
|
||||
warnDefaultInterfaceOnUnsupportedPlatform.Check()
|
||||
}
|
||||
if options.DefaultMark != 0 {
|
||||
warnDefaultMarkOnNonLinux.Check()
|
||||
}
|
||||
if options.FindProcess {
|
||||
warnFindProcessOnUnsupportedPlatform.Check()
|
||||
}
|
||||
|
||||
router := &Router{
|
||||
ctx: ctx,
|
||||
logger: logFactory.NewLogger("router"),
|
||||
@@ -113,12 +143,7 @@ func NewRouter(
|
||||
defaultMark: options.DefaultMark,
|
||||
platformInterface: platformInterface,
|
||||
}
|
||||
router.dnsClient = dns.NewClient(dns.ClientOptions{
|
||||
DisableCache: dnsOptions.DNSClientOptions.DisableCache,
|
||||
DisableExpire: dnsOptions.DNSClientOptions.DisableExpire,
|
||||
IndependentCache: dnsOptions.DNSClientOptions.IndependentCache,
|
||||
Logger: router.dnsLogger,
|
||||
})
|
||||
router.dnsClient = dns.NewClient(dnsOptions.DNSClientOptions.DisableCache, dnsOptions.DNSClientOptions.DisableExpire, router.dnsLogger)
|
||||
for i, ruleOptions := range options.Rules {
|
||||
routeRule, err := NewRule(router, router.logger, ruleOptions)
|
||||
if err != nil {
|
||||
@@ -264,37 +289,29 @@ func NewRouter(
|
||||
router.fakeIPStore = fakeip.NewStore(router, inet4Range, inet6Range)
|
||||
}
|
||||
|
||||
usePlatformDefaultInterfaceMonitor := platformInterface != nil && platformInterface.UsePlatformDefaultInterfaceMonitor()
|
||||
needInterfaceMonitor := options.AutoDetectInterface || common.Any(inbounds, func(inbound option.Inbound) bool {
|
||||
needInterfaceMonitor := platformInterface == nil && (options.AutoDetectInterface || common.Any(inbounds, func(inbound option.Inbound) bool {
|
||||
return inbound.HTTPOptions.SetSystemProxy || inbound.MixedOptions.SetSystemProxy || inbound.TunOptions.AutoRoute
|
||||
})
|
||||
}))
|
||||
|
||||
if needInterfaceMonitor {
|
||||
if !usePlatformDefaultInterfaceMonitor {
|
||||
networkMonitor, err := tun.NewNetworkUpdateMonitor(router)
|
||||
if err != os.ErrInvalid {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
router.networkMonitor = networkMonitor
|
||||
networkMonitor.RegisterCallback(router.interfaceFinder.update)
|
||||
interfaceMonitor, err := tun.NewDefaultInterfaceMonitor(router.networkMonitor, tun.DefaultInterfaceMonitorOptions{
|
||||
OverrideAndroidVPN: options.OverrideAndroidVPN,
|
||||
UnderNetworkExtension: platformInterface != nil && platformInterface.UnderNetworkExtension(),
|
||||
})
|
||||
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
|
||||
networkMonitor, err := tun.NewNetworkUpdateMonitor(router)
|
||||
if err == nil {
|
||||
router.networkMonitor = networkMonitor
|
||||
networkMonitor.RegisterCallback(router.interfaceFinder.update)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
@@ -604,8 +621,7 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
|
||||
switch metadata.Destination.Fqdn {
|
||||
case mux.Destination.Fqdn:
|
||||
r.logger.InfoContext(ctx, "inbound multiplex connection")
|
||||
handler := adapter.NewUpstreamHandler(metadata, r.RouteConnection, r.RoutePacketConnection, r)
|
||||
return mux.HandleConnection(ctx, handler, r.logger, conn, adapter.UpstreamMetadata(metadata))
|
||||
return mux.NewConnection(ctx, r, r, r.logger, conn, metadata)
|
||||
case vmess.MuxDestination.Fqdn:
|
||||
r.logger.InfoContext(ctx, "inbound legacy multiplex connection")
|
||||
return vmess.HandleMuxConnection(ctx, conn, adapter.NewUpstreamHandler(metadata, r.RouteConnection, r.RoutePacketConnection, r))
|
||||
@@ -641,10 +657,6 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
|
||||
r.logger.DebugContext(ctx, "found fakeip domain: ", domain)
|
||||
}
|
||||
|
||||
if deadline.NeedAdditionalReadDeadline(conn) {
|
||||
conn = deadline.NewConn(conn)
|
||||
}
|
||||
|
||||
if metadata.InboundOptions.SniffEnabled {
|
||||
buffer := buf.NewPacket()
|
||||
buffer.FullReset()
|
||||
@@ -750,11 +762,6 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
|
||||
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 {
|
||||
buffer := buf.NewPacket()
|
||||
buffer.FullReset()
|
||||
@@ -879,31 +886,12 @@ func (r *Router) InterfaceFinder() control.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 {
|
||||
return r.autoDetectInterface
|
||||
}
|
||||
|
||||
func (r *Router) AutoDetectInterfaceFunc() control.Func {
|
||||
if r.platformInterface != nil && r.platformInterface.UsePlatformAutoDetectInterfaceControl() {
|
||||
if r.platformInterface != nil {
|
||||
return r.platformInterface.AutoDetectInterfaceControl()
|
||||
} else {
|
||||
return control.BindToInterfaceFunc(r.InterfaceFinder(), func(network string, address string) (interfaceName string, interfaceIndex int) {
|
||||
@@ -982,7 +970,7 @@ func (r *Router) NewError(ctx context.Context, err error) {
|
||||
}
|
||||
|
||||
func (r *Router) notifyNetworkUpdate(int) error {
|
||||
if C.IsAndroid && r.platformInterface == nil {
|
||||
if C.IsAndroid {
|
||||
var vpnStatus string
|
||||
if r.interfaceMonitor.AndroidVPNEnabled() {
|
||||
vpnStatus = "enabled"
|
||||
@@ -994,23 +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()))
|
||||
}
|
||||
|
||||
conntrack.Close()
|
||||
|
||||
for _, outbound := range r.outbounds {
|
||||
listener, isListener := outbound.(adapter.InterfaceUpdateListener)
|
||||
if isListener {
|
||||
err := listener.InterfaceUpdated()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Router) ResetNetwork() error {
|
||||
conntrack.Close()
|
||||
|
||||
for _, outbound := range r.outbounds {
|
||||
listener, isListener := outbound.(adapter.InterfaceUpdateListener)
|
||||
if isListener {
|
||||
|
||||
@@ -73,31 +73,23 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er
|
||||
if len(message.Question) > 0 {
|
||||
r.dnsLogger.DebugContext(ctx, "exchange ", formatQuestion(message.Question[0].String()))
|
||||
}
|
||||
var (
|
||||
response *mDNS.Msg
|
||||
cached bool
|
||||
err error
|
||||
)
|
||||
response, cached = r.dnsClient.ExchangeCache(ctx, message)
|
||||
if !cached {
|
||||
ctx, metadata := adapter.AppendContext(ctx)
|
||||
if len(message.Question) > 0 {
|
||||
metadata.QueryType = message.Question[0].Qtype
|
||||
switch metadata.QueryType {
|
||||
case mDNS.TypeA:
|
||||
metadata.IPVersion = 4
|
||||
case mDNS.TypeAAAA:
|
||||
metadata.IPVersion = 6
|
||||
}
|
||||
metadata.Domain = fqdnToDomain(message.Question[0].Name)
|
||||
}
|
||||
ctx, transport, strategy := r.matchDNS(ctx)
|
||||
ctx, cancel := context.WithTimeout(ctx, C.DNSTimeout)
|
||||
defer cancel()
|
||||
response, err = r.dnsClient.Exchange(ctx, transport, message, strategy)
|
||||
if err != nil && len(message.Question) > 0 {
|
||||
r.dnsLogger.ErrorContext(ctx, E.Cause(err, "exchange failed for ", formatQuestion(message.Question[0].String())))
|
||||
ctx, metadata := adapter.AppendContext(ctx)
|
||||
if len(message.Question) > 0 {
|
||||
metadata.QueryType = message.Question[0].Qtype
|
||||
switch metadata.QueryType {
|
||||
case mDNS.TypeA:
|
||||
metadata.IPVersion = 4
|
||||
case mDNS.TypeAAAA:
|
||||
metadata.IPVersion = 6
|
||||
}
|
||||
metadata.Domain = fqdnToDomain(message.Question[0].Name)
|
||||
}
|
||||
ctx, transport, strategy := r.matchDNS(ctx)
|
||||
ctx, cancel := context.WithTimeout(ctx, C.DNSTimeout)
|
||||
defer cancel()
|
||||
response, err := r.dnsClient.Exchange(ctx, transport, message, strategy)
|
||||
if err != nil && len(message.Question) > 0 {
|
||||
r.dnsLogger.ErrorContext(ctx, E.Cause(err, "exchange failed for ", formatQuestion(message.Question[0].String())))
|
||||
}
|
||||
if len(message.Question) > 0 && response != nil {
|
||||
LogDNSAnswers(r.dnsLogger, ctx, message.Question[0].Name, response.Answer)
|
||||
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
"github.com/sagernet/sing/service/filemanager"
|
||||
)
|
||||
|
||||
func (r *Router) GeoIPReader() *geoip.Reader {
|
||||
@@ -52,7 +51,7 @@ func (r *Router) prepareGeoIPDatabase() error {
|
||||
geoPath = foundPath
|
||||
}
|
||||
}
|
||||
geoPath = filemanager.BasePath(r.ctx, geoPath)
|
||||
geoPath = C.BasePath(geoPath)
|
||||
if rw.FileExists(geoPath) {
|
||||
geoReader, codes, err := geoip.Open(geoPath)
|
||||
if err == nil {
|
||||
@@ -96,7 +95,7 @@ func (r *Router) prepareGeositeDatabase() error {
|
||||
geoPath = foundPath
|
||||
}
|
||||
}
|
||||
geoPath = filemanager.BasePath(r.ctx, geoPath)
|
||||
geoPath = C.BasePath(geoPath)
|
||||
if !rw.FileExists(geoPath) {
|
||||
r.logger.Warn("geosite database not exists: ", geoPath)
|
||||
var err error
|
||||
@@ -143,10 +142,10 @@ func (r *Router) downloadGeoIPDatabase(savePath string) error {
|
||||
}
|
||||
|
||||
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 {
|
||||
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 != "" {
|
||||
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 {
|
||||
return E.Cause(err, "open output file: ", downloadURL)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,13 @@ import (
|
||||
"strings"
|
||||
|
||||
"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)
|
||||
@@ -14,6 +21,7 @@ type PackageNameItem struct {
|
||||
}
|
||||
|
||||
func NewPackageNameItem(packageNameList []string) *PackageNameItem {
|
||||
warnPackageNameOnNonAndroid.Check()
|
||||
rule := &PackageNameItem{
|
||||
packageNames: packageNameList,
|
||||
packageMap: make(map[string]bool),
|
||||
|
||||
@@ -5,6 +5,13 @@ import (
|
||||
"strings"
|
||||
|
||||
"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)
|
||||
@@ -15,6 +22,7 @@ type ProcessItem struct {
|
||||
}
|
||||
|
||||
func NewProcessItem(processNameList []string) *ProcessItem {
|
||||
warnProcessNameOnNonSupportedPlatform.Check()
|
||||
rule := &ProcessItem{
|
||||
processes: processNameList,
|
||||
processMap: make(map[string]bool),
|
||||
|
||||
@@ -4,6 +4,13 @@ import (
|
||||
"strings"
|
||||
|
||||
"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)
|
||||
@@ -14,6 +21,7 @@ type ProcessPathItem struct {
|
||||
}
|
||||
|
||||
func NewProcessPathItem(processNameList []string) *ProcessPathItem {
|
||||
warnProcessPathOnNonSupportedPlatform.Check()
|
||||
rule := &ProcessPathItem{
|
||||
processes: processNameList,
|
||||
processMap: make(map[string]bool),
|
||||
|
||||
@@ -4,9 +4,16 @@ import (
|
||||
"strings"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
var warnUserOnNonLinux = warning.New(
|
||||
func() bool { return !C.IsLinux },
|
||||
"rule item `user` is only supported on Linux",
|
||||
)
|
||||
|
||||
var _ RuleItem = (*UserItem)(nil)
|
||||
|
||||
type UserItem struct {
|
||||
@@ -15,6 +22,7 @@ type UserItem struct {
|
||||
}
|
||||
|
||||
func NewUserItem(users []string) *UserItem {
|
||||
warnUserOnNonLinux.Check()
|
||||
userMap := make(map[string]bool)
|
||||
for _, protocol := range users {
|
||||
userMap[protocol] = true
|
||||
|
||||
@@ -4,9 +4,16 @@ import (
|
||||
"strings"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
var warnUserIDOnNonLinux = warning.New(
|
||||
func() bool { return !C.IsLinux },
|
||||
"rule item `user_id` is only supported on Linux",
|
||||
)
|
||||
|
||||
var _ RuleItem = (*UserIdItem)(nil)
|
||||
|
||||
type UserIdItem struct {
|
||||
@@ -15,6 +22,7 @@ type UserIdItem struct {
|
||||
}
|
||||
|
||||
func NewUserIDItem(userIdList []int32) *UserIdItem {
|
||||
warnUserIDOnNonLinux.Check()
|
||||
rule := &UserIdItem{
|
||||
userIds: userIdList,
|
||||
userIdMap: make(map[int32]bool),
|
||||
|
||||
35
test/go.mod
35
test/go.mod
@@ -7,21 +7,21 @@ require github.com/sagernet/sing-box v0.0.0
|
||||
replace github.com/sagernet/sing-box => ../
|
||||
|
||||
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/gofrs/uuid/v5 v5.0.0
|
||||
github.com/sagernet/sing v0.2.5-0.20230423085534-0902e6216207
|
||||
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230417102954-f77257340507
|
||||
github.com/spyzhov/ajson v0.7.1
|
||||
github.com/sagernet/sing v0.2.2
|
||||
github.com/sagernet/sing-shadowsocks v0.2.1-0.20230408141421-e40d6a4e42d4
|
||||
github.com/spyzhov/ajson v0.8.0
|
||||
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
|
||||
)
|
||||
|
||||
require (
|
||||
berty.tech/go-libtor v1.0.385 // indirect
|
||||
github.com/Dreamacro/clash v1.15.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.0 // indirect
|
||||
github.com/Dreamacro/clash v1.14.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.5.1 // indirect
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/andybalholm/brotli v1.0.5 // 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/mholt/acmez v1.1.0 // 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/onsi/ginkgo/v2 v2.2.0 // indirect
|
||||
github.com/ooni/go-libtor v1.1.7 // indirect
|
||||
@@ -70,17 +70,15 @@ require (
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // 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/sing-dns v0.1.5-0.20230415085626-111ecf799dfc // indirect
|
||||
github.com/sagernet/sing-mux v0.0.0-20230424015424-9b0d527c3bb0 // indirect
|
||||
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b // indirect
|
||||
github.com/sagernet/sing-tun v0.1.5-0.20230422121432-209ec123ca7b // indirect
|
||||
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 // indirect
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230408004833-5adaf486d440 // indirect
|
||||
github.com/sagernet/sing-shadowtls v0.1.1-0.20230408141548-81d74d2a8661 // indirect
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab // 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/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 // indirect
|
||||
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 // indirect
|
||||
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e // indirect
|
||||
github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77 // indirect
|
||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c // indirect
|
||||
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||
go.etcd.io/bbolt v1.3.7 // indirect
|
||||
@@ -93,12 +91,15 @@ require (
|
||||
golang.org/x/mod v0.8.0 // indirect
|
||||
golang.org/x/sys v0.7.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
|
||||
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
|
||||
google.golang.org/grpc v1.54.0 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // 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
|
||||
)
|
||||
|
||||
//replace github.com/sagernet/sing => ../../sing
|
||||
|
||||
73
test/go.sum
73
test/go.sum
@@ -1,10 +1,10 @@
|
||||
berty.tech/go-libtor v1.0.385 h1:RWK94C3hZj6Z2GdvePpHJLnWYobFr3bY/OdUJ5aoEXw=
|
||||
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/Dreamacro/clash v1.15.0 h1:mlpD950VEggXZBNahV66hyKDRxcczkj3vymoAt78KyE=
|
||||
github.com/Dreamacro/clash v1.15.0/go.mod h1:WNH69bN11LiAdgdSr4hpkEuXVMfBbWyhEKMCTx9BtNE=
|
||||
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
|
||||
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
|
||||
github.com/Dreamacro/clash v1.14.0 h1:ehJ/C/1m9LEjmME72WSE/Y2YqbR3Q54AbjqiRCvtyW4=
|
||||
github.com/Dreamacro/clash v1.14.0/go.mod h1:ia2CU7V713H1QdCqMwOLK9U9V5Ay8X0voj3yQr2tk+I=
|
||||
github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY=
|
||||
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/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
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/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/docker v20.10.18+incompatible h1:SN84VYXTBNGn92T/QwIRPlum9zfemfitN7pbsp26WSc=
|
||||
github.com/docker/docker v20.10.18+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v23.0.3+incompatible h1:9GhVsShNWz1hO//9BNg/dpMnZW25KydO4wtVxWAIbho=
|
||||
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/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
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/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw=
|
||||
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-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||
github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c h1:RC8WMpjonrBfyAh6VN/POIPtYD5tRAq0qMqCRjQNK+g=
|
||||
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/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
@@ -126,20 +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/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.2.5-0.20230423085534-0902e6216207 h1:+dDVjW20IT+e8maKryaDeRY2+RFmTFdrQeIzqE2WOss=
|
||||
github.com/sagernet/sing v0.2.5-0.20230423085534-0902e6216207/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.20230415085626-111ecf799dfc/go.mod h1:ZKuuqgsHRxDahYrzgSgy4vIAGGuKPlIf4hLcNzYzLkY=
|
||||
github.com/sagernet/sing-mux v0.0.0-20230424015424-9b0d527c3bb0 h1:87jyxzTjq01VgEiUVSMNRKjCfsSfp/QwyUVT37eXY50=
|
||||
github.com/sagernet/sing-mux v0.0.0-20230424015424-9b0d527c3bb0/go.mod h1:pF+RnLvCAOhECrvauy6LYOpBakJ/vuaF1Wm4lPsWryI=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230417102954-f77257340507 h1:bAHZCdWqJkb8LEW98+YsMVDXGRMUVjka8IC+St6ot88=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230417102954-f77257340507/go.mod h1:UJjvQGw0lyYaDGIDvUraL16fwaAEH1WFw1Y6sUcMPog=
|
||||
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b h1:ouW/6IDCrxkBe19YSbdCd7buHix7b+UZ6BM4Zz74XF4=
|
||||
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b/go.mod h1:oG8bPerYI6cZ74KquY3DvA7ynECyrILPBnce6wtBqeI=
|
||||
github.com/sagernet/sing-tun v0.1.5-0.20230422121432-209ec123ca7b h1:9NsciSJGwzdkXwVvT2c2g+RvkTVkANeBLr2l+soJ7LM=
|
||||
github.com/sagernet/sing-tun v0.1.5-0.20230422121432-209ec123ca7b/go.mod h1:DD7Ce2Gt0GFc6I/1+Uw4D/aUlBsGqrQsC52CMK/V818=
|
||||
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 h1:BHOnxrbC929JonuKqFdJ7ZbDp7zs4oTlH5KFvKtWu9U=
|
||||
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3/go.mod h1:yKrAr+dqZd64DxBXCHWrYicp+n4qbqO73mtwv3dck8U=
|
||||
github.com/sagernet/sing v0.2.2 h1:qfEdSLuwFgIIkeLOcwVQkVDzKLHtclXb93Ql0zZA+aE=
|
||||
github.com/sagernet/sing v0.2.2/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
|
||||
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.20230408004833-5adaf486d440/go.mod h1:69PNSHyEmXdjf6C+bXBOdr2GQnPeEyWjIzo/MV8gmz8=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.1-0.20230408141421-e40d6a4e42d4 h1:mKpXBBnAhTy9/CvDKqt5cN78LKX8cVUqjoWEXI/g0No=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.1-0.20230408141421-e40d6a4e42d4/go.mod h1:NFEROpOEiLG+lEoSPNpSL2yDyXx6q0OJvwWnEAd40cI=
|
||||
github.com/sagernet/sing-shadowtls v0.1.1-0.20230408141548-81d74d2a8661 h1:QnV79JbJbJGT0MJJfd8o7QMEfRu3eUVKsmahxFMonrc=
|
||||
github.com/sagernet/sing-shadowtls v0.1.1-0.20230408141548-81d74d2a8661/go.mod h1:xCeSRP8cV32aPsY+6BbRdJjyD6q8ufdKwhgqxEbU/3U=
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab h1:a9oeWuPBuIZ70qMhIIH6RrYhp886xN9jJIwsuu4ZFUo=
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab/go.mod h1:4YxIDEkkCjGXDOTMPw1SXpLmCQUFAWuaQN250oo+928=
|
||||
github.com/sagernet/sing-vmess v0.1.4-0.20230409073451-6921c3dd77c7 h1:ZArINfN+zcHMdZCeRFOm4rO3SWyvYuLg3VhWcA5zonc=
|
||||
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/go.mod h1:3skNSftZDJWTGVtVaM2jfbce8qHnmH/AGDRe62iNOg0=
|
||||
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE=
|
||||
@@ -148,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/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/wireguard-go v0.0.0-20230420044414-a7bac1754e77 h1:g6QtRWQ2dKX7EQP++1JLNtw4C2TNxd4/ov8YUpOPOSo=
|
||||
github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77/go.mod h1:pJDdXzZIwJ+2vmnT0TKzmf8meeum+e2mTDSehw79eE0=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/spyzhov/ajson v0.7.1 h1:1MDIlPc6x0zjNtpa7tDzRAyFAvRX+X8ZsvtYz5lZg6A=
|
||||
github.com/spyzhov/ajson v0.7.1/go.mod h1:63V+CGM6f1Bu/p4nLIN8885ojBdt88TbLoSFzyqMuVA=
|
||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo=
|
||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/spyzhov/ajson v0.8.0 h1:sFXyMbi4Y/BKjrsfkUZHSjA2JM1184enheSjjoT/zCc=
|
||||
github.com/spyzhov/ajson v0.8.0/go.mod h1:63V+CGM6f1Bu/p4nLIN8885ojBdt88TbLoSFzyqMuVA=
|
||||
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.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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
@@ -176,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.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
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.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
|
||||
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
||||
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/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
||||
@@ -193,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/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/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.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
@@ -216,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-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-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-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-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-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-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-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-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -238,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.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
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-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
||||
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-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-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.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
@@ -266,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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
|
||||
gvisor.dev/gvisor v0.0.0-20230415003630-3981d5d5e523 h1:zUQYeyyPLnSR6yMvLSOmLH37xDWCZ7BqlpE69fE5K3Q=
|
||||
gvisor.dev/gvisor v0.0.0-20230415003630-3981d5d5e523/go.mod h1:pzr6sy8gDLfVmDAg8OYrlKvGEHw5C3PGTiBXBTCx76Q=
|
||||
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
|
||||
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/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user