Compare commits

..

41 Commits

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

View File

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

View File

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

6
box.go
View File

@@ -133,12 +133,6 @@ func New(options Options) (*Box, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if options.PlatformInterface != nil {
err = options.PlatformInterface.Initialize(ctx, router)
if err != nil {
return nil, E.Cause(err, "initialize platform interface")
}
}
preServices := make(map[string]adapter.Service) preServices := make(map[string]adapter.Service)
postServices := make(map[string]adapter.Service) postServices := make(map[string]adapter.Service)
if needClashAPI { if needClashAPI {

View File

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

View File

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

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

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

View File

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

View File

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

View File

@@ -11,6 +11,7 @@ import (
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio" "github.com/sagernet/sing/common/bufio"
"github.com/sagernet/sing/common/bufio/deadline"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
@@ -68,7 +69,7 @@ func (c *Client) DialContext(ctx context.Context, network string, destination M.
if err != nil { if err != nil {
return nil, err return nil, err
} }
return bufio.NewUnbindPacketConn(&ClientPacketConn{ExtendedConn: bufio.NewExtendedConn(stream), destination: destination}), nil return bufio.NewBindPacketConn(deadline.NewPacketConn(bufio.NewNetPacketConn(&ClientPacketConn{ExtendedConn: bufio.NewExtendedConn(stream), destination: destination})), destination), nil
default: default:
return nil, E.Extend(N.ErrUnknownNetwork, network) return nil, E.Extend(N.ErrUnknownNetwork, network)
} }
@@ -79,7 +80,7 @@ func (c *Client) ListenPacket(ctx context.Context, destination M.Socksaddr) (net
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &ClientPacketAddrConn{ExtendedConn: bufio.NewExtendedConn(stream), destination: destination}, nil return deadline.NewPacketConn(&ClientPacketAddrConn{ExtendedConn: bufio.NewExtendedConn(stream), destination: destination}), nil
} }
func (c *Client) openStream() (net.Conn, error) { func (c *Client) openStream() (net.Conn, error) {
@@ -249,10 +250,6 @@ func (c *ClientConn) WriterReplaceable() bool {
return c.requestWrite return c.requestWrite
} }
func (c *ClientConn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *ClientConn) Upstream() any { func (c *ClientConn) Upstream() any {
return c.Conn return c.Conn
} }
@@ -381,10 +378,6 @@ func (c *ClientPacketConn) RemoteAddr() net.Addr {
return c.destination.UDPAddr() return c.destination.UDPAddr()
} }
func (c *ClientPacketConn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *ClientPacketConn) Upstream() any { func (c *ClientPacketConn) Upstream() any {
return c.ExtendedConn return c.ExtendedConn
} }
@@ -526,10 +519,6 @@ func (c *ClientPacketAddrConn) FrontHeadroom() int {
return 2 + M.MaxSocksaddrLength return 2 + M.MaxSocksaddrLength
} }
func (c *ClientPacketAddrConn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *ClientPacketAddrConn) Upstream() any { func (c *ClientPacketAddrConn) Upstream() any {
return c.ExtendedConn return c.ExtendedConn
} }

View File

@@ -10,6 +10,7 @@ import (
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio" "github.com/sagernet/sing/common/bufio"
"github.com/sagernet/sing/common/bufio/deadline"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
@@ -67,7 +68,7 @@ func newConnection(ctx context.Context, router adapter.Router, errorHandler E.Ha
logger.InfoContext(ctx, "inbound multiplex packet connection") logger.InfoContext(ctx, "inbound multiplex packet connection")
packetConn = &ServerPacketAddrConn{ExtendedConn: bufio.NewExtendedConn(stream)} packetConn = &ServerPacketAddrConn{ExtendedConn: bufio.NewExtendedConn(stream)}
} }
hErr := router.RoutePacketConnection(ctx, packetConn, metadata) hErr := router.RoutePacketConnection(ctx, deadline.NewPacketConn(bufio.NewNetPacketConn(packetConn)), metadata)
stream.Close() stream.Close()
if hErr != nil { if hErr != nil {
errorHandler.NewError(ctx, hErr) errorHandler.NewError(ctx, hErr)
@@ -131,10 +132,6 @@ func (c *ServerConn) FrontHeadroom() int {
return 0 return 0
} }
func (c *ServerConn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *ServerConn) Upstream() any { func (c *ServerConn) Upstream() any {
return c.ExtendedConn return c.ExtendedConn
} }
@@ -187,10 +184,6 @@ func (c *ServerPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksad
return c.ExtendedConn.WriteBuffer(buffer) return c.ExtendedConn.WriteBuffer(buffer)
} }
func (c *ServerPacketConn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *ServerPacketConn) Upstream() any { func (c *ServerPacketConn) Upstream() any {
return c.ExtendedConn return c.ExtendedConn
} }
@@ -253,10 +246,6 @@ func (c *ServerPacketAddrConn) WritePacket(buffer *buf.Buffer, destination M.Soc
return c.ExtendedConn.WriteBuffer(buffer) return c.ExtendedConn.WriteBuffer(buffer)
} }
func (c *ServerPacketAddrConn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *ServerPacketAddrConn) Upstream() any { func (c *ServerPacketAddrConn) Upstream() any {
return c.ExtendedConn return c.ExtendedConn
} }

