mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-12 01:57:18 +10:00
Compare commits
10 Commits
v1.3-beta8
...
v1.3-beta2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b290d0ed32 | ||
|
|
2afe662646 | ||
|
|
107a9a3b51 | ||
|
|
3d0c64f523 | ||
|
|
422ca34ac2 | ||
|
|
6d63f9255f | ||
|
|
6f2cc9761d | ||
|
|
b484d9bca6 | ||
|
|
58c4fd745a | ||
|
|
7d1e6affb3 |
12
.github/workflows/debug.yml
vendored
12
.github/workflows/debug.yml
vendored
@@ -31,6 +31,12 @@ jobs:
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ steps.version.outputs.go_version }}
|
||||
- name: Cache go module
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
key: go-${{ hashFiles('**/go.sum') }}
|
||||
- name: Add cache to Go proxy
|
||||
run: |
|
||||
version=`git rev-parse HEAD`
|
||||
@@ -190,6 +196,12 @@ jobs:
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ steps.version.outputs.go_version }}
|
||||
- name: Cache go module
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
key: go-${{ hashFiles('**/go.sum') }}
|
||||
- name: Build
|
||||
id: build
|
||||
run: make
|
||||
|
||||
6
.github/workflows/lint.yml
vendored
6
.github/workflows/lint.yml
vendored
@@ -31,6 +31,12 @@ jobs:
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ steps.version.outputs.go_version }}
|
||||
- name: Cache go module
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
key: go-${{ hashFiles('**/go.sum') }}
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
with:
|
||||
|
||||
13
Makefile
13
Makefile
@@ -77,20 +77,13 @@ test_stdio:
|
||||
go mod tidy && \
|
||||
go test -v -tags "$(TAGS_TEST),force_stdio" .
|
||||
|
||||
android:
|
||||
go run ./cmd/internal/build_libbox -target android
|
||||
|
||||
ios:
|
||||
go run ./cmd/internal/build_libbox -target ios
|
||||
|
||||
lib:
|
||||
go run ./cmd/internal/build_libbox -target android
|
||||
go run ./cmd/internal/build_libbox -target ios
|
||||
go run ./cmd/internal/build_libbox
|
||||
|
||||
lib_install:
|
||||
go get -v -d
|
||||
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.0.0-20230413023804-244d7ff07035
|
||||
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.0.0-20230413023804-244d7ff07035
|
||||
go install -v github.com/sagernet/gomobile/cmd/gomobile@v0.0.0-20221130124640-349ebaa752ca
|
||||
go install -v github.com/sagernet/gomobile/cmd/gobind@v0.0.0-20221130124640-349ebaa752ca
|
||||
|
||||
clean:
|
||||
rm -rf bin dist sing-box
|
||||
|
||||
@@ -8,10 +8,6 @@ The universal proxy platform.
|
||||
|
||||
https://sing-box.sagernet.org
|
||||
|
||||
## Support
|
||||
|
||||
https://community.sagernet.org/c/sing-box/
|
||||
|
||||
## License
|
||||
|
||||
```
|
||||
|
||||
@@ -35,11 +35,6 @@ type OutboundGroup interface {
|
||||
All() []string
|
||||
}
|
||||
|
||||
type URLTestGroup interface {
|
||||
OutboundGroup
|
||||
URLTest(ctx context.Context, url string) (map[string]uint16, error)
|
||||
}
|
||||
|
||||
func OutboundTag(detour Outbound) string {
|
||||
if group, isGroup := detour.(OutboundGroup); isGroup {
|
||||
return group.Now()
|
||||
|
||||
@@ -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
|
||||
|
||||
6
box.go
6
box.go
@@ -133,12 +133,6 @@ 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 {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
|
||||
"github.com/gofrs/uuid/v5"
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ type Conn struct {
|
||||
element *list.Element[io.Closer]
|
||||
}
|
||||
|
||||
func NewConn(conn net.Conn) (net.Conn, error) {
|
||||
func NewConn(conn net.Conn) (*Conn, error) {
|
||||
connAccess.Lock()
|
||||
element := openConnection.PushBack(conn)
|
||||
connAccess.Unlock()
|
||||
|
||||
@@ -12,7 +12,7 @@ type PacketConn struct {
|
||||
element *list.Element[io.Closer]
|
||||
}
|
||||
|
||||
func NewPacketConn(conn net.PacketConn) (net.PacketConn, error) {
|
||||
func NewPacketConn(conn net.PacketConn) (*PacketConn, error) {
|
||||
connAccess.Lock()
|
||||
element := openConnection.PushBack(conn)
|
||||
connAccess.Unlock()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -73,7 +73,7 @@ func (d *ResolveDialer) ListenPacket(ctx context.Context, destination M.Socksadd
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bufio.NewNATPacketConn(bufio.NewPacketConn(conn), M.SocksaddrFrom(destinationAddress, destination.Port), destination), nil
|
||||
return bufio.NewNATPacketConn(bufio.NewPacketConn(conn), destination, M.SocksaddrFrom(destinationAddress, destination.Port)), nil
|
||||
}
|
||||
|
||||
func (d *ResolveDialer) Upstream() any {
|
||||
|
||||
@@ -11,6 +11,7 @@ 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"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
@@ -68,7 +69,7 @@ func (c *Client) DialContext(ctx context.Context, network string, destination M.
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bufio.NewUnbindPacketConn(&ClientPacketConn{ExtendedConn: bufio.NewExtendedConn(stream), destination: destination}), nil
|
||||
return bufio.NewBindPacketConn(deadline.NewPacketConn(bufio.NewNetPacketConn(&ClientPacketConn{ExtendedConn: bufio.NewExtendedConn(stream), destination: destination})), destination), nil
|
||||
default:
|
||||
return nil, E.Extend(N.ErrUnknownNetwork, network)
|
||||
}
|
||||
@@ -79,7 +80,7 @@ func (c *Client) ListenPacket(ctx context.Context, destination M.Socksaddr) (net
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ClientPacketAddrConn{ExtendedConn: bufio.NewExtendedConn(stream), destination: destination}, nil
|
||||
return deadline.NewPacketConn(&ClientPacketAddrConn{ExtendedConn: bufio.NewExtendedConn(stream), destination: destination}), nil
|
||||
}
|
||||
|
||||
func (c *Client) openStream() (net.Conn, error) {
|
||||
@@ -249,10 +250,6 @@ func (c *ClientConn) WriterReplaceable() bool {
|
||||
return c.requestWrite
|
||||
}
|
||||
|
||||
func (c *ClientConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *ClientConn) Upstream() any {
|
||||
return c.Conn
|
||||
}
|
||||
@@ -381,10 +378,6 @@ func (c *ClientPacketConn) RemoteAddr() net.Addr {
|
||||
return c.destination.UDPAddr()
|
||||
}
|
||||
|
||||
func (c *ClientPacketConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *ClientPacketConn) Upstream() any {
|
||||
return c.ExtendedConn
|
||||
}
|
||||
@@ -421,11 +414,7 @@ func (c *ClientPacketAddrConn) ReadFrom(p []byte) (n int, addr net.Addr, err err
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if destination.IsFqdn() {
|
||||
addr = destination
|
||||
} else {
|
||||
addr = destination.UDPAddr()
|
||||
}
|
||||
addr = destination.UDPAddr()
|
||||
var length uint16
|
||||
err = binary.Read(c.ExtendedConn, binary.BigEndian, &length)
|
||||
if err != nil {
|
||||
@@ -526,10 +515,6 @@ func (c *ClientPacketAddrConn) FrontHeadroom() int {
|
||||
return 2 + M.MaxSocksaddrLength
|
||||
}
|
||||
|
||||
func (c *ClientPacketAddrConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *ClientPacketAddrConn) Upstream() any {
|
||||
return c.ExtendedConn
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ 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"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
@@ -67,7 +68,7 @@ func newConnection(ctx context.Context, router adapter.Router, errorHandler E.Ha
|
||||
logger.InfoContext(ctx, "inbound multiplex packet connection")
|
||||
packetConn = &ServerPacketAddrConn{ExtendedConn: bufio.NewExtendedConn(stream)}
|
||||
}
|
||||
hErr := router.RoutePacketConnection(ctx, packetConn, metadata)
|
||||
hErr := router.RoutePacketConnection(ctx, deadline.NewPacketConn(bufio.NewNetPacketConn(packetConn)), metadata)
|
||||
stream.Close()
|
||||
if hErr != nil {
|
||||
errorHandler.NewError(ctx, hErr)
|
||||
@@ -131,10 +132,6 @@ func (c *ServerConn) FrontHeadroom() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *ServerConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *ServerConn) Upstream() any {
|
||||
return c.ExtendedConn
|
||||
}
|
||||
@@ -187,10 +184,6 @@ func (c *ServerPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksad
|
||||
return c.ExtendedConn.WriteBuffer(buffer)
|
||||
}
|
||||
|
||||
func (c *ServerPacketConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *ServerPacketConn) Upstream() any {
|
||||
return c.ExtendedConn
|
||||
}
|
||||
@@ -253,10 +246,6 @@ func (c *ServerPacketAddrConn) WritePacket(buffer *buf.Buffer, destination M.Soc
|
||||
return c.ExtendedConn.WriteBuffer(buffer)
|
||||
}
|
||||
|
||||
func (c *ServerPacketAddrConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *ServerPacketAddrConn) Upstream() any {
|
||||
return c.ExtendedConn
|
||||
}
|
||||
|
||||
@@ -3,12 +3,10 @@ package process
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"os/user"
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-tun"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
)
|
||||
|
||||
type Searcher interface {
|
||||
@@ -30,15 +28,5 @@ type Info struct {
|
||||
}
|
||||
|
||||
func FindProcessInfo(searcher Searcher, ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) {
|
||||
info, err := searcher.FindProcessInfo(ctx, network, source, destination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if info.UserId != -1 {
|
||||
osUser, _ := user.LookupId(F.ToString(info.UserId))
|
||||
if osUser != nil {
|
||||
info.User = osUser.Username
|
||||
}
|
||||
}
|
||||
return info, nil
|
||||
return findProcessInfo(searcher, ctx, network, source, destination)
|
||||
}
|
||||
|
||||
25
common/process/searcher_with_name.go
Normal file
25
common/process/searcher_with_name.go
Normal file
@@ -0,0 +1,25 @@
|
||||
//go:build linux && !android
|
||||
|
||||
package process
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"os/user"
|
||||
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
)
|
||||
|
||||
func findProcessInfo(searcher Searcher, ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) {
|
||||
info, err := searcher.FindProcessInfo(ctx, network, source, destination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if info.UserId != -1 {
|
||||
osUser, _ := user.LookupId(F.ToString(info.UserId))
|
||||
if osUser != nil {
|
||||
info.User = osUser.Username
|
||||
}
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
12
common/process/searcher_without_name.go
Normal file
12
common/process/searcher_without_name.go
Normal file
@@ -0,0 +1,12 @@
|
||||
//go:build !linux || android
|
||||
|
||||
package process
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
func findProcessInfo(searcher Searcher, ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) {
|
||||
return searcher.FindProcessInfo(ctx, network, source, destination)
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
@@ -24,12 +24,12 @@ func PeekStream(ctx context.Context, conn net.Conn, buffer *buf.Buffer, timeout
|
||||
}
|
||||
err := conn.SetReadDeadline(time.Now().Add(timeout))
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "set read deadline")
|
||||
return nil, err
|
||||
}
|
||||
_, err = buffer.ReadOnceFrom(conn)
|
||||
err = E.Errors(err, conn.SetReadDeadline(time.Time{}))
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "read payload")
|
||||
return nil, err
|
||||
}
|
||||
var metadata *adapter.InboundContext
|
||||
var errors []error
|
||||
|
||||
@@ -125,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[:])
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@ func NewSTDServer(ctx context.Context, router adapter.Router, logger log.Logger,
|
||||
tlsConfig.ServerName = options.ServerName
|
||||
}
|
||||
if len(options.ALPN) > 0 {
|
||||
tlsConfig.NextProtos = append(options.ALPN, tlsConfig.NextProtos...)
|
||||
tlsConfig.NextProtos = append(tlsConfig.NextProtos, options.ALPN...)
|
||||
}
|
||||
if options.MinVersion != "" {
|
||||
minVersion, err := ParseTLSVersion(options.MinVersion)
|
||||
|
||||
@@ -50,9 +50,6 @@ func (s *HistoryStorage) StoreURLTestHistory(tag string, history *History) {
|
||||
}
|
||||
|
||||
func URLTest(ctx context.Context, link string, detour N.Dialer) (t uint16, err error) {
|
||||
if link == "" {
|
||||
link = "https://www.gstatic.com/generate_204"
|
||||
}
|
||||
linkURL, err := url.Parse(link)
|
||||
if err != nil {
|
||||
return
|
||||
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,41 +1,3 @@
|
||||
#### 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:
|
||||
|
||||
As in Xray, 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
|
||||
* Download Yacd-meta by default if the specified Clash `external_ui` directory is empty
|
||||
* Add path and headers option for HTTP outbound
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.3-beta4
|
||||
|
||||
* Fix bugs
|
||||
|
||||
#### 1.3-beta2
|
||||
|
||||
* Download clash-dashboard if the specified Clash `external_ui` directory is empty
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
"clash_api": {
|
||||
"external_controller": "127.0.0.1:9090",
|
||||
"external_ui": "folder",
|
||||
"external_ui_download_url": "",
|
||||
"external_ui_download_detour": "",
|
||||
"secret": "",
|
||||
"default_mode": "rule",
|
||||
"store_selected": false,
|
||||
@@ -55,18 +53,6 @@ A relative path to the configuration directory or an absolute path to a
|
||||
directory in which you put some static web resource. sing-box will then
|
||||
serve it at `http://{{external-controller}}/ui`.
|
||||
|
||||
#### external_ui_download_url
|
||||
|
||||
ZIP download URL for the external UI, will be used if the specified `external_ui` directory is empty.
|
||||
|
||||
`https://github.com/MetaCubeX/Yacd-meta/archive/gh-pages.zip` will be used if empty.
|
||||
|
||||
#### external_ui_download_detour
|
||||
|
||||
The tag of the outbound to download the external UI.
|
||||
|
||||
Default outbound will be used if empty.
|
||||
|
||||
#### secret
|
||||
|
||||
Secret for the RESTful API (optional)
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
"clash_api": {
|
||||
"external_controller": "127.0.0.1:9090",
|
||||
"external_ui": "folder",
|
||||
"external_ui_download_url": "",
|
||||
"external_ui_download_detour": "",
|
||||
"secret": "",
|
||||
"default_mode": "rule",
|
||||
"store_selected": false,
|
||||
@@ -53,18 +51,6 @@ RESTful web API 监听地址。如果为空,则禁用 Clash API。
|
||||
|
||||
到静态网页资源目录的相对路径或绝对路径。sing-box 会在 `http://{{external-controller}}/ui` 下提供它。
|
||||
|
||||
#### external_ui_download_url
|
||||
|
||||
静态网页资源的 ZIP 下载 URL,如果指定的 `external_ui` 目录为空,将使用。
|
||||
|
||||
默认使用 `https://github.com/MetaCubeX/Yacd-meta/archive/gh-pages.zip`。
|
||||
|
||||
#### external_ui_download_detour
|
||||
|
||||
用于下载静态网页资源的出站的标签。
|
||||
|
||||
如果为空,将使用默认出站。
|
||||
|
||||
#### secret
|
||||
|
||||
RESTful API 的密钥(可选)
|
||||
|
||||
@@ -11,8 +11,6 @@
|
||||
"server_port": 1080,
|
||||
"username": "sekai",
|
||||
"password": "admin",
|
||||
"path": "",
|
||||
"headers": {},
|
||||
"tls": {},
|
||||
|
||||
... // Dial Fields
|
||||
@@ -41,14 +39,6 @@ Basic authorization username.
|
||||
|
||||
Basic authorization password.
|
||||
|
||||
#### path
|
||||
|
||||
Path of HTTP request.
|
||||
|
||||
#### headers
|
||||
|
||||
Extra headers of HTTP request.
|
||||
|
||||
#### tls
|
||||
|
||||
TLS configuration, see [TLS](/configuration/shared/tls/#outbound).
|
||||
|
||||
@@ -11,8 +11,6 @@
|
||||
"server_port": 1080,
|
||||
"username": "sekai",
|
||||
"password": "admin",
|
||||
"path": "",
|
||||
"headers": {},
|
||||
"tls": {},
|
||||
|
||||
... // 拨号字段
|
||||
@@ -41,14 +39,6 @@ Basic 认证用户名。
|
||||
|
||||
Basic 认证密码。
|
||||
|
||||
#### path
|
||||
|
||||
HTTP 请求路径。
|
||||
|
||||
#### headers
|
||||
|
||||
HTTP 请求的额外标头。
|
||||
|
||||
#### tls
|
||||
|
||||
TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#outbound)。
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"proxy-b",
|
||||
"proxy-c"
|
||||
],
|
||||
"url": "https://www.gstatic.com/generate_204",
|
||||
"url": "http://www.gstatic.com/generate_204",
|
||||
"interval": "1m",
|
||||
"tolerance": 50
|
||||
}
|
||||
@@ -26,7 +26,7 @@ List of outbound tags to test.
|
||||
|
||||
#### url
|
||||
|
||||
The URL to test. `https://www.gstatic.com/generate_204` will be used if empty.
|
||||
The URL to test. `http://www.gstatic.com/generate_204` will be used if empty.
|
||||
|
||||
#### interval
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"proxy-b",
|
||||
"proxy-c"
|
||||
],
|
||||
"url": "https://www.gstatic.com/generate_204",
|
||||
"url": "http://www.gstatic.com/generate_204",
|
||||
"interval": "1m",
|
||||
"tolerance": 50
|
||||
}
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
#### url
|
||||
|
||||
用于测试的链接。默认使用 `https://www.gstatic.com/generate_204`。
|
||||
用于测试的链接。默认使用 `http://www.gstatic.com/generate_204`。
|
||||
|
||||
#### interval
|
||||
|
||||
|
||||
@@ -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 `SFA/$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,78 +0,0 @@
|
||||
package clashapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/common/json"
|
||||
"github.com/sagernet/sing-box/experimental/clashapi/trafficontrol"
|
||||
"github.com/sagernet/websocket"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/render"
|
||||
)
|
||||
|
||||
// API created by Clash.Meta
|
||||
|
||||
func (s *Server) setupMetaAPI(r chi.Router) {
|
||||
r.Get("/memory", memory(s.trafficManager))
|
||||
r.Mount("/group", groupRouter(s))
|
||||
}
|
||||
|
||||
type Memory struct {
|
||||
Inuse uint64 `json:"inuse"`
|
||||
OSLimit uint64 `json:"oslimit"` // maybe we need it in the future
|
||||
}
|
||||
|
||||
func memory(trafficManager *trafficontrol.Manager) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var wsConn *websocket.Conn
|
||||
if websocket.IsWebSocketUpgrade(r) {
|
||||
var err error
|
||||
wsConn, err = upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if wsConn == nil {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
render.Status(r, http.StatusOK)
|
||||
}
|
||||
|
||||
tick := time.NewTicker(time.Second)
|
||||
defer tick.Stop()
|
||||
buf := &bytes.Buffer{}
|
||||
var err error
|
||||
first := true
|
||||
for range tick.C {
|
||||
buf.Reset()
|
||||
|
||||
inuse := trafficManager.Snapshot().Memory
|
||||
|
||||
// make chat.js begin with zero
|
||||
// this is shit var,but we need output 0 for first time
|
||||
if first {
|
||||
first = false
|
||||
inuse = 0
|
||||
}
|
||||
if err := json.NewEncoder(buf).Encode(Memory{
|
||||
Inuse: inuse,
|
||||
OSLimit: 0,
|
||||
}); err != nil {
|
||||
break
|
||||
}
|
||||
if wsConn == nil {
|
||||
_, err = w.Write(buf.Bytes())
|
||||
w.(http.Flusher).Flush()
|
||||
} else {
|
||||
err = wsConn.WriteMessage(websocket.TextMessage, buf.Bytes())
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
package clashapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/badjson"
|
||||
"github.com/sagernet/sing-box/common/urltest"
|
||||
"github.com/sagernet/sing-box/outbound"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/batch"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/render"
|
||||
)
|
||||
|
||||
func groupRouter(server *Server) http.Handler {
|
||||
r := chi.NewRouter()
|
||||
r.Get("/", getGroups(server))
|
||||
r.Route("/{name}", func(r chi.Router) {
|
||||
r.Use(parseProxyName, findProxyByName(server.router))
|
||||
r.Get("/", getGroup(server))
|
||||
r.Get("/delay", getGroupDelay(server))
|
||||
})
|
||||
return r
|
||||
}
|
||||
|
||||
func getGroups(server *Server) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
groups := common.Map(common.Filter(server.router.Outbounds(), func(it adapter.Outbound) bool {
|
||||
_, isGroup := it.(adapter.OutboundGroup)
|
||||
return isGroup
|
||||
}), func(it adapter.Outbound) *badjson.JSONObject {
|
||||
return proxyInfo(server, it)
|
||||
})
|
||||
render.JSON(w, r, render.M{
|
||||
"proxies": groups,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func getGroup(server *Server) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
proxy := r.Context().Value(CtxKeyProxy).(adapter.Outbound)
|
||||
if _, ok := proxy.(adapter.OutboundGroup); ok {
|
||||
render.JSON(w, r, proxyInfo(server, proxy))
|
||||
return
|
||||
}
|
||||
render.Status(r, http.StatusNotFound)
|
||||
render.JSON(w, r, ErrNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
func getGroupDelay(server *Server) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
proxy := r.Context().Value(CtxKeyProxy).(adapter.Outbound)
|
||||
group, ok := proxy.(adapter.OutboundGroup)
|
||||
if !ok {
|
||||
render.Status(r, http.StatusNotFound)
|
||||
render.JSON(w, r, ErrNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
query := r.URL.Query()
|
||||
url := query.Get("url")
|
||||
if strings.HasPrefix(url, "http://") {
|
||||
url = ""
|
||||
}
|
||||
timeout, err := strconv.ParseInt(query.Get("timeout"), 10, 32)
|
||||
if err != nil {
|
||||
render.Status(r, http.StatusBadRequest)
|
||||
render.JSON(w, r, ErrBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(r.Context(), time.Millisecond*time.Duration(timeout))
|
||||
defer cancel()
|
||||
|
||||
var result map[string]uint16
|
||||
if urlTestGroup, isURLTestGroup := group.(adapter.URLTestGroup); isURLTestGroup {
|
||||
result, err = urlTestGroup.URLTest(ctx, url)
|
||||
} else {
|
||||
outbounds := common.FilterNotNil(common.Map(group.All(), func(it string) adapter.Outbound {
|
||||
itOutbound, _ := server.router.Outbound(it)
|
||||
return itOutbound
|
||||
}))
|
||||
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 outbounds {
|
||||
tag := detour.Tag()
|
||||
realTag := outbound.RealTag(detour)
|
||||
if checked[realTag] {
|
||||
continue
|
||||
}
|
||||
checked[realTag] = true
|
||||
p, loaded := server.router.Outbound(realTag)
|
||||
if !loaded {
|
||||
continue
|
||||
}
|
||||
b.Go(realTag, func() (any, error) {
|
||||
t, err := urltest.URLTest(ctx, url, p)
|
||||
if err != nil {
|
||||
server.logger.Debug("outbound ", tag, " unavailable: ", err)
|
||||
server.urlTestHistory.DeleteURLTestHistory(realTag)
|
||||
} else {
|
||||
server.logger.Debug("outbound ", tag, " available: ", t, "ms")
|
||||
server.urlTestHistory.StoreURLTestHistory(realTag, &urltest.History{
|
||||
Time: time.Now(),
|
||||
Delay: t,
|
||||
})
|
||||
resultAccess.Lock()
|
||||
result[tag] = t
|
||||
resultAccess.Unlock()
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
}
|
||||
b.Wait()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
render.Status(r, http.StatusGatewayTimeout)
|
||||
render.JSON(w, r, newError(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, r, result)
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"net/http"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
@@ -215,9 +214,6 @@ func getProxyDelay(server *Server) func(w http.ResponseWriter, r *http.Request)
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
query := r.URL.Query()
|
||||
url := query.Get("url")
|
||||
if strings.HasPrefix(url, "http://") {
|
||||
url = ""
|
||||
}
|
||||
timeout, err := strconv.ParseInt(query.Get("timeout"), 10, 16)
|
||||
if err != nil {
|
||||
render.Status(r, http.StatusBadRequest)
|
||||
|
||||
@@ -109,8 +109,6 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
|
||||
r.Mount("/profile", profileRouter())
|
||||
r.Mount("/cache", cacheRouter(router))
|
||||
r.Mount("/dns", dnsRouter(router))
|
||||
|
||||
server.setupMetaAPI(r)
|
||||
})
|
||||
if options.ExternalUI != "" {
|
||||
server.externalUI = C.BasePath(os.ExpandEnv(options.ExternalUI))
|
||||
@@ -408,5 +406,5 @@ func getLogs(logFactory log.ObservableFactory) func(w http.ResponseWriter, r *ht
|
||||
}
|
||||
|
||||
func version(w http.ResponseWriter, r *http.Request) {
|
||||
render.JSON(w, r, render.M{"version": "sing-box " + C.Version, "premium": true, "meta": true})
|
||||
render.JSON(w, r, render.M{"version": "sing-box " + C.Version, "premium": true})
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ func (s *Server) downloadExternalUI() error {
|
||||
if s.externalUIDownloadURL != "" {
|
||||
downloadURL = s.externalUIDownloadURL
|
||||
} else {
|
||||
downloadURL = "https://github.com/MetaCubeX/Yacd-meta/archive/gh-pages.zip"
|
||||
downloadURL = "https://github.com/Dreamacro/clash-dashboard/archive/refs/heads/gh-pages.zip"
|
||||
}
|
||||
s.logger.Info("downloading external ui")
|
||||
var detour adapter.Outbound
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package trafficontrol
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/experimental/clashapi/compatible"
|
||||
@@ -19,15 +18,12 @@ type Manager struct {
|
||||
connections compatible.Map[string, tracker]
|
||||
ticker *time.Ticker
|
||||
done chan struct{}
|
||||
// process *process.Process
|
||||
memory uint64
|
||||
}
|
||||
|
||||
func NewManager() *Manager {
|
||||
manager := &Manager{
|
||||
ticker: time.NewTicker(time.Second),
|
||||
done: make(chan struct{}),
|
||||
// process: &process.Process{Pid: int32(os.Getpid())},
|
||||
}
|
||||
go manager.handle()
|
||||
return manager
|
||||
@@ -62,18 +58,10 @@ func (m *Manager) Snapshot() *Snapshot {
|
||||
return true
|
||||
})
|
||||
|
||||
//if memoryInfo, err := m.process.MemoryInfo(); err == nil {
|
||||
// m.memory = memoryInfo.RSS
|
||||
//} else {
|
||||
var memStats runtime.MemStats
|
||||
runtime.ReadMemStats(&memStats)
|
||||
m.memory = memStats.StackInuse + memStats.HeapInuse + memStats.HeapIdle - memStats.HeapReleased
|
||||
|
||||
return &Snapshot{
|
||||
UploadTotal: m.uploadTotal.Load(),
|
||||
DownloadTotal: m.downloadTotal.Load(),
|
||||
Connections: connections,
|
||||
Memory: m.memory,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,5 +100,4 @@ type Snapshot struct {
|
||||
DownloadTotal int64 `json:"downloadTotal"`
|
||||
UploadTotal int64 `json:"uploadTotal"`
|
||||
Connections []tracker `json:"connections"`
|
||||
Memory uint64 `json:"memory"`
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/sagernet/sing/common/atomic"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
"github.com/gofrs/uuid/v5"
|
||||
"github.com/gofrs/uuid"
|
||||
)
|
||||
|
||||
type Metadata struct {
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/debug"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/observable"
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
@@ -72,9 +71,7 @@ func (s *CommandServer) loopConnection(listener net.Listener) {
|
||||
go func() {
|
||||
hErr := s.handleConnection(conn)
|
||||
if hErr != nil && !E.IsClosed(err) {
|
||||
if debug.Enabled {
|
||||
log.Warn("log-server: process connection: ", hErr)
|
||||
}
|
||||
log.Warn("log-server: process connection: ", hErr)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ func (s *CommandServer) handleStatusConn(conn net.Conn) error {
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
return nil
|
||||
case <-ticker.C:
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
//go:build darwin || linux
|
||||
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var stderrFile *os.File
|
||||
|
||||
func RedirectStderr(path string) 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 = 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,8 +1,6 @@
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"github.com/sagernet/sing-box/option"
|
||||
)
|
||||
import "github.com/sagernet/sing-box/option"
|
||||
|
||||
type PlatformInterface interface {
|
||||
AutoDetectInterfaceControl(fd int32) error
|
||||
@@ -12,11 +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)
|
||||
}
|
||||
|
||||
type TunInterface interface {
|
||||
@@ -24,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
|
||||
}
|
||||
|
||||
@@ -49,11 +31,6 @@ type OnDemandRule interface {
|
||||
ProbeURL() string
|
||||
}
|
||||
|
||||
type OnDemandRuleIterator interface {
|
||||
Next() OnDemandRule
|
||||
HasNext() bool
|
||||
}
|
||||
|
||||
type onDemandRule struct {
|
||||
option.OnDemandRule
|
||||
}
|
||||
|
||||
@@ -1,33 +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
|
||||
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)
|
||||
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,13 +6,11 @@ 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"
|
||||
@@ -33,7 +31,7 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box
|
||||
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()
|
||||
@@ -60,12 +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) AutoDetectInterfaceControl() control.Func {
|
||||
@@ -76,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")
|
||||
}
|
||||
@@ -87,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) {
|
||||
@@ -130,36 +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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
18
go.mod
18
go.mod
@@ -4,7 +4,7 @@ go 1.18
|
||||
|
||||
require (
|
||||
berty.tech/go-libtor v1.0.385
|
||||
github.com/Dreamacro/clash v1.15.0
|
||||
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
|
||||
@@ -12,7 +12,7 @@ require (
|
||||
github.com/go-chi/chi/v5 v5.0.8
|
||||
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/gofrs/uuid v4.4.0+incompatible
|
||||
github.com/hashicorp/yamux v0.1.1
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||
@@ -22,15 +22,15 @@ require (
|
||||
github.com/oschwald/maxminddb-golang v1.10.0
|
||||
github.com/pires/go-proxyproto v0.7.0
|
||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0
|
||||
github.com/sagernet/gomobile v0.0.0-20230413023804-244d7ff07035
|
||||
github.com/sagernet/gomobile v0.0.0-20221130124640-349ebaa752ca
|
||||
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.4-0.20230418095640-3b5e6c1812d3
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230418025317-8a132998b322
|
||||
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d
|
||||
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230419061614-d744d03d9302
|
||||
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3
|
||||
github.com/sagernet/sing v0.2.2
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230408004833-5adaf486d440
|
||||
github.com/sagernet/sing-shadowsocks v0.2.1-0.20230408141421-e40d6a4e42d4
|
||||
github.com/sagernet/sing-shadowtls v0.1.1-0.20230408141548-81d74d2a8661
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab
|
||||
github.com/sagernet/sing-vmess v0.1.4-0.20230408141409-03460e4b014a
|
||||
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
|
||||
|
||||
36
go.sum
36
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.0 h1:mlpD950VEggXZBNahV66hyKDRxcczkj3vymoAt78KyE=
|
||||
github.com/Dreamacro/clash v1.15.0/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=
|
||||
@@ -33,8 +33,8 @@ 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-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=
|
||||
github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
||||
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
|
||||
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
@@ -101,8 +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-20230413023804-244d7ff07035 h1:KttYh6bBhIw8Y6/Ljn7CGwC3CKZn788rzMJmeAKjY+8=
|
||||
github.com/sagernet/gomobile v0.0.0-20230413023804-244d7ff07035/go.mod h1:5YE39YkJkCcMsfq1jMKkjsrM2GfBoF9JVWnvU89hmvU=
|
||||
github.com/sagernet/gomobile v0.0.0-20221130124640-349ebaa752ca h1:w56+kf8BeqLqllrRJ1tdwKc3sCdWOn/DuNHpY9fAiqs=
|
||||
github.com/sagernet/gomobile v0.0.0-20221130124640-349ebaa752ca/go.mod h1:5YE39YkJkCcMsfq1jMKkjsrM2GfBoF9JVWnvU89hmvU=
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 h1:tztuJB+giOWNRKQEBVY2oI3PsheTooMdh+/yxemYQYY=
|
||||
@@ -111,18 +111,18 @@ github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byL
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||
github.com/sagernet/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.4-0.20230418095640-3b5e6c1812d3 h1:dkH6SEs3yZlUjSXUAn64LUlFAfAgJqiThaWiBKWJ0Q0=
|
||||
github.com/sagernet/sing v0.2.4-0.20230418095640-3b5e6c1812d3/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230418025317-8a132998b322 h1:UDSeJZ2xB3dj1lySnM5LpF48dGlphGstw2BqtkJwcZI=
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230418025317-8a132998b322/go.mod h1:2wjxSr1Gbecq9A0ESA9cnR399tQTcpCZEOGytekb+qI=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d h1:UUxtLujzp5jmtOXqXpSOGvHwHSZcBveKVDzRJ4GlnFU=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d/go.mod h1:Co3PJXcaZoLwHGBfT0rbSnn9C7ywc41zVYWtDeoeI/Q=
|
||||
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.4-0.20230419061614-d744d03d9302 h1:aPb0T2HQRTG2t7fEwLvFLZSXmhmnBh+SMs2NufhmrsI=
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230419061614-d744d03d9302/go.mod h1:bvcVzlf9q9dgxt8qKluW+zOXCFoN1+SpBG3sHTq8/9Q=
|
||||
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.20230408141409-03460e4b014a h1:kClgb0phf3Jn1nKufk9eGTcnGriXpdbm5/dYtP613tA=
|
||||
github.com/sagernet/sing-vmess v0.1.4-0.20230408141409-03460e4b014a/go.mod h1:xuq/+XxniuiYqxK9Qdz9OPRqcDgjkwR2PaobjmNZbLM=
|
||||
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=
|
||||
|
||||
@@ -53,7 +53,7 @@ func NewDirect(ctx context.Context, router adapter.Router, logger log.ContextLog
|
||||
} else {
|
||||
udpTimeout = int64(C.UDPTimeout.Seconds())
|
||||
}
|
||||
inbound.udpNat = udpnat.New[netip.AddrPort](ctx, udpTimeout, adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound))
|
||||
inbound.udpNat = udpnat.New[netip.AddrPort](udpTimeout, adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound))
|
||||
inbound.connHandler = inbound
|
||||
inbound.packetHandler = inbound
|
||||
inbound.packetUpstream = inbound.udpNat
|
||||
|
||||
@@ -90,9 +90,6 @@ func (n *Naive) Start() error {
|
||||
n.httpServer = &http.Server{
|
||||
Handler: n,
|
||||
TLSConfig: tlsConfig,
|
||||
BaseContext: func(listener net.Listener) context.Context {
|
||||
return n.ctx
|
||||
},
|
||||
}
|
||||
go func() {
|
||||
var sErr error
|
||||
@@ -609,10 +606,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
|
||||
}
|
||||
|
||||
@@ -64,11 +64,11 @@ func newShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
|
||||
var err error
|
||||
switch {
|
||||
case options.Method == shadowsocks.MethodNone:
|
||||
inbound.service = shadowsocks.NewNoneService(ctx, options.UDPTimeout, inbound.upstreamContextHandler())
|
||||
inbound.service = shadowsocks.NewNoneService(options.UDPTimeout, inbound.upstreamContextHandler())
|
||||
case common.Contains(shadowaead.List, options.Method):
|
||||
inbound.service, err = shadowaead.NewService(ctx, options.Method, nil, options.Password, udpTimeout, inbound.upstreamContextHandler())
|
||||
inbound.service, err = shadowaead.NewService(options.Method, nil, options.Password, udpTimeout, inbound.upstreamContextHandler())
|
||||
case common.Contains(shadowaead_2022.List, options.Method):
|
||||
inbound.service, err = shadowaead_2022.NewServiceWithPassword(ctx, options.Method, options.Password, udpTimeout, inbound.upstreamContextHandler(), router.TimeFunc())
|
||||
inbound.service, err = shadowaead_2022.NewServiceWithPassword(options.Method, options.Password, udpTimeout, inbound.upstreamContextHandler(), router.TimeFunc())
|
||||
default:
|
||||
err = E.New("unsupported method: ", options.Method)
|
||||
}
|
||||
|
||||
@@ -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,28 +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](
|
||||
ctx,
|
||||
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](
|
||||
ctx,
|
||||
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
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@ func newShadowsocksRelay(ctx context.Context, router adapter.Router, logger log.
|
||||
udpTimeout = int64(C.UDPTimeout.Seconds())
|
||||
}
|
||||
service, err := shadowaead_2022.NewRelayServiceWithPassword[int](
|
||||
ctx,
|
||||
options.Method,
|
||||
options.Password,
|
||||
udpTimeout,
|
||||
|
||||
@@ -45,7 +45,7 @@ func NewTProxy(ctx context.Context, router adapter.Router, logger log.ContextLog
|
||||
}
|
||||
tproxy.connHandler = tproxy
|
||||
tproxy.oobPacketHandler = tproxy
|
||||
tproxy.udpNat = udpnat.New[netip.AddrPort](ctx, udpTimeout, tproxy.upstreamContextHandler())
|
||||
tproxy.udpNat = udpnat.New[netip.AddrPort](udpTimeout, tproxy.upstreamContextHandler())
|
||||
tproxy.packetUpstream = tproxy.udpNat
|
||||
return tproxy
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -2,7 +2,6 @@ package ntp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
@@ -10,7 +9,6 @@ import (
|
||||
"github.com/sagernet/sing-box/common/settings"
|
||||
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/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
@@ -22,7 +20,7 @@ var _ adapter.TimeService = (*Service)(nil)
|
||||
|
||||
type Service struct {
|
||||
ctx context.Context
|
||||
cancel common.ContextCancelCauseFunc
|
||||
cancel context.CancelFunc
|
||||
server M.Socksaddr
|
||||
writeToSystem bool
|
||||
dialer N.Dialer
|
||||
@@ -32,7 +30,7 @@ type Service struct {
|
||||
}
|
||||
|
||||
func NewService(ctx context.Context, router adapter.Router, logger logger.Logger, options option.NTPOptions) *Service {
|
||||
ctx, cancel := common.ContextWithCancelCause(ctx)
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
server := options.ServerOptions.Build()
|
||||
if server.Port == 0 {
|
||||
server.Port = 123
|
||||
@@ -66,7 +64,7 @@ func (s *Service) Start() error {
|
||||
|
||||
func (s *Service) Close() error {
|
||||
s.ticker.Stop()
|
||||
s.cancel(os.ErrClosed)
|
||||
s.cancel()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
@@ -27,9 +27,7 @@ type SocksOutboundOptions struct {
|
||||
type HTTPOutboundOptions struct {
|
||||
DialerOptions
|
||||
ServerOptions
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
TLS *OutboundTLSOptions `json:"tls,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Headers map[string]Listable[string] `json:"headers,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
TLS *OutboundTLSOptions `json:"tls,omitempty"`
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ func New(ctx context.Context, router adapter.Router, logger log.ContextLogger, t
|
||||
case C.TypeSelector:
|
||||
return NewSelector(router, logger, tag, options.SelectorOptions)
|
||||
case C.TypeURLTest:
|
||||
return NewURLTest(ctx, router, logger, tag, options.URLTestOptions)
|
||||
return NewURLTest(router, logger, tag, options.URLTestOptions)
|
||||
default:
|
||||
return nil, E.New("unknown outbound type: ", options.Type)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -102,10 +102,11 @@ 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 {
|
||||
ctx = adapter.WithContext(ctx, &metadata)
|
||||
fastClose, cancel := common.ContextWithCancelCause(ctx)
|
||||
fastClose, cancel := context.WithCancel(ctx)
|
||||
timeout := canceler.New(fastClose, cancel, C.DNSTimeout)
|
||||
var group task.Group
|
||||
group.Append0(func(ctx context.Context) error {
|
||||
defer cancel()
|
||||
_buffer := buf.StackNewSize(dns.FixedPacketSize)
|
||||
defer common.KeepAlive(_buffer)
|
||||
buffer := common.Dup(_buffer)
|
||||
@@ -114,13 +115,11 @@ func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metada
|
||||
buffer.FullReset()
|
||||
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()
|
||||
@@ -128,22 +127,17 @@ func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metada
|
||||
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
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package outbound
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
@@ -15,14 +14,14 @@ import (
|
||||
"github.com/sagernet/sing/common"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
sHTTP "github.com/sagernet/sing/protocol/http"
|
||||
"github.com/sagernet/sing/protocol/http"
|
||||
)
|
||||
|
||||
var _ adapter.Outbound = (*HTTP)(nil)
|
||||
|
||||
type HTTP struct {
|
||||
myOutboundAdapter
|
||||
client *sHTTP.Client
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
func NewHTTP(router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPOutboundOptions) (*HTTP, error) {
|
||||
@@ -30,13 +29,6 @@ func NewHTTP(router adapter.Router, logger log.ContextLogger, tag string, option
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var headers http.Header
|
||||
if options.Headers != nil {
|
||||
headers = make(http.Header)
|
||||
for key, values := range options.Headers {
|
||||
headers[key] = values
|
||||
}
|
||||
}
|
||||
return &HTTP{
|
||||
myOutboundAdapter{
|
||||
protocol: C.TypeHTTP,
|
||||
@@ -45,14 +37,7 @@ func NewHTTP(router adapter.Router, logger log.ContextLogger, tag string, option
|
||||
logger: logger,
|
||||
tag: tag,
|
||||
},
|
||||
sHTTP.NewClient(sHTTP.Options{
|
||||
Dialer: detour,
|
||||
Server: options.ServerOptions.Build(),
|
||||
Username: options.Username,
|
||||
Password: options.Password,
|
||||
Path: options.Path,
|
||||
Headers: headers,
|
||||
}),
|
||||
http.NewClient(detour, options.ServerOptions.Build(), options.Username, options.Password, nil),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"net"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
@@ -13,7 +12,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,14 +19,12 @@ 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 {
|
||||
myOutboundAdapter
|
||||
ctx context.Context
|
||||
tags []string
|
||||
link string
|
||||
interval time.Duration
|
||||
@@ -36,7 +32,7 @@ type URLTest struct {
|
||||
group *URLTestGroup
|
||||
}
|
||||
|
||||
func NewURLTest(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.URLTestOutboundOptions) (*URLTest, error) {
|
||||
func NewURLTest(router adapter.Router, logger log.ContextLogger, tag string, options option.URLTestOutboundOptions) (*URLTest, error) {
|
||||
outbound := &URLTest{
|
||||
myOutboundAdapter: myOutboundAdapter{
|
||||
protocol: C.TypeURLTest,
|
||||
@@ -44,7 +40,6 @@ func NewURLTest(ctx context.Context, router adapter.Router, logger log.ContextLo
|
||||
logger: logger,
|
||||
tag: tag,
|
||||
},
|
||||
ctx: ctx,
|
||||
tags: options.Outbounds,
|
||||
link: options.URL,
|
||||
interval: time.Duration(options.Interval),
|
||||
@@ -72,12 +67,11 @@ 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
|
||||
s.group = NewURLTestGroup(s.router, s.logger, outbounds, s.link, s.interval, s.tolerance)
|
||||
return s.group.Start()
|
||||
}
|
||||
|
||||
func (s *URLTest) Close() error {
|
||||
func (s URLTest) Close() error {
|
||||
return common.Close(
|
||||
common.PtrOrNil(s.group),
|
||||
)
|
||||
@@ -91,31 +85,39 @@ func (s *URLTest) All() []string {
|
||||
return s.tags
|
||||
}
|
||||
|
||||
func (s *URLTest) URLTest(ctx context.Context, link string) (map[string]uint16, error) {
|
||||
return s.group.URLTest(ctx, link)
|
||||
}
|
||||
|
||||
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 {
|
||||
return conn, nil
|
||||
}
|
||||
s.logger.ErrorContext(ctx, err)
|
||||
s.group.history.DeleteURLTestHistory(outbound.Tag())
|
||||
go s.group.checkOutbounds()
|
||||
outbounds := s.group.Fallback(outbound)
|
||||
for _, fallback := range outbounds {
|
||||
conn, err = fallback.DialContext(ctx, network, destination)
|
||||
if err == nil {
|
||||
return conn, nil
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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 {
|
||||
return conn, nil
|
||||
}
|
||||
s.logger.ErrorContext(ctx, err)
|
||||
s.group.history.DeleteURLTestHistory(outbound.Tag())
|
||||
go s.group.checkOutbounds()
|
||||
outbounds := s.group.Fallback(outbound)
|
||||
for _, fallback := range outbounds {
|
||||
conn, err = fallback.ListenPacket(ctx, destination)
|
||||
if err == nil {
|
||||
return conn, nil
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -127,13 +129,7 @@ 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
|
||||
logger log.Logger
|
||||
outbounds []adapter.Outbound
|
||||
@@ -141,14 +137,16 @@ type URLTestGroup struct {
|
||||
interval time.Duration
|
||||
tolerance uint16
|
||||
history *urltest.HistoryStorage
|
||||
checking atomic.Bool
|
||||
|
||||
access sync.Mutex
|
||||
ticker *time.Ticker
|
||||
close chan struct{}
|
||||
}
|
||||
|
||||
func NewURLTestGroup(ctx context.Context, router adapter.Router, logger log.Logger, outbounds []adapter.Outbound, link string, interval time.Duration, tolerance uint16) *URLTestGroup {
|
||||
func NewURLTestGroup(router adapter.Router, logger log.Logger, outbounds []adapter.Outbound, link string, interval time.Duration, tolerance uint16) *URLTestGroup {
|
||||
if link == "" {
|
||||
//goland:noinspection HttpUrlsUsage
|
||||
link = "http://www.gstatic.com/generate_204"
|
||||
}
|
||||
if interval == 0 {
|
||||
interval = C.DefaultURLTestInterval
|
||||
}
|
||||
@@ -162,7 +160,6 @@ func NewURLTestGroup(ctx context.Context, router adapter.Router, logger log.Logg
|
||||
history = urltest.NewHistoryStorage()
|
||||
}
|
||||
return &URLTestGroup{
|
||||
ctx: ctx,
|
||||
router: router,
|
||||
logger: logger,
|
||||
outbounds: outbounds,
|
||||
@@ -174,23 +171,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
|
||||
@@ -250,34 +237,20 @@ 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) 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))
|
||||
func (g *URLTestGroup) checkOutbounds() {
|
||||
b, _ := batch.New(context.Background(), batch.WithConcurrencyNum[any](10))
|
||||
checked := make(map[string]bool)
|
||||
var resultAccess sync.Mutex
|
||||
for _, detour := range g.outbounds {
|
||||
tag := detour.Tag()
|
||||
realTag := RealTag(detour)
|
||||
@@ -285,7 +258,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
|
||||
@@ -296,7 +269,7 @@ func (g *URLTestGroup) urlTest(ctx context.Context, link string, force bool) (ma
|
||||
b.Go(realTag, func() (any, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), C.TCPTimeout)
|
||||
defer cancel()
|
||||
t, err := urltest.URLTest(ctx, link, p)
|
||||
t, err := urltest.URLTest(ctx, g.link, p)
|
||||
if err != nil {
|
||||
g.logger.Debug("outbound ", tag, " unavailable: ", err)
|
||||
g.history.DeleteURLTestHistory(realTag)
|
||||
@@ -306,13 +279,9 @@ func (g *URLTestGroup) urlTest(ctx context.Context, link string, force bool) (ma
|
||||
Time: time.Now(),
|
||||
Delay: t,
|
||||
})
|
||||
resultAccess.Lock()
|
||||
result[tag] = t
|
||||
resultAccess.Unlock()
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
}
|
||||
b.Wait()
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
115
route/router.go
115
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,7 +143,7 @@ func NewRouter(
|
||||
defaultMark: options.DefaultMark,
|
||||
platformInterface: platformInterface,
|
||||
}
|
||||
router.dnsClient = dns.NewClient(ctx, dnsOptions.DNSClientOptions.DisableCache, dnsOptions.DNSClientOptions.DisableExpire, 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 {
|
||||
@@ -259,36 +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,
|
||||
})
|
||||
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
|
||||
@@ -634,14 +657,10 @@ 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()
|
||||
sniffMetadata, err := sniff.PeekStream(ctx, conn, buffer, time.Duration(metadata.InboundOptions.SniffTimeout), sniff.StreamDomainNameQuery, sniff.TLSClientHello, sniff.HTTPHost)
|
||||
sniffMetadata, _ := sniff.PeekStream(ctx, conn, buffer, time.Duration(metadata.InboundOptions.SniffTimeout), sniff.StreamDomainNameQuery, sniff.TLSClientHello, sniff.HTTPHost)
|
||||
if sniffMetadata != nil {
|
||||
metadata.Protocol = sniffMetadata.Protocol
|
||||
metadata.Domain = sniffMetadata.Domain
|
||||
@@ -656,8 +675,6 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
|
||||
} else {
|
||||
r.logger.DebugContext(ctx, "sniffed protocol: ", metadata.Protocol)
|
||||
}
|
||||
} else if err != nil {
|
||||
r.logger.TraceContext(ctx, "sniffed no protocol: ", err)
|
||||
}
|
||||
if !buffer.IsEmpty() {
|
||||
conn = bufio.NewCachedConn(conn, buffer)
|
||||
@@ -743,11 +760,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()
|
||||
@@ -872,25 +884,6 @@ 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
|
||||
}
|
||||
@@ -975,7 +968,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"
|
||||
@@ -987,10 +980,6 @@ func (r *Router) notifyNetworkUpdate(int) error {
|
||||
r.logger.Info("updated default interface ", r.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", r.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified()))
|
||||
}
|
||||
|
||||
if conntrack.Enabled {
|
||||
conntrack.Close()
|
||||
}
|
||||
|
||||
for _, outbound := range r.outbounds {
|
||||
listener, isListener := outbound.(adapter.InterfaceUpdateListener)
|
||||
if isListener {
|
||||
|
||||
@@ -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),
|
||||
|
||||
15
test/go.mod
15
test/go.mod
@@ -10,8 +10,8 @@ require (
|
||||
github.com/docker/docker v20.10.18+incompatible
|
||||
github.com/docker/go-connections v0.4.0
|
||||
github.com/gofrs/uuid v4.4.0+incompatible
|
||||
github.com/sagernet/sing v0.2.4-0.20230418025125-f196b4303e31
|
||||
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d
|
||||
github.com/sagernet/sing v0.2.2-0.20230407053809-308e421e33c2
|
||||
github.com/sagernet/sing-shadowsocks v0.2.0
|
||||
github.com/spyzhov/ajson v0.7.1
|
||||
github.com/stretchr/testify v1.8.2
|
||||
go.uber.org/goleak v1.2.0
|
||||
@@ -20,7 +20,7 @@ require (
|
||||
|
||||
require (
|
||||
berty.tech/go-libtor v1.0.385 // indirect
|
||||
github.com/Dreamacro/clash v1.15.0 // indirect
|
||||
github.com/Dreamacro/clash v1.14.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.5.1 // indirect
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
@@ -36,14 +36,13 @@ require (
|
||||
github.com/go-chi/cors v1.2.1 // indirect
|
||||
github.com/go-chi/render v1.0.2 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/gofrs/uuid/v5 v5.0.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // 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/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16 // indirect
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230327135226-74ae03f2425e // indirect
|
||||
github.com/josharian/native v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.15.15 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.1.1 // indirect
|
||||
@@ -71,9 +70,9 @@ 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.20230418025317-8a132998b322 // indirect
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230407055526-2a27418e7855 // indirect
|
||||
github.com/sagernet/sing-shadowtls v0.1.0 // indirect
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230419061614-d744d03d9302 // indirect
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab // indirect
|
||||
github.com/sagernet/sing-vmess v0.1.3 // indirect
|
||||
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect
|
||||
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 // indirect
|
||||
@@ -88,7 +87,7 @@ require (
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.24.0 // indirect
|
||||
go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35 // indirect
|
||||
golang.org/x/crypto v0.8.0 // indirect
|
||||
golang.org/x/crypto v0.7.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
|
||||
golang.org/x/mod v0.8.0 // indirect
|
||||
golang.org/x/sys v0.7.0 // indirect
|
||||
|
||||
30
test/go.sum
30
test/go.sum
@@ -1,8 +1,8 @@
|
||||
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/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=
|
||||
@@ -45,8 +45,6 @@ github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8Wd
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
|
||||
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
|
||||
github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
@@ -63,8 +61,8 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe
|
||||
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16 h1:+aAGyK41KRn8jbF2Q7PLL0Sxwg6dShGcQSeCC7nZQ8E=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16/go.mod h1:IKrnDWs3/Mqq5n0lI+RxA2sB7MvN/vbMBP3ehXg65UI=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230327135226-74ae03f2425e h1:8ChxkWKTVYg7LKBvYNLNRnlobgbPrzzossZUoST2T7o=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230327135226-74ae03f2425e/go.mod h1:IKrnDWs3/Mqq5n0lI+RxA2sB7MvN/vbMBP3ehXg65UI=
|
||||
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
||||
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
@@ -128,16 +126,16 @@ 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.4-0.20230418025125-f196b4303e31 h1:qgq8jeY/rbnY9NwYXByO//AP0ByIxnsKUxQx1tOB3W0=
|
||||
github.com/sagernet/sing v0.2.4-0.20230418025125-f196b4303e31/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230418025317-8a132998b322 h1:UDSeJZ2xB3dj1lySnM5LpF48dGlphGstw2BqtkJwcZI=
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230418025317-8a132998b322/go.mod h1:2wjxSr1Gbecq9A0ESA9cnR399tQTcpCZEOGytekb+qI=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d h1:UUxtLujzp5jmtOXqXpSOGvHwHSZcBveKVDzRJ4GlnFU=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d/go.mod h1:Co3PJXcaZoLwHGBfT0rbSnn9C7ywc41zVYWtDeoeI/Q=
|
||||
github.com/sagernet/sing v0.2.2-0.20230407053809-308e421e33c2 h1:VjeHDxEgpB2fqK5G16yBvtLacibvg3h2MsIjal0UXH0=
|
||||
github.com/sagernet/sing v0.2.2-0.20230407053809-308e421e33c2/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw=
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230407055526-2a27418e7855 h1:a3W2X1n5C/oYGp/Dd26eoymME3iXN8TJq7LZtO2MSUY=
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230407055526-2a27418e7855/go.mod h1:69PNSHyEmXdjf6C+bXBOdr2GQnPeEyWjIzo/MV8gmz8=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.0 h1:ILDWL7pwWfkPLEbviE/MyCgfjaBmJY/JVVY+5jhSb58=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.0/go.mod h1:ysYzszRLpNzJSorvlWRMuzU6Vchsp7sd52q+JNY4axw=
|
||||
github.com/sagernet/sing-shadowtls v0.1.0 h1:05MYce8aR5xfKIn+y7xRFsdKhKt44QZTSEQW+lG5IWQ=
|
||||
github.com/sagernet/sing-shadowtls v0.1.0/go.mod h1:Kn1VUIprdkwCgkS6SXYaLmIpKzQbqBIKJBMY+RvBhYc=
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230419061614-d744d03d9302 h1:aPb0T2HQRTG2t7fEwLvFLZSXmhmnBh+SMs2NufhmrsI=
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230419061614-d744d03d9302/go.mod h1:bvcVzlf9q9dgxt8qKluW+zOXCFoN1+SpBG3sHTq8/9Q=
|
||||
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.3 h1:q/+tsF46dvvapL6CpQBgPHJ6nQrDUZqEtLHCbsjO7iM=
|
||||
github.com/sagernet/sing-vmess v0.1.3/go.mod h1:GVXqAHwe9U21uS+Voh4YBIrADQyE4F9v0ayGSixSQAE=
|
||||
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as=
|
||||
@@ -191,8 +189,8 @@ golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaE
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
|
||||
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
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=
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
||||
|
||||
"github.com/gofrs/uuid/v5"
|
||||
"github.com/gofrs/uuid"
|
||||
)
|
||||
|
||||
var muxProtocols = []mux.Protocol{
|
||||
|
||||
@@ -141,95 +141,6 @@ func TestVLESSVisionReality(t *testing.T) {
|
||||
testSuit(t, clientPort, testPort)
|
||||
}
|
||||
|
||||
func TestVLESSVisionRealityPlain(t *testing.T) {
|
||||
userUUID := newUUID()
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
||||
ListenPort: clientPort,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: C.TypeVLESS,
|
||||
VLESSOptions: option.VLESSInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
||||
ListenPort: serverPort,
|
||||
},
|
||||
Users: []option.VLESSUser{
|
||||
{
|
||||
Name: "sekai",
|
||||
UUID: userUUID.String(),
|
||||
Flow: vless.FlowVision,
|
||||
},
|
||||
},
|
||||
TLS: &option.InboundTLSOptions{
|
||||
Enabled: true,
|
||||
ServerName: "google.com",
|
||||
Reality: &option.InboundRealityOptions{
|
||||
Enabled: true,
|
||||
Handshake: option.InboundRealityHandshakeOptions{
|
||||
ServerOptions: option.ServerOptions{
|
||||
Server: "google.com",
|
||||
ServerPort: 443,
|
||||
},
|
||||
},
|
||||
ShortID: []string{"0123456789abcdef"},
|
||||
PrivateKey: "UuMBgl7MXTPx9inmQp2UC7Jcnwc6XYbwDNebonM-FCc",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Outbounds: []option.Outbound{
|
||||
{
|
||||
Type: C.TypeDirect,
|
||||
},
|
||||
{
|
||||
Type: C.TypeVLESS,
|
||||
Tag: "vless-out",
|
||||
VLESSOptions: option.VLESSOutboundOptions{
|
||||
ServerOptions: option.ServerOptions{
|
||||
Server: "127.0.0.1",
|
||||
ServerPort: serverPort,
|
||||
},
|
||||
UUID: userUUID.String(),
|
||||
Flow: vless.FlowVision,
|
||||
TLS: &option.OutboundTLSOptions{
|
||||
Enabled: true,
|
||||
ServerName: "google.com",
|
||||
Reality: &option.OutboundRealityOptions{
|
||||
Enabled: true,
|
||||
ShortID: "0123456789abcdef",
|
||||
PublicKey: "jNXHt1yRo0vDuchQlIP6Z0ZvjT3KtzVI-T4E7RoLJS0",
|
||||
},
|
||||
UTLS: &option.OutboundUTLSOptions{
|
||||
Enabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Route: &option.RouteOptions{
|
||||
Rules: []option.Rule{
|
||||
{
|
||||
DefaultOptions: option.DefaultRule{
|
||||
Inbound: []string{"mixed-in"},
|
||||
Outbound: "vless-out",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
testSuit(t, clientPort, testPort)
|
||||
}
|
||||
|
||||
func TestVLESSRealityTransport(t *testing.T) {
|
||||
t.Run("grpc", func(t *testing.T) {
|
||||
testVLESSRealityTransport(t, &option.V2RayTransportOptions{
|
||||
@@ -249,6 +160,8 @@ func TestVLESSRealityTransport(t *testing.T) {
|
||||
}
|
||||
|
||||
func testVLESSRealityTransport(t *testing.T, transport *option.V2RayTransportOptions) {
|
||||
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||
|
||||
userUUID := newUUID()
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
@@ -293,11 +206,52 @@ func testVLESSRealityTransport(t *testing.T, transport *option.V2RayTransportOpt
|
||||
Transport: transport,
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: C.TypeTrojan,
|
||||
Tag: "trojan",
|
||||
TrojanOptions: option.TrojanInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
||||
ListenPort: otherPort,
|
||||
},
|
||||
Users: []option.TrojanUser{
|
||||
{
|
||||
Name: "sekai",
|
||||
Password: userUUID.String(),
|
||||
},
|
||||
},
|
||||
TLS: &option.InboundTLSOptions{
|
||||
Enabled: true,
|
||||
ServerName: "example.org",
|
||||
CertificatePath: certPem,
|
||||
KeyPath: keyPem,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Outbounds: []option.Outbound{
|
||||
{
|
||||
Type: C.TypeDirect,
|
||||
},
|
||||
{
|
||||
Type: C.TypeTrojan,
|
||||
Tag: "trojan-out",
|
||||
TrojanOptions: option.TrojanOutboundOptions{
|
||||
ServerOptions: option.ServerOptions{
|
||||
Server: "127.0.0.1",
|
||||
ServerPort: otherPort,
|
||||
},
|
||||
Password: userUUID.String(),
|
||||
TLS: &option.OutboundTLSOptions{
|
||||
Enabled: true,
|
||||
ServerName: "example.org",
|
||||
CertificatePath: certPem,
|
||||
},
|
||||
DialerOptions: option.DialerOptions{
|
||||
Detour: "vless-out",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: C.TypeVLESS,
|
||||
Tag: "vless-out",
|
||||
@@ -328,7 +282,7 @@ func testVLESSRealityTransport(t *testing.T, transport *option.V2RayTransportOpt
|
||||
{
|
||||
DefaultOptions: option.DefaultRule{
|
||||
Inbound: []string{"mixed-in"},
|
||||
Outbound: "vless-out",
|
||||
Outbound: "trojan-out",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/udpnat"
|
||||
)
|
||||
|
||||
func TestUDPNatClose(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
connCtx, connCancel := common.ContextWithCancelCause(context.Background())
|
||||
defer connCancel(net.ErrClosed)
|
||||
service := udpnat.New[int](ctx, 1, &testUDPNatCloseHandler{connCancel})
|
||||
service.NewPacket(ctx, 0, buf.As([]byte("Hello")), M.Metadata{}, func(natConn N.PacketConn) N.PacketWriter {
|
||||
return &testPacketWriter{}
|
||||
})
|
||||
select {
|
||||
case <-connCtx.Done():
|
||||
if E.IsClosed(connCtx.Err()) {
|
||||
t.Fatal(E.New("conn closed unexpectedly: ", connCtx.Err()))
|
||||
}
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Fatal("conn not closed")
|
||||
}
|
||||
}
|
||||
|
||||
type testUDPNatCloseHandler struct {
|
||||
done common.ContextCancelCauseFunc
|
||||
}
|
||||
|
||||
func (h *testUDPNatCloseHandler) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
||||
for {
|
||||
buffer := buf.NewPacket()
|
||||
_, err := conn.ReadPacket(buffer)
|
||||
buffer.Release()
|
||||
if err != nil {
|
||||
h.done(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *testUDPNatCloseHandler) NewError(ctx context.Context, err error) {
|
||||
}
|
||||
|
||||
type testPacketWriter struct{}
|
||||
|
||||
func (t *testPacketWriter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
return nil
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
|
||||
"github.com/gofrs/uuid/v5"
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/spyzhov/ajson"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
|
||||
"github.com/gofrs/uuid/v5"
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
|
||||
"github.com/gofrs/uuid/v5"
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/spyzhov/ajson"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/vless"
|
||||
|
||||
"github.com/gofrs/uuid/v5"
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/spyzhov/ajson"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
|
||||
"github.com/gofrs/uuid/v5"
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/spyzhov/ajson"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -119,7 +119,7 @@ func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg,
|
||||
func (t *Transport) fetchInterface() (*net.Interface, error) {
|
||||
interfaceName := t.interfaceName
|
||||
if t.autoInterface {
|
||||
if t.router.InterfaceMonitor() == nil {
|
||||
if t.router.NetworkMonitor() == nil {
|
||||
return nil, E.New("missing monitor for auto DHCP, set route.auto_detect_interface")
|
||||
}
|
||||
interfaceName = t.router.InterfaceMonitor().DefaultInterfaceName(netip.Addr{})
|
||||
|
||||
@@ -17,30 +17,22 @@ type NATPacketConn struct {
|
||||
func NewNATPacketConn(conn N.PacketConn, origin M.Socksaddr, destination M.Socksaddr) *NATPacketConn {
|
||||
return &NATPacketConn{
|
||||
PacketConn: conn,
|
||||
origin: socksaddrWithoutPort(origin),
|
||||
destination: socksaddrWithoutPort(destination),
|
||||
origin: origin,
|
||||
destination: destination,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *NATPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
|
||||
destination, err = c.PacketConn.ReadPacket(buffer)
|
||||
if socksaddrWithoutPort(destination) == c.origin {
|
||||
destination = M.Socksaddr{
|
||||
Addr: c.destination.Addr,
|
||||
Fqdn: c.destination.Fqdn,
|
||||
Port: destination.Port,
|
||||
}
|
||||
if destination == c.origin {
|
||||
destination = c.destination
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *NATPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
if socksaddrWithoutPort(destination) == c.destination {
|
||||
destination = M.Socksaddr{
|
||||
Addr: c.origin.Addr,
|
||||
Fqdn: c.origin.Fqdn,
|
||||
Port: destination.Port,
|
||||
}
|
||||
if destination == c.destination {
|
||||
destination = c.origin
|
||||
}
|
||||
return c.PacketConn.WritePacket(buffer, destination)
|
||||
}
|
||||
@@ -48,8 +40,3 @@ func (c *NATPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr)
|
||||
func (c *NATPacketConn) Upstream() any {
|
||||
return c.PacketConn
|
||||
}
|
||||
|
||||
func socksaddrWithoutPort(destination M.Socksaddr) M.Socksaddr {
|
||||
destination.Port = 0
|
||||
return destination
|
||||
}
|
||||
|
||||
@@ -498,12 +498,7 @@ func (c *PacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
return
|
||||
}
|
||||
n = copy(p, msg.Data)
|
||||
destination := M.ParseSocksaddrHostPort(msg.Host, msg.Port)
|
||||
if destination.IsFqdn() {
|
||||
addr = destination
|
||||
} else {
|
||||
addr = destination.UDPAddr()
|
||||
}
|
||||
addr = M.ParseSocksaddrHostPort(msg.Host, msg.Port).UDPAddr()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -535,10 +530,6 @@ func (c *PacketConn) SetWriteDeadline(t time.Time) error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (c *PacketConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *PacketConn) Read(b []byte) (n int, err error) {
|
||||
return 0, os.ErrInvalid
|
||||
}
|
||||
|
||||
@@ -32,10 +32,10 @@ type TLSObfs struct {
|
||||
func (to *TLSObfs) read(b []byte, discardN int) (int, error) {
|
||||
buf := B.Get(discardN)
|
||||
_, err := io.ReadFull(to.Conn, buf)
|
||||
B.Put(buf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
B.Put(buf)
|
||||
|
||||
sizeBuf := make([]byte, 2)
|
||||
_, err = io.ReadFull(to.Conn, sizeBuf)
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"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"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
@@ -53,7 +55,7 @@ func newMuxConnection0(ctx context.Context, stream net.Conn, metadata M.Metadata
|
||||
case CommandTCP:
|
||||
return handler.NewConnection(ctx, stream, metadata)
|
||||
case CommandUDP:
|
||||
return handler.NewPacketConnection(ctx, &PacketConn{stream}, metadata)
|
||||
return handler.NewPacketConnection(ctx, deadline.NewPacketConn(bufio.NewNetPacketConn(&PacketConn{stream})), metadata)
|
||||
default:
|
||||
return E.New("unknown command ", command)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user