View File

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

View File

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

View File

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

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

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

View File

@@ -1,24 +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 #### 1.3-beta6
* Fix WireGuard reconnect * Fix WireGuard reconnect

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

12
go.mod
View File

@@ -25,12 +25,12 @@ require (
github.com/sagernet/gomobile v0.0.0-20230413023804-244d7ff07035 github.com/sagernet/gomobile v0.0.0-20230413023804-244d7ff07035
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
github.com/sagernet/sing v0.2.4-0.20230418095640-3b5e6c1812d3 github.com/sagernet/sing v0.2.3-0.20230413105021-520dd58fb01d
github.com/sagernet/sing-dns v0.1.5-0.20230418025317-8a132998b322 github.com/sagernet/sing-dns v0.1.5-0.20230408004833-5adaf486d440
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d github.com/sagernet/sing-shadowsocks v0.2.1-0.20230412123110-1a7c32b4e2e7
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b github.com/sagernet/sing-shadowtls v0.1.1-0.20230409094821-9abef019436f
github.com/sagernet/sing-tun v0.1.4-0.20230419061614-d744d03d9302 github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 github.com/sagernet/sing-vmess v0.1.4-0.20230412122845-9470e68f5e45
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2

24
go.sum
View File

@@ -111,18 +111,18 @@ github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byL
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
github.com/sagernet/sing v0.2.4-0.20230418095640-3b5e6c1812d3 h1:dkH6SEs3yZlUjSXUAn64LUlFAfAgJqiThaWiBKWJ0Q0= github.com/sagernet/sing v0.2.3-0.20230413105021-520dd58fb01d h1:+hp4necjzOvjg9dQwqmTfH/jAgCCnguVNQzIRUOpO00=
github.com/sagernet/sing v0.2.4-0.20230418095640-3b5e6c1812d3/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= github.com/sagernet/sing v0.2.3-0.20230413105021-520dd58fb01d/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
github.com/sagernet/sing-dns v0.1.5-0.20230418025317-8a132998b322 h1:UDSeJZ2xB3dj1lySnM5LpF48dGlphGstw2BqtkJwcZI= 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.20230418025317-8a132998b322/go.mod h1:2wjxSr1Gbecq9A0ESA9cnR399tQTcpCZEOGytekb+qI= github.com/sagernet/sing-dns v0.1.5-0.20230408004833-5adaf486d440/go.mod h1:69PNSHyEmXdjf6C+bXBOdr2GQnPeEyWjIzo/MV8gmz8=
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d h1:UUxtLujzp5jmtOXqXpSOGvHwHSZcBveKVDzRJ4GlnFU= github.com/sagernet/sing-shadowsocks v0.2.1-0.20230412123110-1a7c32b4e2e7 h1:3WDMIF1aE/twc5gJ+9PF2ZJqUxwZ80MPtNBKE3yBevU=
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d/go.mod h1:Co3PJXcaZoLwHGBfT0rbSnn9C7ywc41zVYWtDeoeI/Q= github.com/sagernet/sing-shadowsocks v0.2.1-0.20230412123110-1a7c32b4e2e7/go.mod h1:WoVjGUvRqsx5yhYeDAB5CijCHpNDi0LUPHl3cf7u8Lc=
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b h1:ouW/6IDCrxkBe19YSbdCd7buHix7b+UZ6BM4Zz74XF4= github.com/sagernet/sing-shadowtls v0.1.1-0.20230409094821-9abef019436f h1:qzQvpcDm60zPW8UlZa8UEaBoFORFeGAnhDncPc3VWT4=
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b/go.mod h1:oG8bPerYI6cZ74KquY3DvA7ynECyrILPBnce6wtBqeI= github.com/sagernet/sing-shadowtls v0.1.1-0.20230409094821-9abef019436f/go.mod h1:MxB+Q9H0pAHcrlvNmwSs1crljRwHFFVhtXyOMBy44Nw=
github.com/sagernet/sing-tun v0.1.4-0.20230419061614-d744d03d9302 h1:aPb0T2HQRTG2t7fEwLvFLZSXmhmnBh+SMs2NufhmrsI= github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab h1:a9oeWuPBuIZ70qMhIIH6RrYhp886xN9jJIwsuu4ZFUo=
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/go.mod h1:4YxIDEkkCjGXDOTMPw1SXpLmCQUFAWuaQN250oo+928=
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 h1:BHOnxrbC929JonuKqFdJ7ZbDp7zs4oTlH5KFvKtWu9U= github.com/sagernet/sing-vmess v0.1.4-0.20230412122845-9470e68f5e45 h1:QqYhWah3u+o2tvLRuTfEu3BwsGpf/wNnVK/VNQV2YBM=
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3/go.mod h1:yKrAr+dqZd64DxBXCHWrYicp+n4qbqO73mtwv3dck8U= github.com/sagernet/sing-vmess v0.1.4-0.20230412122845-9470e68f5e45/go.mod h1:eULig3LgaeNiWSquSlzXF42Joypsj3fO1W+Qy93o6hk=
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as= github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as=
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37/go.mod h1:3skNSftZDJWTGVtVaM2jfbce8qHnmH/AGDRe62iNOg0= github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37/go.mod h1:3skNSftZDJWTGVtVaM2jfbce8qHnmH/AGDRe62iNOg0=
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE= github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE=

View File

@@ -53,7 +53,7 @@ func NewDirect(ctx context.Context, router adapter.Router, logger log.ContextLog
} else { } else {
udpTimeout = int64(C.UDPTimeout.Seconds()) 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.connHandler = inbound
inbound.packetHandler = inbound inbound.packetHandler = inbound
inbound.packetUpstream = inbound.udpNat inbound.packetUpstream = inbound.udpNat

View File

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

View File

@@ -64,11 +64,11 @@ func newShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
var err error var err error
switch { switch {
case options.Method == shadowsocks.MethodNone: 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): 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): 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: default:
err = E.New("unsupported method: ", options.Method) err = E.New("unsupported method: ", options.Method)
} }

View File

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

View File

@@ -50,7 +50,6 @@ func newShadowsocksRelay(ctx context.Context, router adapter.Router, logger log.
udpTimeout = int64(C.UDPTimeout.Seconds()) udpTimeout = int64(C.UDPTimeout.Seconds())
} }
service, err := shadowaead_2022.NewRelayServiceWithPassword[int]( service, err := shadowaead_2022.NewRelayServiceWithPassword[int](
ctx,
options.Method, options.Method,
options.Password, options.Password,
udpTimeout, udpTimeout,

View File

@@ -45,7 +45,7 @@ func NewTProxy(ctx context.Context, router adapter.Router, logger log.ContextLog
} }
tproxy.connHandler = tproxy tproxy.connHandler = tproxy
tproxy.oobPacketHandler = 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 tproxy.packetUpstream = tproxy.udpNat
return tproxy return tproxy
} }

View File

@@ -152,7 +152,7 @@ func (t *Tun) Start() error {
) )
t.logger.Trace("opening interface") t.logger.Trace("opening interface")
if t.platformInterface != nil { if t.platformInterface != nil {
tunInterface, err = t.platformInterface.OpenTun(&t.tunOptions, t.platformOptions) tunInterface, err = t.platformInterface.OpenTun(t.tunOptions, t.platformOptions)
} else { } else {
tunInterface, err = tun.New(t.tunOptions) tunInterface, err = tun.New(t.tunOptions)
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -7,20 +7,20 @@ require github.com/sagernet/sing-box v0.0.0
replace github.com/sagernet/sing-box => ../ replace github.com/sagernet/sing-box => ../
require ( require (
github.com/docker/docker v20.10.18+incompatible github.com/docker/docker v23.0.3+incompatible
github.com/docker/go-connections v0.4.0 github.com/docker/go-connections v0.4.0
github.com/gofrs/uuid v4.4.0+incompatible github.com/gofrs/uuid/v5 v5.0.0
github.com/sagernet/sing v0.2.4-0.20230418025125-f196b4303e31 github.com/sagernet/sing v0.2.2
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d github.com/sagernet/sing-shadowsocks v0.2.1-0.20230408141421-e40d6a4e42d4
github.com/spyzhov/ajson v0.7.1 github.com/spyzhov/ajson v0.8.0
github.com/stretchr/testify v1.8.2 github.com/stretchr/testify v1.8.2
go.uber.org/goleak v1.2.0 go.uber.org/goleak v1.2.1
golang.org/x/net v0.9.0 golang.org/x/net v0.9.0
) )
require ( require (
berty.tech/go-libtor v1.0.385 // indirect berty.tech/go-libtor v1.0.385 // indirect
github.com/Dreamacro/clash v1.15.0 // indirect github.com/Dreamacro/clash v1.14.0 // indirect
github.com/Microsoft/go-winio v0.5.1 // indirect github.com/Microsoft/go-winio v0.5.1 // indirect
github.com/ajg/form v1.5.1 // indirect github.com/ajg/form v1.5.1 // indirect
github.com/andybalholm/brotli v1.0.5 // indirect github.com/andybalholm/brotli v1.0.5 // indirect
@@ -36,7 +36,6 @@ require (
github.com/go-chi/cors v1.2.1 // indirect github.com/go-chi/cors v1.2.1 // indirect
github.com/go-chi/render v1.0.2 // indirect github.com/go-chi/render v1.0.2 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // 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/gogo/protobuf v1.3.2 // indirect
github.com/golang/mock v1.6.0 // indirect github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect github.com/golang/protobuf v1.5.2 // indirect
@@ -71,16 +70,15 @@ require (
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 // indirect github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 // indirect
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect
github.com/sagernet/sing-dns v0.1.5-0.20230418025317-8a132998b322 // indirect github.com/sagernet/sing-dns v0.1.5-0.20230408004833-5adaf486d440 // indirect
github.com/sagernet/sing-shadowtls v0.1.0 // indirect github.com/sagernet/sing-shadowtls v0.1.1-0.20230408141548-81d74d2a8661 // 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/sing-vmess v0.1.4-0.20230409073451-6921c3dd77c7 // indirect
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 // indirect github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 // indirect
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 // indirect github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 // indirect
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e // indirect github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e // indirect
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c // indirect github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
go.etcd.io/bbolt v1.3.7 // indirect go.etcd.io/bbolt v1.3.7 // indirect

View File

@@ -1,8 +1,8 @@
berty.tech/go-libtor v1.0.385 h1:RWK94C3hZj6Z2GdvePpHJLnWYobFr3bY/OdUJ5aoEXw= berty.tech/go-libtor v1.0.385 h1:RWK94C3hZj6Z2GdvePpHJLnWYobFr3bY/OdUJ5aoEXw=
berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+fJEw= berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+fJEw=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Dreamacro/clash v1.15.0 h1:mlpD950VEggXZBNahV66hyKDRxcczkj3vymoAt78KyE= github.com/Dreamacro/clash v1.14.0 h1:ehJ/C/1m9LEjmME72WSE/Y2YqbR3Q54AbjqiRCvtyW4=
github.com/Dreamacro/clash v1.15.0/go.mod h1:WNH69bN11LiAdgdSr4hpkEuXVMfBbWyhEKMCTx9BtNE= github.com/Dreamacro/clash v1.14.0/go.mod h1:ia2CU7V713H1QdCqMwOLK9U9V5Ay8X0voj3yQr2tk+I=
github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= 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/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
@@ -25,8 +25,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v20.10.18+incompatible h1:SN84VYXTBNGn92T/QwIRPlum9zfemfitN7pbsp26WSc= github.com/docker/docker v23.0.3+incompatible h1:9GhVsShNWz1hO//9BNg/dpMnZW25KydO4wtVxWAIbho=
github.com/docker/docker v20.10.18+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v23.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
@@ -43,8 +43,6 @@ 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-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 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= 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 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= 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 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
@@ -128,18 +126,18 @@ github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byL
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
github.com/sagernet/sing v0.2.4-0.20230418025125-f196b4303e31 h1:qgq8jeY/rbnY9NwYXByO//AP0ByIxnsKUxQx1tOB3W0= github.com/sagernet/sing v0.2.2 h1:qfEdSLuwFgIIkeLOcwVQkVDzKLHtclXb93Ql0zZA+aE=
github.com/sagernet/sing v0.2.4-0.20230418025125-f196b4303e31/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= github.com/sagernet/sing v0.2.2/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
github.com/sagernet/sing-dns v0.1.5-0.20230418025317-8a132998b322 h1:UDSeJZ2xB3dj1lySnM5LpF48dGlphGstw2BqtkJwcZI= 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.20230418025317-8a132998b322/go.mod h1:2wjxSr1Gbecq9A0ESA9cnR399tQTcpCZEOGytekb+qI= github.com/sagernet/sing-dns v0.1.5-0.20230408004833-5adaf486d440/go.mod h1:69PNSHyEmXdjf6C+bXBOdr2GQnPeEyWjIzo/MV8gmz8=
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d h1:UUxtLujzp5jmtOXqXpSOGvHwHSZcBveKVDzRJ4GlnFU= github.com/sagernet/sing-shadowsocks v0.2.1-0.20230408141421-e40d6a4e42d4 h1:mKpXBBnAhTy9/CvDKqt5cN78LKX8cVUqjoWEXI/g0No=
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d/go.mod h1:Co3PJXcaZoLwHGBfT0rbSnn9C7ywc41zVYWtDeoeI/Q= github.com/sagernet/sing-shadowsocks v0.2.1-0.20230408141421-e40d6a4e42d4/go.mod h1:NFEROpOEiLG+lEoSPNpSL2yDyXx6q0OJvwWnEAd40cI=
github.com/sagernet/sing-shadowtls v0.1.0 h1:05MYce8aR5xfKIn+y7xRFsdKhKt44QZTSEQW+lG5IWQ= github.com/sagernet/sing-shadowtls v0.1.1-0.20230408141548-81d74d2a8661 h1:QnV79JbJbJGT0MJJfd8o7QMEfRu3eUVKsmahxFMonrc=
github.com/sagernet/sing-shadowtls v0.1.0/go.mod h1:Kn1VUIprdkwCgkS6SXYaLmIpKzQbqBIKJBMY+RvBhYc= 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.20230419061614-d744d03d9302 h1:aPb0T2HQRTG2t7fEwLvFLZSXmhmnBh+SMs2NufhmrsI= github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab h1:a9oeWuPBuIZ70qMhIIH6RrYhp886xN9jJIwsuu4ZFUo=
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/go.mod h1:4YxIDEkkCjGXDOTMPw1SXpLmCQUFAWuaQN250oo+928=
github.com/sagernet/sing-vmess v0.1.3 h1:q/+tsF46dvvapL6CpQBgPHJ6nQrDUZqEtLHCbsjO7iM= github.com/sagernet/sing-vmess v0.1.4-0.20230409073451-6921c3dd77c7 h1:ZArINfN+zcHMdZCeRFOm4rO3SWyvYuLg3VhWcA5zonc=
github.com/sagernet/sing-vmess v0.1.3/go.mod h1:GVXqAHwe9U21uS+Voh4YBIrADQyE4F9v0ayGSixSQAE= github.com/sagernet/sing-vmess v0.1.4-0.20230409073451-6921c3dd77c7/go.mod h1:A9iGfwYmAEmVg8bavkqQ7Hx7nr9Pc2oWMYDwbLIBDjY=
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as= github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as=
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37/go.mod h1:3skNSftZDJWTGVtVaM2jfbce8qHnmH/AGDRe62iNOg0= github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37/go.mod h1:3skNSftZDJWTGVtVaM2jfbce8qHnmH/AGDRe62iNOg0=
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE= github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE=
@@ -151,10 +149,8 @@ github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e/go.mod h1:45TUl
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo= github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo=
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI= github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/spyzhov/ajson v0.8.0 h1:sFXyMbi4Y/BKjrsfkUZHSjA2JM1184enheSjjoT/zCc=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spyzhov/ajson v0.8.0/go.mod h1:63V+CGM6f1Bu/p4nLIN8885ojBdt88TbLoSFzyqMuVA=
github.com/spyzhov/ajson v0.7.1 h1:1MDIlPc6x0zjNtpa7tDzRAyFAvRX+X8ZsvtYz5lZg6A=
github.com/spyzhov/ajson v0.7.1/go.mod h1:63V+CGM6f1Bu/p4nLIN8885ojBdt88TbLoSFzyqMuVA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@@ -178,8 +174,8 @@ go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
@@ -195,7 +191,6 @@ golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -230,7 +225,6 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View File

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

View File

@@ -1,59 +0,0 @@
package main
import (
"context"
"net"
"testing"
"time"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/udpnat"
)
func TestUDPNatClose(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
connCtx, connCancel := common.ContextWithCancelCause(context.Background())
defer connCancel(net.ErrClosed)
service := udpnat.New[int](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
}

View File

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

View File

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

View File

@@ -4,6 +4,8 @@ import (
"context" "context"
"net" "net"
"github.com/sagernet/sing/common/bufio"
"github.com/sagernet/sing/common/bufio/deadline"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/common/rw" "github.com/sagernet/sing/common/rw"
@@ -53,7 +55,7 @@ func newMuxConnection0(ctx context.Context, stream net.Conn, metadata M.Metadata
case CommandTCP: case CommandTCP:
return handler.NewConnection(ctx, stream, metadata) return handler.NewConnection(ctx, stream, metadata)
case CommandUDP: case CommandUDP:
return handler.NewPacketConnection(ctx, &PacketConn{stream}, metadata) return handler.NewPacketConnection(ctx, deadline.NewPacketConn(bufio.NewNetPacketConn(&PacketConn{stream})), metadata)
default: default:
return E.New("unknown command ", command) return E.New("unknown command ", command)
} }

View File

@@ -8,6 +8,7 @@ import (
"github.com/sagernet/sing/common/auth" "github.com/sagernet/sing/common/auth"
"github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio" "github.com/sagernet/sing/common/bufio"
"github.com/sagernet/sing/common/bufio/deadline"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
@@ -106,7 +107,7 @@ func (s *Service[K]) NewConnection(ctx context.Context, conn net.Conn, metadata
case CommandTCP: case CommandTCP:
return s.handler.NewConnection(ctx, conn, metadata) return s.handler.NewConnection(ctx, conn, metadata)
case CommandUDP: case CommandUDP:
return s.handler.NewPacketConnection(ctx, &PacketConn{conn}, metadata) return s.handler.NewPacketConnection(ctx, deadline.NewPacketConn(bufio.NewNetPacketConn(&PacketConn{conn})), metadata)
// case CommandMux: // case CommandMux:
default: default:
return HandleMuxConnection(ctx, conn, metadata, s.handler) return HandleMuxConnection(ctx, conn, metadata, s.handler)
@@ -136,11 +137,3 @@ func (c *PacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) er
func (c *PacketConn) FrontHeadroom() int { func (c *PacketConn) FrontHeadroom() int {
return M.MaxSocksaddrLength + 4 return M.MaxSocksaddrLength + 4
} }
func (c *PacketConn) NeedAdditionalReadDeadline() bool {
return true
}
func (c *PacketConn) Upstream() any {
return c.Conn
}

View File

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

View File

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

View File

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

View File

@@ -12,6 +12,7 @@ import (
"github.com/sagernet/sing-box/common/tls" "github.com/sagernet/sing-box/common/tls"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/transport/v2rayhttp" "github.com/sagernet/sing-box/transport/v2rayhttp"
"github.com/sagernet/sing/common/bufio/deadline"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
@@ -92,7 +93,7 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
conn.setup(nil, err) conn.setup(nil, err)
} }
}() }()
return conn, nil return deadline.NewConn(conn), nil
} }
func (c *Client) Close() error { func (c *Client) Close() error {

View File

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

View File

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

View File

@@ -182,21 +182,32 @@ func (c *HTTP2Conn) RemoteAddr() net.Addr {
} }
func (c *HTTP2Conn) SetDeadline(t time.Time) error { func (c *HTTP2Conn) SetDeadline(t time.Time) error {
if responseWriter, loaded := c.writer.(interface {
SetWriteDeadline(time.Time) error
}); loaded {
return responseWriter.SetWriteDeadline(t)
}
return os.ErrInvalid return os.ErrInvalid
} }
func (c *HTTP2Conn) SetReadDeadline(t time.Time) error { func (c *HTTP2Conn) SetReadDeadline(t time.Time) error {
if responseWriter, loaded := c.writer.(interface {
SetReadDeadline(time.Time) error
}); loaded {
return responseWriter.SetReadDeadline(t)
}
return os.ErrInvalid return os.ErrInvalid
} }
func (c *HTTP2Conn) SetWriteDeadline(t time.Time) error { func (c *HTTP2Conn) SetWriteDeadline(t time.Time) error {
if responseWriter, loaded := c.writer.(interface {
SetWriteDeadline(time.Time) error
}); loaded {
return responseWriter.SetWriteDeadline(t)
}
return os.ErrInvalid return os.ErrInvalid
} }
func (c *HTTP2Conn) NeedAdditionalReadDeadline() bool {
return true
}
type ServerHTTPConn struct { type ServerHTTPConn struct {
HTTP2Conn HTTP2Conn
flusher http.Flusher flusher http.Flusher

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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