mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-12 10:07:20 +10:00
Compare commits
48 Commits
v1.3-beta5
...
v1.3-beta8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbb01555c8 | ||
|
|
3c217fea63 | ||
|
|
a02c676d33 | ||
|
|
c2ae261c8d | ||
|
|
1fe69cc08c | ||
|
|
dacb885930 | ||
|
|
31682c313b | ||
|
|
6fd73131cb | ||
|
|
f3ff76abcc | ||
|
|
d9757bfaff | ||
|
|
9d97ed6220 | ||
|
|
054211d091 | ||
|
|
69b92810d6 | ||
|
|
ea23267ced | ||
|
|
bbe2b1ab09 | ||
|
|
d3530a54fb | ||
|
|
c238df9309 | ||
|
|
a223262678 | ||
|
|
ec13965fd0 | ||
|
|
ddf747006e | ||
|
|
4382093868 | ||
|
|
a5322850b3 | ||
|
|
407b08975c | ||
|
|
c7067ff5e8 | ||
|
|
9b2384b296 | ||
|
|
b498a22972 | ||
|
|
20e9da5c67 | ||
|
|
ec8974673b | ||
|
|
5e6e7923e4 | ||
|
|
de1b5971e1 | ||
|
|
5c20d0b4d5 | ||
|
|
9df96ac7f1 | ||
|
|
87cd925144 | ||
|
|
fecb796000 | ||
|
|
cfb6c804aa | ||
|
|
11c50c7558 | ||
|
|
34cc7f176e | ||
|
|
b54da9c6af | ||
|
|
f44f86b832 | ||
|
|
4ebf40f582 | ||
|
|
53e4302143 | ||
|
|
cf778eda4f | ||
|
|
bb63429079 | ||
|
|
f7f9a7ae20 | ||
|
|
8699412a4c | ||
|
|
0d7aa19cd1 | ||
|
|
50a7295360 | ||
|
|
e57b6ae98d |
@@ -8,6 +8,10 @@ The universal proxy platform.
|
||||
|
||||
https://sing-box.sagernet.org
|
||||
|
||||
## Support
|
||||
|
||||
https://community.sagernet.org/c/sing-box/
|
||||
|
||||
## License
|
||||
|
||||
```
|
||||
|
||||
@@ -37,6 +37,7 @@ type Router interface {
|
||||
LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error)
|
||||
|
||||
InterfaceFinder() control.InterfaceFinder
|
||||
UpdateInterfaces() error
|
||||
DefaultInterface() string
|
||||
AutoDetectInterface() bool
|
||||
AutoDetectInterfaceFunc() control.Func
|
||||
|
||||
6
box.go
6
box.go
@@ -133,6 +133,12 @@ func New(options Options) (*Box, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if options.PlatformInterface != nil {
|
||||
err = options.PlatformInterface.Initialize(ctx, router)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "initialize platform interface")
|
||||
}
|
||||
}
|
||||
preServices := make(map[string]adapter.Service)
|
||||
postServices := make(map[string]adapter.Service)
|
||||
if needClashAPI {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build go1.19 && !go1.20
|
||||
//go:build go1.20 && !go1.21
|
||||
|
||||
package badtls
|
||||
|
||||
@@ -14,39 +14,60 @@ import (
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
aTLS "github.com/sagernet/sing/common/tls"
|
||||
)
|
||||
|
||||
type Conn struct {
|
||||
*tls.Conn
|
||||
writer N.ExtendedWriter
|
||||
activeCall *int32
|
||||
closeNotifySent *bool
|
||||
version *uint16
|
||||
rand io.Reader
|
||||
halfAccess *sync.Mutex
|
||||
halfError *error
|
||||
cipher cipher.AEAD
|
||||
explicitNonceLen int
|
||||
halfPtr uintptr
|
||||
halfSeq []byte
|
||||
halfScratchBuf []byte
|
||||
writer N.ExtendedWriter
|
||||
isHandshakeComplete *atomic.Bool
|
||||
activeCall *atomic.Int32
|
||||
closeNotifySent *bool
|
||||
version *uint16
|
||||
rand io.Reader
|
||||
halfAccess *sync.Mutex
|
||||
halfError *error
|
||||
cipher cipher.AEAD
|
||||
explicitNonceLen int
|
||||
halfPtr uintptr
|
||||
halfSeq []byte
|
||||
halfScratchBuf []byte
|
||||
}
|
||||
|
||||
func Create(conn *tls.Conn) (TLSConn, error) {
|
||||
if !handshakeComplete(conn) {
|
||||
func TryCreate(conn aTLS.Conn) aTLS.Conn {
|
||||
tlsConn, ok := conn.(*tls.Conn)
|
||||
if !ok {
|
||||
return conn
|
||||
}
|
||||
badConn, err := Create(tlsConn)
|
||||
if err != nil {
|
||||
log.Warn("initialize badtls: ", err)
|
||||
return conn
|
||||
}
|
||||
return badConn
|
||||
}
|
||||
|
||||
func Create(conn *tls.Conn) (aTLS.Conn, error) {
|
||||
rawConn := reflect.Indirect(reflect.ValueOf(conn))
|
||||
rawIsHandshakeComplete := rawConn.FieldByName("isHandshakeComplete")
|
||||
if !rawIsHandshakeComplete.IsValid() || rawIsHandshakeComplete.Kind() != reflect.Struct {
|
||||
return nil, E.New("badtls: invalid isHandshakeComplete")
|
||||
}
|
||||
isHandshakeComplete := (*atomic.Bool)(unsafe.Pointer(rawIsHandshakeComplete.UnsafeAddr()))
|
||||
if !isHandshakeComplete.Load() {
|
||||
return nil, E.New("handshake not finished")
|
||||
}
|
||||
rawConn := reflect.Indirect(reflect.ValueOf(conn))
|
||||
rawActiveCall := rawConn.FieldByName("activeCall")
|
||||
if !rawActiveCall.IsValid() || rawActiveCall.Kind() != reflect.Int32 {
|
||||
if !rawActiveCall.IsValid() || rawActiveCall.Kind() != reflect.Struct {
|
||||
return nil, E.New("badtls: invalid active call")
|
||||
}
|
||||
activeCall := (*int32)(unsafe.Pointer(rawActiveCall.UnsafeAddr()))
|
||||
activeCall := (*atomic.Int32)(unsafe.Pointer(rawActiveCall.UnsafeAddr()))
|
||||
rawHalfConn := rawConn.FieldByName("out")
|
||||
if !rawHalfConn.IsValid() || rawHalfConn.Kind() != reflect.Struct {
|
||||
return nil, E.New("badtls: invalid half conn")
|
||||
@@ -108,19 +129,20 @@ func Create(conn *tls.Conn) (TLSConn, error) {
|
||||
}
|
||||
halfScratchBuf := rawHalfScratchBuf.Bytes()
|
||||
return &Conn{
|
||||
Conn: conn,
|
||||
writer: bufio.NewExtendedWriter(conn.NetConn()),
|
||||
activeCall: activeCall,
|
||||
closeNotifySent: closeNotifySent,
|
||||
version: version,
|
||||
halfAccess: halfAccess,
|
||||
halfError: halfError,
|
||||
cipher: aeadCipher,
|
||||
explicitNonceLen: explicitNonceLen,
|
||||
rand: randReader,
|
||||
halfPtr: rawHalfConn.UnsafeAddr(),
|
||||
halfSeq: halfSeq,
|
||||
halfScratchBuf: halfScratchBuf,
|
||||
Conn: conn,
|
||||
writer: bufio.NewExtendedWriter(conn.NetConn()),
|
||||
isHandshakeComplete: isHandshakeComplete,
|
||||
activeCall: activeCall,
|
||||
closeNotifySent: closeNotifySent,
|
||||
version: version,
|
||||
halfAccess: halfAccess,
|
||||
halfError: halfError,
|
||||
cipher: aeadCipher,
|
||||
explicitNonceLen: explicitNonceLen,
|
||||
rand: randReader,
|
||||
halfPtr: rawHalfConn.UnsafeAddr(),
|
||||
halfSeq: halfSeq,
|
||||
halfScratchBuf: halfScratchBuf,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -130,15 +152,15 @@ func (c *Conn) WriteBuffer(buffer *buf.Buffer) error {
|
||||
return common.Error(c.Write(buffer.Bytes()))
|
||||
}
|
||||
for {
|
||||
x := atomic.LoadInt32(c.activeCall)
|
||||
x := c.activeCall.Load()
|
||||
if x&1 != 0 {
|
||||
return net.ErrClosed
|
||||
}
|
||||
if atomic.CompareAndSwapInt32(c.activeCall, x, x+2) {
|
||||
if c.activeCall.CompareAndSwap(x, x+2) {
|
||||
break
|
||||
}
|
||||
}
|
||||
defer atomic.AddInt32(c.activeCall, -2)
|
||||
defer c.activeCall.Add(-2)
|
||||
c.halfAccess.Lock()
|
||||
defer c.halfAccess.Unlock()
|
||||
if err := *c.halfError; err != nil {
|
||||
@@ -186,6 +208,7 @@ func (c *Conn) WriteBuffer(buffer *buf.Buffer) error {
|
||||
binary.BigEndian.PutUint16(outBuf[3:], uint16(dataLen+c.explicitNonceLen+c.cipher.Overhead()))
|
||||
}
|
||||
incSeq(c.halfPtr)
|
||||
log.Trace("badtls write ", buffer.Len())
|
||||
return c.writer.WriteBuffer(buffer)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build !go1.19 || go1.20
|
||||
//go:build !go1.19 || go1.21
|
||||
|
||||
package badtls
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package badtls
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
)
|
||||
|
||||
type TLSConn interface {
|
||||
net.Conn
|
||||
HandshakeContext(ctx context.Context) error
|
||||
ConnectionState() tls.ConnectionState
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
//go:build go1.19 && !go.1.20
|
||||
//go:build go1.20 && !go.1.21
|
||||
|
||||
package badtls
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"reflect"
|
||||
_ "unsafe"
|
||||
)
|
||||
@@ -16,9 +15,6 @@ const (
|
||||
//go:linkname errShutdown crypto/tls.errShutdown
|
||||
var errShutdown error
|
||||
|
||||
//go:linkname handshakeComplete crypto/tls.(*Conn).handshakeComplete
|
||||
func handshakeComplete(conn *tls.Conn) bool
|
||||
|
||||
//go:linkname incSeq crypto/tls.(*halfConn).incSeq
|
||||
func incSeq(conn uintptr)
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer/conntrack"
|
||||
"github.com/sagernet/sing-box/common/warning"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
@@ -17,41 +16,6 @@ import (
|
||||
"github.com/sagernet/tfo-go"
|
||||
)
|
||||
|
||||
var warnBindInterfaceOnUnsupportedPlatform = warning.New(
|
||||
func() bool {
|
||||
return !(C.IsLinux || C.IsWindows || C.IsDarwin)
|
||||
},
|
||||
"outbound option `bind_interface` is only supported on Linux and Windows",
|
||||
)
|
||||
|
||||
var warnRoutingMarkOnUnsupportedPlatform = warning.New(
|
||||
func() bool {
|
||||
return !C.IsLinux
|
||||
},
|
||||
"outbound option `routing_mark` is only supported on Linux",
|
||||
)
|
||||
|
||||
var warnReuseAdderOnUnsupportedPlatform = warning.New(
|
||||
func() bool {
|
||||
return !(C.IsDarwin || C.IsDragonfly || C.IsFreebsd || C.IsLinux || C.IsNetbsd || C.IsOpenbsd || C.IsSolaris || C.IsWindows)
|
||||
},
|
||||
"outbound option `reuse_addr` is unsupported on current platform",
|
||||
)
|
||||
|
||||
var warnProtectPathOnNonAndroid = warning.New(
|
||||
func() bool {
|
||||
return !C.IsAndroid
|
||||
},
|
||||
"outbound option `protect_path` is only supported on Android",
|
||||
)
|
||||
|
||||
var warnTFOOnUnsupportedPlatform = warning.New(
|
||||
func() bool {
|
||||
return !(C.IsDarwin || C.IsFreebsd || C.IsLinux || C.IsWindows)
|
||||
},
|
||||
"outbound option `tcp_fast_open` is unsupported on current platform",
|
||||
)
|
||||
|
||||
type DefaultDialer struct {
|
||||
dialer4 tfo.Dialer
|
||||
dialer6 tfo.Dialer
|
||||
@@ -66,7 +30,6 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
|
||||
var dialer net.Dialer
|
||||
var listener net.ListenConfig
|
||||
if options.BindInterface != "" {
|
||||
warnBindInterfaceOnUnsupportedPlatform.Check()
|
||||
bindFunc := control.BindToInterface(router.InterfaceFinder(), options.BindInterface, -1)
|
||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||
listener.Control = control.Append(listener.Control, bindFunc)
|
||||
@@ -80,7 +43,6 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
|
||||
listener.Control = control.Append(listener.Control, bindFunc)
|
||||
}
|
||||
if options.RoutingMark != 0 {
|
||||
warnRoutingMarkOnUnsupportedPlatform.Check()
|
||||
dialer.Control = control.Append(dialer.Control, control.RoutingMark(options.RoutingMark))
|
||||
listener.Control = control.Append(listener.Control, control.RoutingMark(options.RoutingMark))
|
||||
} else if router.DefaultMark() != 0 {
|
||||
@@ -88,11 +50,9 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
|
||||
listener.Control = control.Append(listener.Control, control.RoutingMark(router.DefaultMark()))
|
||||
}
|
||||
if options.ReuseAddr {
|
||||
warnReuseAdderOnUnsupportedPlatform.Check()
|
||||
listener.Control = control.Append(listener.Control, control.ReuseAddr())
|
||||
}
|
||||
if options.ProtectPath != "" {
|
||||
warnProtectPathOnNonAndroid.Check()
|
||||
dialer.Control = control.Append(dialer.Control, control.ProtectPath(options.ProtectPath))
|
||||
listener.Control = control.Append(listener.Control, control.ProtectPath(options.ProtectPath))
|
||||
}
|
||||
@@ -101,9 +61,6 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
|
||||
} else {
|
||||
dialer.Timeout = C.TCPTimeout
|
||||
}
|
||||
if options.TCPFastOpen {
|
||||
warnTFOOnUnsupportedPlatform.Check()
|
||||
}
|
||||
var udpFragment bool
|
||||
if options.UDPFragment != nil {
|
||||
udpFragment = *options.UDPFragment
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
"github.com/sagernet/sing/common/bufio/deadline"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
@@ -69,7 +68,7 @@ func (c *Client) DialContext(ctx context.Context, network string, destination M.
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bufio.NewBindPacketConn(deadline.NewPacketConn(bufio.NewNetPacketConn(&ClientPacketConn{ExtendedConn: bufio.NewExtendedConn(stream), destination: destination})), destination), nil
|
||||
return bufio.NewUnbindPacketConn(&ClientPacketConn{ExtendedConn: bufio.NewExtendedConn(stream), destination: destination}), nil
|
||||
default:
|
||||
return nil, E.Extend(N.ErrUnknownNetwork, network)
|
||||
}
|
||||
@@ -80,7 +79,7 @@ func (c *Client) ListenPacket(ctx context.Context, destination M.Socksaddr) (net
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return deadline.NewPacketConn(&ClientPacketAddrConn{ExtendedConn: bufio.NewExtendedConn(stream), destination: destination}), nil
|
||||
return &ClientPacketAddrConn{ExtendedConn: bufio.NewExtendedConn(stream), destination: destination}, nil
|
||||
}
|
||||
|
||||
func (c *Client) openStream() (net.Conn, error) {
|
||||
@@ -250,6 +249,10 @@ func (c *ClientConn) WriterReplaceable() bool {
|
||||
return c.requestWrite
|
||||
}
|
||||
|
||||
func (c *ClientConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *ClientConn) Upstream() any {
|
||||
return c.Conn
|
||||
}
|
||||
@@ -378,6 +381,10 @@ func (c *ClientPacketConn) RemoteAddr() net.Addr {
|
||||
return c.destination.UDPAddr()
|
||||
}
|
||||
|
||||
func (c *ClientPacketConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *ClientPacketConn) Upstream() any {
|
||||
return c.ExtendedConn
|
||||
}
|
||||
@@ -519,6 +526,10 @@ func (c *ClientPacketAddrConn) FrontHeadroom() int {
|
||||
return 2 + M.MaxSocksaddrLength
|
||||
}
|
||||
|
||||
func (c *ClientPacketAddrConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *ClientPacketAddrConn) Upstream() any {
|
||||
return c.ExtendedConn
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
"github.com/sagernet/sing/common/bufio/deadline"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
@@ -68,7 +67,7 @@ func newConnection(ctx context.Context, router adapter.Router, errorHandler E.Ha
|
||||
logger.InfoContext(ctx, "inbound multiplex packet connection")
|
||||
packetConn = &ServerPacketAddrConn{ExtendedConn: bufio.NewExtendedConn(stream)}
|
||||
}
|
||||
hErr := router.RoutePacketConnection(ctx, deadline.NewPacketConn(bufio.NewNetPacketConn(packetConn)), metadata)
|
||||
hErr := router.RoutePacketConnection(ctx, packetConn, metadata)
|
||||
stream.Close()
|
||||
if hErr != nil {
|
||||
errorHandler.NewError(ctx, hErr)
|
||||
@@ -132,6 +131,10 @@ func (c *ServerConn) FrontHeadroom() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *ServerConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *ServerConn) Upstream() any {
|
||||
return c.ExtendedConn
|
||||
}
|
||||
@@ -184,6 +187,10 @@ func (c *ServerPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksad
|
||||
return c.ExtendedConn.WriteBuffer(buffer)
|
||||
}
|
||||
|
||||
func (c *ServerPacketConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *ServerPacketConn) Upstream() any {
|
||||
return c.ExtendedConn
|
||||
}
|
||||
@@ -246,6 +253,10 @@ func (c *ServerPacketAddrConn) WritePacket(buffer *buf.Buffer, destination M.Soc
|
||||
return c.ExtendedConn.WriteBuffer(buffer)
|
||||
}
|
||||
|
||||
func (c *ServerPacketAddrConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *ServerPacketAddrConn) Upstream() any {
|
||||
return c.ExtendedConn
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
"github.com/sagernet/sing/protocol/http"
|
||||
)
|
||||
|
||||
@@ -15,5 +16,5 @@ func HTTPHost(ctx context.Context, reader io.Reader) (*adapter.InboundContext, e
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &adapter.InboundContext{Protocol: C.ProtocolHTTP, Domain: request.Host}, nil
|
||||
return &adapter.InboundContext{Protocol: C.ProtocolHTTP, Domain: M.ParseSocksaddr(request.Host).AddrString()}, nil
|
||||
}
|
||||
|
||||
27
common/sniff/http_test.go
Normal file
27
common/sniff/http_test.go
Normal file
@@ -0,0 +1,27 @@
|
||||
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")
|
||||
}
|
||||
@@ -125,7 +125,7 @@ func (e *RealityClientConfig) ClientHandshake(ctx context.Context, conn net.Conn
|
||||
|
||||
hello.SessionId[0] = 1
|
||||
hello.SessionId[1] = 8
|
||||
hello.SessionId[2] = 0
|
||||
hello.SessionId[2] = 1
|
||||
binary.BigEndian.PutUint32(hello.SessionId[4:], uint32(time.Now().Unix()))
|
||||
copy(hello.SessionId[8:], e.shortID[:])
|
||||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
package warning
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
)
|
||||
|
||||
type Warning struct {
|
||||
logger log.Logger
|
||||
check CheckFunc
|
||||
message string
|
||||
checkOnce sync.Once
|
||||
}
|
||||
|
||||
type CheckFunc = func() bool
|
||||
|
||||
func New(checkFunc CheckFunc, message string) Warning {
|
||||
return Warning{
|
||||
check: checkFunc,
|
||||
message: message,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Warning) Check() {
|
||||
w.checkOnce.Do(func() {
|
||||
if w.check() {
|
||||
log.Warn(w.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,3 +1,30 @@
|
||||
#### 1.3-beta8
|
||||
|
||||
* Fix `system` tun stack for ios
|
||||
* Fix network monitor for android/ios
|
||||
* Update VLESS and XUDP protocol **1**
|
||||
* Fixes and improvements
|
||||
|
||||
*1:
|
||||
|
||||
As in Xray, this is an incompatible update for XUDP in VLESS if vision flow is enabled.
|
||||
|
||||
#### 1.3-beta7
|
||||
|
||||
* Add `path` and `headers` options for HTTP outbound
|
||||
* Add multi-user support for Shadowsocks legacy AEAD inbound
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.2.4
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
#### 1.3-beta6
|
||||
|
||||
* Fix WireGuard reconnect
|
||||
* Perform URLTest recheck after network changes
|
||||
* Fix bugs and update dependencies
|
||||
|
||||
#### 1.3-beta5
|
||||
|
||||
* Add Clash.Meta API compatibility for Clash API
|
||||
|
||||
@@ -12,5 +12,6 @@ Experimental Android client for sing-box.
|
||||
|
||||
#### Note
|
||||
|
||||
* Working directory is at `/sdcard/Android/data/io.nekohasekai.sfa/files` (External files directory)
|
||||
* User Agent is `SFA/$version ($version_code; sing-box $sing_box_version)` in the remote profile request
|
||||
* User Agent in remote profile request is `SFA/$version ($version_code; sing-box $sing_box_version)`
|
||||
* The working directory is located at `/sdcard/Android/data/io.nekohasekai.sfa/files` (External files directory)
|
||||
* Crash logs is located in `$working_directory/stderr.log`
|
||||
|
||||
@@ -12,5 +12,6 @@
|
||||
|
||||
#### 注意事项
|
||||
|
||||
* 工作目录位于 `/sdcard/Android/data/io.nekohasekai.sfa/files` (外部文件目录)
|
||||
* 远程配置文件请求中的 User Agent 为 `SFA/$version ($version_code; sing-box $sing_box_version)`
|
||||
* 工作目录位于 `/sdcard/Android/data/io.nekohasekai.sfa/files` (外部文件目录)
|
||||
* 崩溃日志位于 `$working_directory/stderr.log`
|
||||
|
||||
@@ -13,8 +13,8 @@ Experimental iOS client for sing-box.
|
||||
|
||||
#### Note
|
||||
|
||||
* `system` tun stack not working on iOS
|
||||
* User Agent is `SFI/$version ($version_code; sing-box $sing_box_version)` in the remote profile request
|
||||
* User Agent in remote profile request is `SFA/$version ($version_code; sing-box $sing_box_version)`
|
||||
* Crash logs is located in `Settings` -> `View Service Log`
|
||||
|
||||
#### Privacy policy
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
|
||||
#### 注意事项
|
||||
|
||||
* `system` tun stack 在 iOS 不工作
|
||||
* 远程配置文件请求中的 User Agent 为 `SFI/$version ($version_code; sing-box $sing_box_version)`
|
||||
* 崩溃日志位于 `设置` -> `查看服务日志`
|
||||
|
||||
#### 隐私政策
|
||||
|
||||
|
||||
@@ -29,3 +29,19 @@ func (i *iterator[T]) Next() T {
|
||||
func (i *iterator[T]) HasNext() bool {
|
||||
return len(i.values) > 0
|
||||
}
|
||||
|
||||
type abstractIterator[T any] interface {
|
||||
Next() T
|
||||
HasNext() bool
|
||||
}
|
||||
|
||||
func iteratorToArray[T any](iterator abstractIterator[T]) []T {
|
||||
if iterator == nil {
|
||||
return nil
|
||||
}
|
||||
var values []T
|
||||
for iterator.HasNext() {
|
||||
values = append(values, iterator.Next())
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
183
experimental/libbox/monitor.go
Normal file
183
experimental/libbox/monitor.go
Normal file
@@ -0,0 +1,183 @@
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/netip"
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
)
|
||||
|
||||
var (
|
||||
_ tun.DefaultInterfaceMonitor = (*platformDefaultInterfaceMonitor)(nil)
|
||||
_ InterfaceUpdateListener = (*platformDefaultInterfaceMonitor)(nil)
|
||||
)
|
||||
|
||||
type platformDefaultInterfaceMonitor struct {
|
||||
*platformInterfaceWrapper
|
||||
errorHandler E.Handler
|
||||
networkAddresses []networkAddress
|
||||
defaultInterfaceName string
|
||||
defaultInterfaceIndex int
|
||||
element *list.Element[tun.NetworkUpdateCallback]
|
||||
access sync.Mutex
|
||||
callbacks list.List[tun.DefaultInterfaceUpdateCallback]
|
||||
}
|
||||
|
||||
type networkAddress struct {
|
||||
interfaceName string
|
||||
interfaceIndex int
|
||||
addresses []netip.Prefix
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) Start() error {
|
||||
return m.iif.StartDefaultInterfaceMonitor(m)
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) Close() error {
|
||||
return m.iif.CloseDefaultInterfaceMonitor(m)
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) DefaultInterfaceName(destination netip.Addr) string {
|
||||
for _, address := range m.networkAddresses {
|
||||
for _, prefix := range address.addresses {
|
||||
if prefix.Contains(destination) {
|
||||
return address.interfaceName
|
||||
}
|
||||
}
|
||||
}
|
||||
return m.defaultInterfaceName
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) DefaultInterfaceIndex(destination netip.Addr) int {
|
||||
for _, address := range m.networkAddresses {
|
||||
for _, prefix := range address.addresses {
|
||||
if prefix.Contains(destination) {
|
||||
return address.interfaceIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
return m.defaultInterfaceIndex
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) OverrideAndroidVPN() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) AndroidVPNEnabled() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) RegisterCallback(callback tun.DefaultInterfaceUpdateCallback) *list.Element[tun.DefaultInterfaceUpdateCallback] {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
return m.callbacks.PushBack(callback)
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) UnregisterCallback(element *list.Element[tun.DefaultInterfaceUpdateCallback]) {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
m.callbacks.Remove(element)
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) UpdateDefaultInterface(interfaceName string, interfaceIndex32 int32) {
|
||||
var err error
|
||||
if m.iif.UsePlatformInterfaceGetter() {
|
||||
err = m.updateInterfacesPlatform()
|
||||
} else {
|
||||
err = m.updateInterfaces()
|
||||
}
|
||||
if err == nil {
|
||||
err = m.router.UpdateInterfaces()
|
||||
}
|
||||
if err != nil {
|
||||
m.errorHandler.NewError(context.Background(), E.Cause(err, "update interfaces"))
|
||||
}
|
||||
interfaceIndex := int(interfaceIndex32)
|
||||
if interfaceName == "" {
|
||||
for _, netIf := range m.networkAddresses {
|
||||
if netIf.interfaceIndex == interfaceIndex {
|
||||
interfaceName = netIf.interfaceName
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if interfaceIndex == -1 {
|
||||
for _, netIf := range m.networkAddresses {
|
||||
if netIf.interfaceName == interfaceName {
|
||||
interfaceIndex = netIf.interfaceIndex
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if interfaceName == "" {
|
||||
m.errorHandler.NewError(context.Background(), E.New("invalid interface name for ", interfaceIndex))
|
||||
return
|
||||
} else if interfaceIndex == -1 {
|
||||
m.errorHandler.NewError(context.Background(), E.New("invalid interface index for ", interfaceName))
|
||||
return
|
||||
}
|
||||
if m.defaultInterfaceName == interfaceName && m.defaultInterfaceIndex == interfaceIndex {
|
||||
return
|
||||
}
|
||||
m.defaultInterfaceName = interfaceName
|
||||
m.defaultInterfaceIndex = interfaceIndex
|
||||
m.access.Lock()
|
||||
callbacks := m.callbacks.Array()
|
||||
m.access.Unlock()
|
||||
for _, callback := range callbacks {
|
||||
err = callback(tun.EventInterfaceUpdate)
|
||||
if err != nil {
|
||||
m.errorHandler.NewError(context.Background(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) updateInterfaces() error {
|
||||
interfaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var addresses []networkAddress
|
||||
for _, iif := range interfaces {
|
||||
var netAddresses []net.Addr
|
||||
netAddresses, err = iif.Addrs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var address networkAddress
|
||||
address.interfaceName = iif.Name
|
||||
address.interfaceIndex = iif.Index
|
||||
address.addresses = common.Map(common.FilterIsInstance(netAddresses, func(it net.Addr) (*net.IPNet, bool) {
|
||||
value, loaded := it.(*net.IPNet)
|
||||
return value, loaded
|
||||
}), func(it *net.IPNet) netip.Prefix {
|
||||
bits, _ := it.Mask.Size()
|
||||
return netip.PrefixFrom(M.AddrFromIP(it.IP), bits)
|
||||
})
|
||||
addresses = append(addresses, address)
|
||||
}
|
||||
m.networkAddresses = addresses
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) updateInterfacesPlatform() error {
|
||||
interfaces, err := m.Interfaces()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var addresses []networkAddress
|
||||
for _, iif := range interfaces {
|
||||
var address networkAddress
|
||||
address.interfaceName = iif.Name
|
||||
address.interfaceIndex = iif.Index
|
||||
// address.addresses = common.Map(iif.Addresses, netip.MustParsePrefix)
|
||||
addresses = append(addresses, address)
|
||||
}
|
||||
m.networkAddresses = addresses
|
||||
return nil
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package libbox
|
||||
|
||||
import "github.com/sagernet/sing-box/option"
|
||||
import (
|
||||
"github.com/sagernet/sing-box/option"
|
||||
)
|
||||
|
||||
type PlatformInterface interface {
|
||||
AutoDetectInterfaceControl(fd int32) error
|
||||
@@ -10,6 +12,11 @@ type PlatformInterface interface {
|
||||
FindConnectionOwner(ipProtocol int32, sourceAddress string, sourcePort int32, destinationAddress string, destinationPort int32) (int32, error)
|
||||
PackageNameByUid(uid int32) (string, error)
|
||||
UIDByPackageName(packageName string) (int32, error)
|
||||
UsePlatformDefaultInterfaceMonitor() bool
|
||||
StartDefaultInterfaceMonitor(listener InterfaceUpdateListener) error
|
||||
CloseDefaultInterfaceMonitor(listener InterfaceUpdateListener) error
|
||||
UsePlatformInterfaceGetter() bool
|
||||
GetInterfaces() (NetworkInterfaceIterator, error)
|
||||
}
|
||||
|
||||
type TunInterface interface {
|
||||
@@ -17,8 +24,19 @@ type TunInterface interface {
|
||||
Close() error
|
||||
}
|
||||
|
||||
type OnDemandRuleIterator interface {
|
||||
Next() OnDemandRule
|
||||
type InterfaceUpdateListener interface {
|
||||
UpdateDefaultInterface(interfaceName string, interfaceIndex int32)
|
||||
}
|
||||
|
||||
type NetworkInterface struct {
|
||||
Index int32
|
||||
MTU int32
|
||||
Name string
|
||||
Addresses StringIterator
|
||||
}
|
||||
|
||||
type NetworkInterfaceIterator interface {
|
||||
Next() *NetworkInterface
|
||||
HasNext() bool
|
||||
}
|
||||
|
||||
@@ -31,6 +49,11 @@ type OnDemandRule interface {
|
||||
ProbeURL() string
|
||||
}
|
||||
|
||||
type OnDemandRuleIterator interface {
|
||||
Next() OnDemandRule
|
||||
HasNext() bool
|
||||
}
|
||||
|
||||
type onDemandRule struct {
|
||||
option.OnDemandRule
|
||||
}
|
||||
|
||||
@@ -1,17 +1,33 @@
|
||||
package platform
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/netip"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/process"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
type Interface interface {
|
||||
Initialize(ctx context.Context, router adapter.Router) error
|
||||
AutoDetectInterfaceControl() control.Func
|
||||
OpenTun(options tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error)
|
||||
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
|
||||
io.Writer
|
||||
}
|
||||
|
||||
type NetworkInterface struct {
|
||||
Index int
|
||||
MTU int
|
||||
Name string
|
||||
Addresses []netip.Prefix
|
||||
}
|
||||
|
||||
@@ -6,11 +6,13 @@ import (
|
||||
"syscall"
|
||||
|
||||
"github.com/sagernet/sing-box"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/process"
|
||||
"github.com/sagernet/sing-box/experimental/libbox/internal/procfs"
|
||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
@@ -31,7 +33,7 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box
|
||||
instance, err := box.New(box.Options{
|
||||
Context: ctx,
|
||||
Options: options,
|
||||
PlatformInterface: &platformInterfaceWrapper{platformInterface, platformInterface.UseProcFS()},
|
||||
PlatformInterface: &platformInterfaceWrapper{iif: platformInterface, useProcFS: platformInterface.UseProcFS()},
|
||||
})
|
||||
if err != nil {
|
||||
cancel()
|
||||
@@ -58,6 +60,12 @@ var _ platform.Interface = (*platformInterfaceWrapper)(nil)
|
||||
type platformInterfaceWrapper struct {
|
||||
iif PlatformInterface
|
||||
useProcFS bool
|
||||
router adapter.Router
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) Initialize(ctx context.Context, router adapter.Router) error {
|
||||
w.router = router
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) AutoDetectInterfaceControl() control.Func {
|
||||
@@ -68,7 +76,7 @@ func (w *platformInterfaceWrapper) AutoDetectInterfaceControl() control.Func {
|
||||
}
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) OpenTun(options tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error) {
|
||||
func (w *platformInterfaceWrapper) OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error) {
|
||||
if len(options.IncludeUID) > 0 || len(options.ExcludeUID) > 0 {
|
||||
return nil, E.New("android: unsupported uid options")
|
||||
}
|
||||
@@ -79,12 +87,16 @@ func (w *platformInterfaceWrapper) OpenTun(options tun.Options, platformOptions
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options.Name, err = getTunnelName(tunFd)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "query tun name")
|
||||
}
|
||||
dupFd, err := dup(int(tunFd))
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "dup tun file descriptor")
|
||||
}
|
||||
options.FileDescriptor = dupFd
|
||||
return tun.New(options)
|
||||
return tun.New(*options)
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) Write(p []byte) (n int, err error) {
|
||||
@@ -118,3 +130,36 @@ func (w *platformInterfaceWrapper) FindProcessInfo(ctx context.Context, network
|
||||
packageName, _ := w.iif.PackageNameByUid(uid)
|
||||
return &process.Info{UserId: uid, PackageName: packageName}, nil
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) UsePlatformDefaultInterfaceMonitor() bool {
|
||||
return w.iif.UsePlatformDefaultInterfaceMonitor()
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) CreateDefaultInterfaceMonitor(errorHandler E.Handler) tun.DefaultInterfaceMonitor {
|
||||
return &platformDefaultInterfaceMonitor{
|
||||
platformInterfaceWrapper: w,
|
||||
errorHandler: errorHandler,
|
||||
defaultInterfaceIndex: -1,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) UsePlatformInterfaceGetter() bool {
|
||||
return w.iif.UsePlatformInterfaceGetter()
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) Interfaces() ([]platform.NetworkInterface, error) {
|
||||
interfaceIterator, err := w.iif.GetInterfaces()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var interfaces []platform.NetworkInterface
|
||||
for _, netInterface := range iteratorToArray[*NetworkInterface](interfaceIterator) {
|
||||
interfaces = append(interfaces, platform.NetworkInterface{
|
||||
Index: int(netInterface.Index),
|
||||
MTU: int(netInterface.MTU),
|
||||
Name: netInterface.Name,
|
||||
Addresses: common.Map(iteratorToArray[string](netInterface.Addresses), netip.MustParsePrefix),
|
||||
})
|
||||
}
|
||||
return interfaces, nil
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ func mapRoutePrefix(prefixes []netip.Prefix) RoutePrefixIterator {
|
||||
var _ TunOptions = (*tunOptions)(nil)
|
||||
|
||||
type tunOptions struct {
|
||||
tun.Options
|
||||
*tun.Options
|
||||
option.TunPlatformOptions
|
||||
}
|
||||
|
||||
|
||||
11
experimental/libbox/tun_name_darwin.go
Normal file
11
experimental/libbox/tun_name_darwin.go
Normal file
@@ -0,0 +1,11 @@
|
||||
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 */
|
||||
)
|
||||
}
|
||||
26
experimental/libbox/tun_name_linux.go
Normal file
26
experimental/libbox/tun_name_linux.go
Normal file
@@ -0,0 +1,26 @@
|
||||
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
|
||||
}
|
||||
9
experimental/libbox/tun_name_other.go
Normal file
9
experimental/libbox/tun_name_other.go
Normal file
@@ -0,0 +1,9 @@
|
||||
//go:build !(darwin || linux)
|
||||
|
||||
package libbox
|
||||
|
||||
import "os"
|
||||
|
||||
func getTunnelName(fd int32) (string, error) {
|
||||
return "", os.ErrInvalid
|
||||
}
|
||||
14
go.mod
14
go.mod
@@ -4,7 +4,7 @@ go 1.18
|
||||
|
||||
require (
|
||||
berty.tech/go-libtor v1.0.385
|
||||
github.com/Dreamacro/clash v1.14.0
|
||||
github.com/Dreamacro/clash v1.15.0
|
||||
github.com/caddyserver/certmagic v0.17.2
|
||||
github.com/cretz/bine v0.2.0
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
@@ -25,12 +25,12 @@ require (
|
||||
github.com/sagernet/gomobile v0.0.0-20230413023804-244d7ff07035
|
||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
||||
github.com/sagernet/sing v0.2.3-0.20230413023204-48b019b13e78
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230408004833-5adaf486d440
|
||||
github.com/sagernet/sing-shadowsocks v0.2.1-0.20230412123110-1a7c32b4e2e7
|
||||
github.com/sagernet/sing-shadowtls v0.1.1-0.20230409094821-9abef019436f
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab
|
||||
github.com/sagernet/sing-vmess v0.1.4-0.20230412122845-9470e68f5e45
|
||||
github.com/sagernet/sing v0.2.4-0.20230418095640-3b5e6c1812d3
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230418025317-8a132998b322
|
||||
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d
|
||||
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230419061614-d744d03d9302
|
||||
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3
|
||||
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37
|
||||
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9
|
||||
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2
|
||||
|
||||
30
go.sum
30
go.sum
@@ -1,7 +1,7 @@
|
||||
berty.tech/go-libtor v1.0.385 h1:RWK94C3hZj6Z2GdvePpHJLnWYobFr3bY/OdUJ5aoEXw=
|
||||
berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+fJEw=
|
||||
github.com/Dreamacro/clash v1.14.0 h1:ehJ/C/1m9LEjmME72WSE/Y2YqbR3Q54AbjqiRCvtyW4=
|
||||
github.com/Dreamacro/clash v1.14.0/go.mod h1:ia2CU7V713H1QdCqMwOLK9U9V5Ay8X0voj3yQr2tk+I=
|
||||
github.com/Dreamacro/clash v1.15.0 h1:mlpD950VEggXZBNahV66hyKDRxcczkj3vymoAt78KyE=
|
||||
github.com/Dreamacro/clash v1.15.0/go.mod h1:WNH69bN11LiAdgdSr4hpkEuXVMfBbWyhEKMCTx9BtNE=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||
@@ -101,8 +101,6 @@ github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0 h1:KyhtFFt
|
||||
github.com/sagernet/cloudflare-tls v0.0.0-20221031050923-d70792f4c3a0/go.mod h1:D4SFEOkJK+4W1v86ZhX0jPM0rAL498fyQAChqMtes/I=
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
|
||||
github.com/sagernet/gomobile v0.0.0-20230413023437-ec061884b992 h1:WkcHhOX3ce9ElLKDUQKJrAt7SjpKNnASsPbMfqfZEPc=
|
||||
github.com/sagernet/gomobile v0.0.0-20230413023437-ec061884b992/go.mod h1:5YE39YkJkCcMsfq1jMKkjsrM2GfBoF9JVWnvU89hmvU=
|
||||
github.com/sagernet/gomobile v0.0.0-20230413023804-244d7ff07035 h1:KttYh6bBhIw8Y6/Ljn7CGwC3CKZn788rzMJmeAKjY+8=
|
||||
github.com/sagernet/gomobile v0.0.0-20230413023804-244d7ff07035/go.mod h1:5YE39YkJkCcMsfq1jMKkjsrM2GfBoF9JVWnvU89hmvU=
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
||||
@@ -113,18 +111,18 @@ github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byL
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||
github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
||||
github.com/sagernet/sing v0.2.3-0.20230413023204-48b019b13e78 h1:bTE9RgURmiFiTjXaYN6q9BYPTBPKIzlFYNy2p+WOubI=
|
||||
github.com/sagernet/sing v0.2.3-0.20230413023204-48b019b13e78/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230408004833-5adaf486d440 h1:VH8/BcOVuApHtS+vKP+khxlGRcXH7KKhgkTDtNynqSQ=
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230408004833-5adaf486d440/go.mod h1:69PNSHyEmXdjf6C+bXBOdr2GQnPeEyWjIzo/MV8gmz8=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.1-0.20230412123110-1a7c32b4e2e7 h1:3WDMIF1aE/twc5gJ+9PF2ZJqUxwZ80MPtNBKE3yBevU=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.1-0.20230412123110-1a7c32b4e2e7/go.mod h1:WoVjGUvRqsx5yhYeDAB5CijCHpNDi0LUPHl3cf7u8Lc=
|
||||
github.com/sagernet/sing-shadowtls v0.1.1-0.20230409094821-9abef019436f h1:qzQvpcDm60zPW8UlZa8UEaBoFORFeGAnhDncPc3VWT4=
|
||||
github.com/sagernet/sing-shadowtls v0.1.1-0.20230409094821-9abef019436f/go.mod h1:MxB+Q9H0pAHcrlvNmwSs1crljRwHFFVhtXyOMBy44Nw=
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab h1:a9oeWuPBuIZ70qMhIIH6RrYhp886xN9jJIwsuu4ZFUo=
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab/go.mod h1:4YxIDEkkCjGXDOTMPw1SXpLmCQUFAWuaQN250oo+928=
|
||||
github.com/sagernet/sing-vmess v0.1.4-0.20230412122845-9470e68f5e45 h1:QqYhWah3u+o2tvLRuTfEu3BwsGpf/wNnVK/VNQV2YBM=
|
||||
github.com/sagernet/sing-vmess v0.1.4-0.20230412122845-9470e68f5e45/go.mod h1:eULig3LgaeNiWSquSlzXF42Joypsj3fO1W+Qy93o6hk=
|
||||
github.com/sagernet/sing v0.2.4-0.20230418095640-3b5e6c1812d3 h1:dkH6SEs3yZlUjSXUAn64LUlFAfAgJqiThaWiBKWJ0Q0=
|
||||
github.com/sagernet/sing v0.2.4-0.20230418095640-3b5e6c1812d3/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230418025317-8a132998b322 h1:UDSeJZ2xB3dj1lySnM5LpF48dGlphGstw2BqtkJwcZI=
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230418025317-8a132998b322/go.mod h1:2wjxSr1Gbecq9A0ESA9cnR399tQTcpCZEOGytekb+qI=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d h1:UUxtLujzp5jmtOXqXpSOGvHwHSZcBveKVDzRJ4GlnFU=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d/go.mod h1:Co3PJXcaZoLwHGBfT0rbSnn9C7ywc41zVYWtDeoeI/Q=
|
||||
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b h1:ouW/6IDCrxkBe19YSbdCd7buHix7b+UZ6BM4Zz74XF4=
|
||||
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b/go.mod h1:oG8bPerYI6cZ74KquY3DvA7ynECyrILPBnce6wtBqeI=
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230419061614-d744d03d9302 h1:aPb0T2HQRTG2t7fEwLvFLZSXmhmnBh+SMs2NufhmrsI=
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230419061614-d744d03d9302/go.mod h1:bvcVzlf9q9dgxt8qKluW+zOXCFoN1+SpBG3sHTq8/9Q=
|
||||
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 h1:BHOnxrbC929JonuKqFdJ7ZbDp7zs4oTlH5KFvKtWu9U=
|
||||
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3/go.mod h1:yKrAr+dqZd64DxBXCHWrYicp+n4qbqO73mtwv3dck8U=
|
||||
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as=
|
||||
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37/go.mod h1:3skNSftZDJWTGVtVaM2jfbce8qHnmH/AGDRe62iNOg0=
|
||||
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE=
|
||||
|
||||
@@ -53,7 +53,7 @@ func NewDirect(ctx context.Context, router adapter.Router, logger log.ContextLog
|
||||
} else {
|
||||
udpTimeout = int64(C.UDPTimeout.Seconds())
|
||||
}
|
||||
inbound.udpNat = udpnat.New[netip.AddrPort](udpTimeout, adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound))
|
||||
inbound.udpNat = udpnat.New[netip.AddrPort](ctx, udpTimeout, adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound))
|
||||
inbound.connHandler = inbound
|
||||
inbound.packetHandler = inbound
|
||||
inbound.packetUpstream = inbound.udpNat
|
||||
|
||||
@@ -609,6 +609,10 @@ func (c *naiveH2Conn) SetWriteDeadline(t time.Time) error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (c *naiveH2Conn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *naiveH2Conn) UpstreamReader() any {
|
||||
return c.reader
|
||||
}
|
||||
|
||||
@@ -64,11 +64,11 @@ func newShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
|
||||
var err error
|
||||
switch {
|
||||
case options.Method == shadowsocks.MethodNone:
|
||||
inbound.service = shadowsocks.NewNoneService(options.UDPTimeout, inbound.upstreamContextHandler())
|
||||
inbound.service = shadowsocks.NewNoneService(ctx, options.UDPTimeout, inbound.upstreamContextHandler())
|
||||
case common.Contains(shadowaead.List, options.Method):
|
||||
inbound.service, err = shadowaead.NewService(options.Method, nil, options.Password, udpTimeout, inbound.upstreamContextHandler())
|
||||
inbound.service, err = shadowaead.NewService(ctx, options.Method, nil, options.Password, udpTimeout, inbound.upstreamContextHandler())
|
||||
case common.Contains(shadowaead_2022.List, options.Method):
|
||||
inbound.service, err = shadowaead_2022.NewServiceWithPassword(options.Method, options.Password, udpTimeout, inbound.upstreamContextHandler(), router.TimeFunc())
|
||||
inbound.service, err = shadowaead_2022.NewServiceWithPassword(ctx, options.Method, options.Password, udpTimeout, inbound.upstreamContextHandler(), router.TimeFunc())
|
||||
default:
|
||||
err = E.New("unsupported method: ", options.Method)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ import (
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-shadowsocks"
|
||||
"github.com/sagernet/sing-shadowsocks/shadowaead"
|
||||
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
@@ -25,7 +27,7 @@ var (
|
||||
|
||||
type ShadowsocksMulti struct {
|
||||
myInboundAdapter
|
||||
service *shadowaead_2022.MultiService[int]
|
||||
service shadowsocks.MultiService[int]
|
||||
users []option.ShadowsocksUser
|
||||
}
|
||||
|
||||
@@ -49,16 +51,28 @@ func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.
|
||||
} else {
|
||||
udpTimeout = int64(C.UDPTimeout.Seconds())
|
||||
}
|
||||
if !common.Contains(shadowaead_2022.List, options.Method) {
|
||||
var (
|
||||
service shadowsocks.MultiService[int]
|
||||
err error
|
||||
)
|
||||
if common.Contains(shadowaead_2022.List, options.Method) {
|
||||
service, err = shadowaead_2022.NewMultiServiceWithPassword[int](
|
||||
ctx,
|
||||
options.Method,
|
||||
options.Password,
|
||||
udpTimeout,
|
||||
adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound),
|
||||
router.TimeFunc(),
|
||||
)
|
||||
} else if common.Contains(shadowaead.List, options.Method) {
|
||||
service, err = shadowaead.NewMultiService[int](
|
||||
ctx,
|
||||
options.Method,
|
||||
udpTimeout,
|
||||
adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound))
|
||||
} else {
|
||||
return nil, E.New("unsupported method: " + options.Method)
|
||||
}
|
||||
service, err := shadowaead_2022.NewMultiServiceWithPassword[int](
|
||||
options.Method,
|
||||
options.Password,
|
||||
udpTimeout,
|
||||
adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound),
|
||||
router.TimeFunc(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ func newShadowsocksRelay(ctx context.Context, router adapter.Router, logger log.
|
||||
udpTimeout = int64(C.UDPTimeout.Seconds())
|
||||
}
|
||||
service, err := shadowaead_2022.NewRelayServiceWithPassword[int](
|
||||
ctx,
|
||||
options.Method,
|
||||
options.Password,
|
||||
udpTimeout,
|
||||
|
||||
@@ -45,7 +45,7 @@ func NewTProxy(ctx context.Context, router adapter.Router, logger log.ContextLog
|
||||
}
|
||||
tproxy.connHandler = tproxy
|
||||
tproxy.oobPacketHandler = tproxy
|
||||
tproxy.udpNat = udpnat.New[netip.AddrPort](udpTimeout, tproxy.upstreamContextHandler())
|
||||
tproxy.udpNat = udpnat.New[netip.AddrPort](ctx, udpTimeout, tproxy.upstreamContextHandler())
|
||||
tproxy.packetUpstream = tproxy.udpNat
|
||||
return tproxy
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ func (t *Tun) Start() error {
|
||||
)
|
||||
t.logger.Trace("opening interface")
|
||||
if t.platformInterface != nil {
|
||||
tunInterface, err = t.platformInterface.OpenTun(t.tunOptions, t.platformOptions)
|
||||
tunInterface, err = t.platformInterface.OpenTun(&t.tunOptions, t.platformOptions)
|
||||
} else {
|
||||
tunInterface, err = tun.New(t.tunOptions)
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"github.com/sagernet/sing-vmess/packetaddr"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
"github.com/sagernet/sing/common/bufio/deadline"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
@@ -172,7 +171,7 @@ func (h *VLESS) newPacketConnection(ctx context.Context, conn N.PacketConn, meta
|
||||
}
|
||||
if metadata.Destination.Fqdn == packetaddr.SeqPacketMagicAddress {
|
||||
metadata.Destination = M.Socksaddr{}
|
||||
conn = deadline.NewPacketConn(packetaddr.NewConn(conn.(vmess.PacketConn), metadata.Destination))
|
||||
conn = packetaddr.NewConn(conn.(vmess.PacketConn), metadata.Destination)
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet addr connection")
|
||||
} else {
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
"github.com/sagernet/sing-vmess/packetaddr"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
"github.com/sagernet/sing/common/bufio/deadline"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
@@ -180,7 +179,7 @@ func (h *VMess) newPacketConnection(ctx context.Context, conn N.PacketConn, meta
|
||||
}
|
||||
if metadata.Destination.Fqdn == packetaddr.SeqPacketMagicAddress {
|
||||
metadata.Destination = M.Socksaddr{}
|
||||
conn = deadline.NewPacketConn(packetaddr.NewConn(conn.(vmess.PacketConn), metadata.Destination))
|
||||
conn = packetaddr.NewConn(conn.(vmess.PacketConn), metadata.Destination)
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet addr connection")
|
||||
} else {
|
||||
h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
|
||||
|
||||
@@ -4,7 +4,7 @@ type ShadowsocksInboundOptions struct {
|
||||
ListenOptions
|
||||
Network NetworkList `json:"network,omitempty"`
|
||||
Method string `json:"method"`
|
||||
Password string `json:"password"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Users []ShadowsocksUser `json:"users,omitempty"`
|
||||
Destinations []ShadowsocksDestination `json:"destinations,omitempty"`
|
||||
}
|
||||
|
||||
@@ -39,6 +39,10 @@ func (a *myOutboundAdapter) Network() []string {
|
||||
return a.network
|
||||
}
|
||||
|
||||
func (a *myOutboundAdapter) NewError(ctx context.Context, err error) {
|
||||
NewError(a.logger, ctx, err)
|
||||
}
|
||||
|
||||
func NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
ctx = adapter.WithContext(ctx, &metadata)
|
||||
var outConn net.Conn
|
||||
@@ -121,3 +125,12 @@ func CopyEarlyConn(ctx context.Context, conn net.Conn, serverConn net.Conn) erro
|
||||
}
|
||||
return bufio.CopyConn(ctx, conn, serverConn)
|
||||
}
|
||||
|
||||
func NewError(logger log.ContextLogger, ctx context.Context, err error) {
|
||||
common.Close(err)
|
||||
if E.IsClosedOrCanceled(err) {
|
||||
logger.DebugContext(ctx, "connection closed: ", err)
|
||||
return
|
||||
}
|
||||
logger.ErrorContext(ctx, err)
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/atomic"
|
||||
"github.com/sagernet/sing/common/batch"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
@@ -20,8 +21,9 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
_ adapter.Outbound = (*URLTest)(nil)
|
||||
_ adapter.OutboundGroup = (*URLTest)(nil)
|
||||
_ adapter.Outbound = (*URLTest)(nil)
|
||||
_ adapter.OutboundGroup = (*URLTest)(nil)
|
||||
_ adapter.InterfaceUpdateListener = (*URLTest)(nil)
|
||||
)
|
||||
|
||||
type URLTest struct {
|
||||
@@ -71,7 +73,8 @@ func (s *URLTest) Start() error {
|
||||
outbounds = append(outbounds, detour)
|
||||
}
|
||||
s.group = NewURLTestGroup(s.ctx, s.router, s.logger, outbounds, s.link, s.interval, s.tolerance)
|
||||
return s.group.Start()
|
||||
go s.group.CheckOutbounds(false)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *URLTest) Close() error {
|
||||
@@ -93,6 +96,7 @@ func (s *URLTest) URLTest(ctx context.Context, link string) (map[string]uint16,
|
||||
}
|
||||
|
||||
func (s *URLTest) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
s.group.Start()
|
||||
outbound := s.group.Select(network)
|
||||
conn, err := outbound.DialContext(ctx, network, destination)
|
||||
if err == nil {
|
||||
@@ -104,6 +108,7 @@ func (s *URLTest) DialContext(ctx context.Context, network string, destination M
|
||||
}
|
||||
|
||||
func (s *URLTest) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
s.group.Start()
|
||||
outbound := s.group.Select(N.NetworkUDP)
|
||||
conn, err := outbound.ListenPacket(ctx, destination)
|
||||
if err == nil {
|
||||
@@ -122,6 +127,11 @@ func (s *URLTest) NewPacketConnection(ctx context.Context, conn N.PacketConn, me
|
||||
return NewPacketConnection(ctx, s, conn, metadata)
|
||||
}
|
||||
|
||||
func (s *URLTest) InterfaceUpdated() error {
|
||||
go s.group.CheckOutbounds(true)
|
||||
return nil
|
||||
}
|
||||
|
||||
type URLTestGroup struct {
|
||||
ctx context.Context
|
||||
router adapter.Router
|
||||
@@ -131,7 +141,9 @@ type URLTestGroup struct {
|
||||
interval time.Duration
|
||||
tolerance uint16
|
||||
history *urltest.HistoryStorage
|
||||
checking atomic.Bool
|
||||
|
||||
access sync.Mutex
|
||||
ticker *time.Ticker
|
||||
close chan struct{}
|
||||
}
|
||||
@@ -162,13 +174,23 @@ func NewURLTestGroup(ctx context.Context, router adapter.Router, logger log.Logg
|
||||
}
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) Start() error {
|
||||
func (g *URLTestGroup) Start() {
|
||||
if g.ticker != nil {
|
||||
return
|
||||
}
|
||||
g.access.Lock()
|
||||
defer g.access.Unlock()
|
||||
if g.ticker != nil {
|
||||
return
|
||||
}
|
||||
g.ticker = time.NewTicker(g.interval)
|
||||
go g.loopCheck()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) Close() error {
|
||||
if g.ticker == nil {
|
||||
return nil
|
||||
}
|
||||
g.ticker.Stop()
|
||||
close(g.close)
|
||||
return nil
|
||||
@@ -228,25 +250,33 @@ func (g *URLTestGroup) Fallback(used adapter.Outbound) []adapter.Outbound {
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) loopCheck() {
|
||||
go g.checkOutbounds()
|
||||
go g.CheckOutbounds(true)
|
||||
for {
|
||||
select {
|
||||
case <-g.close:
|
||||
return
|
||||
case <-g.ticker.C:
|
||||
g.checkOutbounds()
|
||||
g.CheckOutbounds(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) checkOutbounds() {
|
||||
_, _ = g.URLTest(g.ctx, g.link)
|
||||
func (g *URLTestGroup) CheckOutbounds(force bool) {
|
||||
_, _ = g.urlTest(g.ctx, g.link, force)
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) URLTest(ctx context.Context, link string) (map[string]uint16, error) {
|
||||
return g.urlTest(ctx, link, false)
|
||||
}
|
||||
|
||||
func (g *URLTestGroup) urlTest(ctx context.Context, link string, force bool) (map[string]uint16, error) {
|
||||
result := make(map[string]uint16)
|
||||
if g.checking.Swap(true) {
|
||||
return result, nil
|
||||
}
|
||||
defer g.checking.Store(false)
|
||||
b, _ := batch.New(ctx, batch.WithConcurrencyNum[any](10))
|
||||
checked := make(map[string]bool)
|
||||
result := make(map[string]uint16)
|
||||
var resultAccess sync.Mutex
|
||||
for _, detour := range g.outbounds {
|
||||
tag := detour.Tag()
|
||||
@@ -255,7 +285,7 @@ func (g *URLTestGroup) URLTest(ctx context.Context, link string) (map[string]uin
|
||||
continue
|
||||
}
|
||||
history := g.history.LoadURLTestHistory(realTag)
|
||||
if history != nil && time.Now().Sub(history.Time) < g.interval {
|
||||
if !force && history != nil && time.Now().Sub(history.Time) < g.interval {
|
||||
continue
|
||||
}
|
||||
checked[realTag] = true
|
||||
|
||||
@@ -67,7 +67,7 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
|
||||
connectAddr = options.ServerOptions.Build()
|
||||
}
|
||||
}
|
||||
outbound.bind = wireguard.NewClientBind(ctx, dialer.New(router, options.DialerOptions), isConnect, connectAddr, reserved)
|
||||
outbound.bind = wireguard.NewClientBind(ctx, outbound, dialer.New(router, options.DialerOptions), isConnect, connectAddr, reserved)
|
||||
localPrefixes := common.Map(options.LocalAddress, option.ListenPrefix.Build)
|
||||
if len(localPrefixes) == 0 {
|
||||
return nil, E.New("missing local address")
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
var _ control.InterfaceFinder = (*myInterfaceFinder)(nil)
|
||||
|
||||
type myInterfaceFinder struct {
|
||||
ifs []net.Interface
|
||||
interfaces []net.Interface
|
||||
}
|
||||
|
||||
func (f *myInterfaceFinder) update() error {
|
||||
@@ -17,12 +17,16 @@ func (f *myInterfaceFinder) update() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.ifs = ifs
|
||||
f.interfaces = ifs
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *myInterfaceFinder) updateInterfaces(interfaces []net.Interface) {
|
||||
f.interfaces = interfaces
|
||||
}
|
||||
|
||||
func (f *myInterfaceFinder) InterfaceIndexByName(name string) (interfaceIndex int, err error) {
|
||||
for _, netInterface := range f.ifs {
|
||||
for _, netInterface := range f.interfaces {
|
||||
if netInterface.Name == name {
|
||||
return netInterface.Index, nil
|
||||
}
|
||||
@@ -36,7 +40,7 @@ func (f *myInterfaceFinder) InterfaceIndexByName(name string) (interfaceIndex in
|
||||
}
|
||||
|
||||
func (f *myInterfaceFinder) InterfaceNameByIndex(index int) (interfaceName string, err error) {
|
||||
for _, netInterface := range f.ifs {
|
||||
for _, netInterface := range f.interfaces {
|
||||
if netInterface.Index == index {
|
||||
return netInterface.Name, nil
|
||||
}
|
||||
|
||||
111
route/router.go
111
route/router.go
@@ -12,12 +12,12 @@ import (
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
"github.com/sagernet/sing-box/common/dialer/conntrack"
|
||||
"github.com/sagernet/sing-box/common/geoip"
|
||||
"github.com/sagernet/sing-box/common/geosite"
|
||||
"github.com/sagernet/sing-box/common/mux"
|
||||
"github.com/sagernet/sing-box/common/process"
|
||||
"github.com/sagernet/sing-box/common/sniff"
|
||||
"github.com/sagernet/sing-box/common/warning"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
"github.com/sagernet/sing/common/bufio/deadline"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
@@ -39,27 +40,6 @@ import (
|
||||
"github.com/sagernet/sing/common/uot"
|
||||
)
|
||||
|
||||
var warnDefaultInterfaceOnUnsupportedPlatform = warning.New(
|
||||
func() bool {
|
||||
return !(C.IsLinux || C.IsWindows || C.IsDarwin)
|
||||
},
|
||||
"route option `default_mark` is only supported on Linux and Windows",
|
||||
)
|
||||
|
||||
var warnDefaultMarkOnNonLinux = warning.New(
|
||||
func() bool {
|
||||
return !C.IsLinux
|
||||
},
|
||||
"route option `default_mark` is only supported on Linux",
|
||||
)
|
||||
|
||||
var warnFindProcessOnUnsupportedPlatform = warning.New(
|
||||
func() bool {
|
||||
return !(C.IsLinux || C.IsWindows || C.IsDarwin)
|
||||
},
|
||||
"route option `find_process` is only supported on Linux, Windows, and macOS",
|
||||
)
|
||||
|
||||
var _ adapter.Router = (*Router)(nil)
|
||||
|
||||
type Router struct {
|
||||
@@ -113,16 +93,6 @@ func NewRouter(
|
||||
inbounds []option.Inbound,
|
||||
platformInterface platform.Interface,
|
||||
) (*Router, error) {
|
||||
if options.DefaultInterface != "" {
|
||||
warnDefaultInterfaceOnUnsupportedPlatform.Check()
|
||||
}
|
||||
if options.DefaultMark != 0 {
|
||||
warnDefaultMarkOnNonLinux.Check()
|
||||
}
|
||||
if options.FindProcess {
|
||||
warnFindProcessOnUnsupportedPlatform.Check()
|
||||
}
|
||||
|
||||
router := &Router{
|
||||
ctx: ctx,
|
||||
logger: logFactory.NewLogger("router"),
|
||||
@@ -143,7 +113,7 @@ func NewRouter(
|
||||
defaultMark: options.DefaultMark,
|
||||
platformInterface: platformInterface,
|
||||
}
|
||||
router.dnsClient = dns.NewClient(dnsOptions.DNSClientOptions.DisableCache, dnsOptions.DNSClientOptions.DisableExpire, router.dnsLogger)
|
||||
router.dnsClient = dns.NewClient(ctx, dnsOptions.DNSClientOptions.DisableCache, dnsOptions.DNSClientOptions.DisableExpire, router.dnsLogger)
|
||||
for i, ruleOptions := range options.Rules {
|
||||
routeRule, err := NewRule(router, router.logger, ruleOptions)
|
||||
if err != nil {
|
||||
@@ -289,29 +259,36 @@ func NewRouter(
|
||||
router.fakeIPStore = fakeip.NewStore(router, inet4Range, inet6Range)
|
||||
}
|
||||
|
||||
needInterfaceMonitor := platformInterface == nil && (options.AutoDetectInterface || common.Any(inbounds, func(inbound option.Inbound) bool {
|
||||
usePlatformDefaultInterfaceMonitor := platformInterface != nil && platformInterface.UsePlatformDefaultInterfaceMonitor()
|
||||
needInterfaceMonitor := options.AutoDetectInterface || common.Any(inbounds, func(inbound option.Inbound) bool {
|
||||
return inbound.HTTPOptions.SetSystemProxy || inbound.MixedOptions.SetSystemProxy || inbound.TunOptions.AutoRoute
|
||||
}))
|
||||
})
|
||||
|
||||
if needInterfaceMonitor {
|
||||
networkMonitor, err := tun.NewNetworkUpdateMonitor(router)
|
||||
if err == nil {
|
||||
router.networkMonitor = networkMonitor
|
||||
networkMonitor.RegisterCallback(router.interfaceFinder.update)
|
||||
if !usePlatformDefaultInterfaceMonitor {
|
||||
networkMonitor, err := tun.NewNetworkUpdateMonitor(router)
|
||||
if err != os.ErrInvalid {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
router.networkMonitor = networkMonitor
|
||||
networkMonitor.RegisterCallback(router.interfaceFinder.update)
|
||||
interfaceMonitor, err := tun.NewDefaultInterfaceMonitor(router.networkMonitor, tun.DefaultInterfaceMonitorOptions{
|
||||
OverrideAndroidVPN: options.OverrideAndroidVPN,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, E.New("auto_detect_interface unsupported on current platform")
|
||||
}
|
||||
interfaceMonitor.RegisterCallback(router.notifyNetworkUpdate)
|
||||
router.interfaceMonitor = interfaceMonitor
|
||||
}
|
||||
} else {
|
||||
interfaceMonitor := platformInterface.CreateDefaultInterfaceMonitor(router)
|
||||
interfaceMonitor.RegisterCallback(router.notifyNetworkUpdate)
|
||||
router.interfaceMonitor = interfaceMonitor
|
||||
}
|
||||
}
|
||||
|
||||
if router.networkMonitor != nil && needInterfaceMonitor {
|
||||
interfaceMonitor, err := tun.NewDefaultInterfaceMonitor(router.networkMonitor, tun.DefaultInterfaceMonitorOptions{
|
||||
OverrideAndroidVPN: options.OverrideAndroidVPN,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, E.New("auto_detect_interface unsupported on current platform")
|
||||
}
|
||||
interfaceMonitor.RegisterCallback(router.notifyNetworkUpdate)
|
||||
router.interfaceMonitor = interfaceMonitor
|
||||
}
|
||||
|
||||
needFindProcess := hasRule(options.Rules, isProcessRule) || hasDNSRule(dnsOptions.Rules, isProcessDNSRule) || options.FindProcess
|
||||
needPackageManager := C.IsAndroid && platformInterface == nil && (needFindProcess || common.Any(inbounds, func(inbound option.Inbound) bool {
|
||||
return len(inbound.TunOptions.IncludePackage) > 0 || len(inbound.TunOptions.ExcludePackage) > 0
|
||||
@@ -657,6 +634,10 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
|
||||
r.logger.DebugContext(ctx, "found fakeip domain: ", domain)
|
||||
}
|
||||
|
||||
if deadline.NeedAdditionalReadDeadline(conn) {
|
||||
conn = deadline.NewConn(conn)
|
||||
}
|
||||
|
||||
if metadata.InboundOptions.SniffEnabled {
|
||||
buffer := buf.NewPacket()
|
||||
buffer.FullReset()
|
||||
@@ -762,6 +743,11 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
|
||||
r.logger.DebugContext(ctx, "found fakeip domain: ", domain)
|
||||
}
|
||||
|
||||
// Currently we don't have deadline usages for UDP connections
|
||||
/*if deadline.NeedAdditionalReadDeadline(conn) {
|
||||
conn = deadline.NewPacketConn(bufio.NewNetPacketConn(conn))
|
||||
}*/
|
||||
|
||||
if metadata.InboundOptions.SniffEnabled {
|
||||
buffer := buf.NewPacket()
|
||||
buffer.FullReset()
|
||||
@@ -886,6 +872,25 @@ func (r *Router) InterfaceFinder() control.InterfaceFinder {
|
||||
return &r.interfaceFinder
|
||||
}
|
||||
|
||||
func (r *Router) UpdateInterfaces() error {
|
||||
if r.platformInterface == nil || !r.platformInterface.UsePlatformInterfaceGetter() {
|
||||
return r.interfaceFinder.update()
|
||||
} else {
|
||||
interfaces, err := r.platformInterface.Interfaces()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.interfaceFinder.updateInterfaces(common.Map(interfaces, func(it platform.NetworkInterface) net.Interface {
|
||||
return net.Interface{
|
||||
Name: it.Name,
|
||||
Index: it.Index,
|
||||
MTU: it.MTU,
|
||||
}
|
||||
}))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Router) AutoDetectInterface() bool {
|
||||
return r.autoDetectInterface
|
||||
}
|
||||
@@ -970,7 +975,7 @@ func (r *Router) NewError(ctx context.Context, err error) {
|
||||
}
|
||||
|
||||
func (r *Router) notifyNetworkUpdate(int) error {
|
||||
if C.IsAndroid {
|
||||
if C.IsAndroid && r.platformInterface == nil {
|
||||
var vpnStatus string
|
||||
if r.interfaceMonitor.AndroidVPNEnabled() {
|
||||
vpnStatus = "enabled"
|
||||
@@ -982,6 +987,10 @@ func (r *Router) notifyNetworkUpdate(int) error {
|
||||
r.logger.Info("updated default interface ", r.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", r.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified()))
|
||||
}
|
||||
|
||||
if conntrack.Enabled {
|
||||
conntrack.Close()
|
||||
}
|
||||
|
||||
for _, outbound := range r.outbounds {
|
||||
listener, isListener := outbound.(adapter.InterfaceUpdateListener)
|
||||
if isListener {
|
||||
|
||||
@@ -4,13 +4,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/warning"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
)
|
||||
|
||||
var warnPackageNameOnNonAndroid = warning.New(
|
||||
func() bool { return !C.IsAndroid },
|
||||
"rule item `package_name` is only supported on Android",
|
||||
)
|
||||
|
||||
var _ RuleItem = (*PackageNameItem)(nil)
|
||||
@@ -21,7 +14,6 @@ type PackageNameItem struct {
|
||||
}
|
||||
|
||||
func NewPackageNameItem(packageNameList []string) *PackageNameItem {
|
||||
warnPackageNameOnNonAndroid.Check()
|
||||
rule := &PackageNameItem{
|
||||
packageNames: packageNameList,
|
||||
packageMap: make(map[string]bool),
|
||||
|
||||
@@ -5,13 +5,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/warning"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
)
|
||||
|
||||
var warnProcessNameOnNonSupportedPlatform = warning.New(
|
||||
func() bool { return !(C.IsLinux || C.IsWindows || C.IsDarwin) },
|
||||
"rule item `process_name` is only supported on Linux, Windows and macOS",
|
||||
)
|
||||
|
||||
var _ RuleItem = (*ProcessItem)(nil)
|
||||
@@ -22,7 +15,6 @@ type ProcessItem struct {
|
||||
}
|
||||
|
||||
func NewProcessItem(processNameList []string) *ProcessItem {
|
||||
warnProcessNameOnNonSupportedPlatform.Check()
|
||||
rule := &ProcessItem{
|
||||
processes: processNameList,
|
||||
processMap: make(map[string]bool),
|
||||
|
||||
@@ -4,13 +4,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/warning"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
)
|
||||
|
||||
var warnProcessPathOnNonSupportedPlatform = warning.New(
|
||||
func() bool { return !(C.IsLinux || C.IsWindows || C.IsDarwin) },
|
||||
"rule item `process_path` is only supported on Linux, Windows and macOS",
|
||||
)
|
||||
|
||||
var _ RuleItem = (*ProcessPathItem)(nil)
|
||||
@@ -21,7 +14,6 @@ type ProcessPathItem struct {
|
||||
}
|
||||
|
||||
func NewProcessPathItem(processNameList []string) *ProcessPathItem {
|
||||
warnProcessPathOnNonSupportedPlatform.Check()
|
||||
rule := &ProcessPathItem{
|
||||
processes: processNameList,
|
||||
processMap: make(map[string]bool),
|
||||
|
||||
@@ -4,16 +4,9 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/warning"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
)
|
||||
|
||||
var warnUserOnNonLinux = warning.New(
|
||||
func() bool { return !C.IsLinux },
|
||||
"rule item `user` is only supported on Linux",
|
||||
)
|
||||
|
||||
var _ RuleItem = (*UserItem)(nil)
|
||||
|
||||
type UserItem struct {
|
||||
@@ -22,7 +15,6 @@ type UserItem struct {
|
||||
}
|
||||
|
||||
func NewUserItem(users []string) *UserItem {
|
||||
warnUserOnNonLinux.Check()
|
||||
userMap := make(map[string]bool)
|
||||
for _, protocol := range users {
|
||||
userMap[protocol] = true
|
||||
|
||||
@@ -4,16 +4,9 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/warning"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
)
|
||||
|
||||
var warnUserIDOnNonLinux = warning.New(
|
||||
func() bool { return !C.IsLinux },
|
||||
"rule item `user_id` is only supported on Linux",
|
||||
)
|
||||
|
||||
var _ RuleItem = (*UserIdItem)(nil)
|
||||
|
||||
type UserIdItem struct {
|
||||
@@ -22,7 +15,6 @@ type UserIdItem struct {
|
||||
}
|
||||
|
||||
func NewUserIDItem(userIdList []int32) *UserIdItem {
|
||||
warnUserIDOnNonLinux.Check()
|
||||
rule := &UserIdItem{
|
||||
userIds: userIdList,
|
||||
userIdMap: make(map[int32]bool),
|
||||
|
||||
24
test/go.mod
24
test/go.mod
@@ -7,20 +7,20 @@ require github.com/sagernet/sing-box v0.0.0
|
||||
replace github.com/sagernet/sing-box => ../
|
||||
|
||||
require (
|
||||
github.com/docker/docker v23.0.3+incompatible
|
||||
github.com/docker/docker v20.10.18+incompatible
|
||||
github.com/docker/go-connections v0.4.0
|
||||
github.com/gofrs/uuid/v5 v5.0.0
|
||||
github.com/sagernet/sing v0.2.2
|
||||
github.com/sagernet/sing-shadowsocks v0.2.1-0.20230408141421-e40d6a4e42d4
|
||||
github.com/spyzhov/ajson v0.8.0
|
||||
github.com/gofrs/uuid v4.4.0+incompatible
|
||||
github.com/sagernet/sing v0.2.4-0.20230418025125-f196b4303e31
|
||||
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d
|
||||
github.com/spyzhov/ajson v0.7.1
|
||||
github.com/stretchr/testify v1.8.2
|
||||
go.uber.org/goleak v1.2.1
|
||||
go.uber.org/goleak v1.2.0
|
||||
golang.org/x/net v0.9.0
|
||||
)
|
||||
|
||||
require (
|
||||
berty.tech/go-libtor v1.0.385 // indirect
|
||||
github.com/Dreamacro/clash v1.14.0 // indirect
|
||||
github.com/Dreamacro/clash v1.15.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.5.1 // indirect
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
@@ -36,6 +36,7 @@ require (
|
||||
github.com/go-chi/cors v1.2.1 // indirect
|
||||
github.com/go-chi/render v1.0.2 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/gofrs/uuid/v5 v5.0.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
@@ -70,15 +71,16 @@ require (
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
|
||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 // indirect
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230408004833-5adaf486d440 // indirect
|
||||
github.com/sagernet/sing-shadowtls v0.1.1-0.20230408141548-81d74d2a8661 // indirect
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab // indirect
|
||||
github.com/sagernet/sing-vmess v0.1.4-0.20230409073451-6921c3dd77c7 // indirect
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230418025317-8a132998b322 // indirect
|
||||
github.com/sagernet/sing-shadowtls v0.1.0 // indirect
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230419061614-d744d03d9302 // indirect
|
||||
github.com/sagernet/sing-vmess v0.1.3 // indirect
|
||||
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect
|
||||
github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 // indirect
|
||||
github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 // indirect
|
||||
github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e // indirect
|
||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c // indirect
|
||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||
go.etcd.io/bbolt v1.3.7 // indirect
|
||||
|
||||
46
test/go.sum
46
test/go.sum
@@ -1,8 +1,8 @@
|
||||
berty.tech/go-libtor v1.0.385 h1:RWK94C3hZj6Z2GdvePpHJLnWYobFr3bY/OdUJ5aoEXw=
|
||||
berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+fJEw=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/Dreamacro/clash v1.14.0 h1:ehJ/C/1m9LEjmME72WSE/Y2YqbR3Q54AbjqiRCvtyW4=
|
||||
github.com/Dreamacro/clash v1.14.0/go.mod h1:ia2CU7V713H1QdCqMwOLK9U9V5Ay8X0voj3yQr2tk+I=
|
||||
github.com/Dreamacro/clash v1.15.0 h1:mlpD950VEggXZBNahV66hyKDRxcczkj3vymoAt78KyE=
|
||||
github.com/Dreamacro/clash v1.15.0/go.mod h1:WNH69bN11LiAdgdSr4hpkEuXVMfBbWyhEKMCTx9BtNE=
|
||||
github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY=
|
||||
github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
@@ -25,8 +25,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
|
||||
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v23.0.3+incompatible h1:9GhVsShNWz1hO//9BNg/dpMnZW25KydO4wtVxWAIbho=
|
||||
github.com/docker/docker v23.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v20.10.18+incompatible h1:SN84VYXTBNGn92T/QwIRPlum9zfemfitN7pbsp26WSc=
|
||||
github.com/docker/docker v20.10.18+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
||||
@@ -43,6 +43,8 @@ github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg=
|
||||
github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
|
||||
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
|
||||
github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
@@ -126,18 +128,18 @@ github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byL
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||
github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
||||
github.com/sagernet/sing v0.2.2 h1:qfEdSLuwFgIIkeLOcwVQkVDzKLHtclXb93Ql0zZA+aE=
|
||||
github.com/sagernet/sing v0.2.2/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230408004833-5adaf486d440 h1:VH8/BcOVuApHtS+vKP+khxlGRcXH7KKhgkTDtNynqSQ=
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230408004833-5adaf486d440/go.mod h1:69PNSHyEmXdjf6C+bXBOdr2GQnPeEyWjIzo/MV8gmz8=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.1-0.20230408141421-e40d6a4e42d4 h1:mKpXBBnAhTy9/CvDKqt5cN78LKX8cVUqjoWEXI/g0No=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.1-0.20230408141421-e40d6a4e42d4/go.mod h1:NFEROpOEiLG+lEoSPNpSL2yDyXx6q0OJvwWnEAd40cI=
|
||||
github.com/sagernet/sing-shadowtls v0.1.1-0.20230408141548-81d74d2a8661 h1:QnV79JbJbJGT0MJJfd8o7QMEfRu3eUVKsmahxFMonrc=
|
||||
github.com/sagernet/sing-shadowtls v0.1.1-0.20230408141548-81d74d2a8661/go.mod h1:xCeSRP8cV32aPsY+6BbRdJjyD6q8ufdKwhgqxEbU/3U=
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab h1:a9oeWuPBuIZ70qMhIIH6RrYhp886xN9jJIwsuu4ZFUo=
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230326080954-8848c0e4cbab/go.mod h1:4YxIDEkkCjGXDOTMPw1SXpLmCQUFAWuaQN250oo+928=
|
||||
github.com/sagernet/sing-vmess v0.1.4-0.20230409073451-6921c3dd77c7 h1:ZArINfN+zcHMdZCeRFOm4rO3SWyvYuLg3VhWcA5zonc=
|
||||
github.com/sagernet/sing-vmess v0.1.4-0.20230409073451-6921c3dd77c7/go.mod h1:A9iGfwYmAEmVg8bavkqQ7Hx7nr9Pc2oWMYDwbLIBDjY=
|
||||
github.com/sagernet/sing v0.2.4-0.20230418025125-f196b4303e31 h1:qgq8jeY/rbnY9NwYXByO//AP0ByIxnsKUxQx1tOB3W0=
|
||||
github.com/sagernet/sing v0.2.4-0.20230418025125-f196b4303e31/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230418025317-8a132998b322 h1:UDSeJZ2xB3dj1lySnM5LpF48dGlphGstw2BqtkJwcZI=
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230418025317-8a132998b322/go.mod h1:2wjxSr1Gbecq9A0ESA9cnR399tQTcpCZEOGytekb+qI=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d h1:UUxtLujzp5jmtOXqXpSOGvHwHSZcBveKVDzRJ4GlnFU=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230418025154-6114beeeba6d/go.mod h1:Co3PJXcaZoLwHGBfT0rbSnn9C7ywc41zVYWtDeoeI/Q=
|
||||
github.com/sagernet/sing-shadowtls v0.1.0 h1:05MYce8aR5xfKIn+y7xRFsdKhKt44QZTSEQW+lG5IWQ=
|
||||
github.com/sagernet/sing-shadowtls v0.1.0/go.mod h1:Kn1VUIprdkwCgkS6SXYaLmIpKzQbqBIKJBMY+RvBhYc=
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230419061614-d744d03d9302 h1:aPb0T2HQRTG2t7fEwLvFLZSXmhmnBh+SMs2NufhmrsI=
|
||||
github.com/sagernet/sing-tun v0.1.4-0.20230419061614-d744d03d9302/go.mod h1:bvcVzlf9q9dgxt8qKluW+zOXCFoN1+SpBG3sHTq8/9Q=
|
||||
github.com/sagernet/sing-vmess v0.1.3 h1:q/+tsF46dvvapL6CpQBgPHJ6nQrDUZqEtLHCbsjO7iM=
|
||||
github.com/sagernet/sing-vmess v0.1.3/go.mod h1:GVXqAHwe9U21uS+Voh4YBIrADQyE4F9v0ayGSixSQAE=
|
||||
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as=
|
||||
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=
|
||||
@@ -149,8 +151,10 @@ 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/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/spyzhov/ajson v0.8.0 h1:sFXyMbi4Y/BKjrsfkUZHSjA2JM1184enheSjjoT/zCc=
|
||||
github.com/spyzhov/ajson v0.8.0/go.mod h1:63V+CGM6f1Bu/p4nLIN8885ojBdt88TbLoSFzyqMuVA=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/spyzhov/ajson v0.7.1 h1:1MDIlPc6x0zjNtpa7tDzRAyFAvRX+X8ZsvtYz5lZg6A=
|
||||
github.com/spyzhov/ajson v0.7.1/go.mod h1:63V+CGM6f1Bu/p4nLIN8885ojBdt88TbLoSFzyqMuVA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
@@ -174,8 +178,8 @@ go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
||||
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
|
||||
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
|
||||
go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
|
||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
||||
@@ -191,6 +195,7 @@ golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
|
||||
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
@@ -225,6 +230,7 @@ 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-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
||||
@@ -141,6 +141,95 @@ func TestVLESSVisionReality(t *testing.T) {
|
||||
testSuit(t, clientPort, testPort)
|
||||
}
|
||||
|
||||
func TestVLESSVisionRealityPlain(t *testing.T) {
|
||||
userUUID := newUUID()
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
||||
ListenPort: clientPort,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: C.TypeVLESS,
|
||||
VLESSOptions: option.VLESSInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
||||
ListenPort: serverPort,
|
||||
},
|
||||
Users: []option.VLESSUser{
|
||||
{
|
||||
Name: "sekai",
|
||||
UUID: userUUID.String(),
|
||||
Flow: vless.FlowVision,
|
||||
},
|
||||
},
|
||||
TLS: &option.InboundTLSOptions{
|
||||
Enabled: true,
|
||||
ServerName: "google.com",
|
||||
Reality: &option.InboundRealityOptions{
|
||||
Enabled: true,
|
||||
Handshake: option.InboundRealityHandshakeOptions{
|
||||
ServerOptions: option.ServerOptions{
|
||||
Server: "google.com",
|
||||
ServerPort: 443,
|
||||
},
|
||||
},
|
||||
ShortID: []string{"0123456789abcdef"},
|
||||
PrivateKey: "UuMBgl7MXTPx9inmQp2UC7Jcnwc6XYbwDNebonM-FCc",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Outbounds: []option.Outbound{
|
||||
{
|
||||
Type: C.TypeDirect,
|
||||
},
|
||||
{
|
||||
Type: C.TypeVLESS,
|
||||
Tag: "vless-out",
|
||||
VLESSOptions: option.VLESSOutboundOptions{
|
||||
ServerOptions: option.ServerOptions{
|
||||
Server: "127.0.0.1",
|
||||
ServerPort: serverPort,
|
||||
},
|
||||
UUID: userUUID.String(),
|
||||
Flow: vless.FlowVision,
|
||||
TLS: &option.OutboundTLSOptions{
|
||||
Enabled: true,
|
||||
ServerName: "google.com",
|
||||
Reality: &option.OutboundRealityOptions{
|
||||
Enabled: true,
|
||||
ShortID: "0123456789abcdef",
|
||||
PublicKey: "jNXHt1yRo0vDuchQlIP6Z0ZvjT3KtzVI-T4E7RoLJS0",
|
||||
},
|
||||
UTLS: &option.OutboundUTLSOptions{
|
||||
Enabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Route: &option.RouteOptions{
|
||||
Rules: []option.Rule{
|
||||
{
|
||||
DefaultOptions: option.DefaultRule{
|
||||
Inbound: []string{"mixed-in"},
|
||||
Outbound: "vless-out",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
testSuit(t, clientPort, testPort)
|
||||
}
|
||||
|
||||
func TestVLESSRealityTransport(t *testing.T) {
|
||||
t.Run("grpc", func(t *testing.T) {
|
||||
testVLESSRealityTransport(t, &option.V2RayTransportOptions{
|
||||
@@ -160,8 +249,6 @@ func TestVLESSRealityTransport(t *testing.T) {
|
||||
}
|
||||
|
||||
func testVLESSRealityTransport(t *testing.T, transport *option.V2RayTransportOptions) {
|
||||
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
||||
|
||||
userUUID := newUUID()
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
@@ -206,52 +293,11 @@ func testVLESSRealityTransport(t *testing.T, transport *option.V2RayTransportOpt
|
||||
Transport: transport,
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: C.TypeTrojan,
|
||||
Tag: "trojan",
|
||||
TrojanOptions: option.TrojanInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
||||
ListenPort: otherPort,
|
||||
},
|
||||
Users: []option.TrojanUser{
|
||||
{
|
||||
Name: "sekai",
|
||||
Password: userUUID.String(),
|
||||
},
|
||||
},
|
||||
TLS: &option.InboundTLSOptions{
|
||||
Enabled: true,
|
||||
ServerName: "example.org",
|
||||
CertificatePath: certPem,
|
||||
KeyPath: keyPem,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Outbounds: []option.Outbound{
|
||||
{
|
||||
Type: C.TypeDirect,
|
||||
},
|
||||
{
|
||||
Type: C.TypeTrojan,
|
||||
Tag: "trojan-out",
|
||||
TrojanOptions: option.TrojanOutboundOptions{
|
||||
ServerOptions: option.ServerOptions{
|
||||
Server: "127.0.0.1",
|
||||
ServerPort: otherPort,
|
||||
},
|
||||
Password: userUUID.String(),
|
||||
TLS: &option.OutboundTLSOptions{
|
||||
Enabled: true,
|
||||
ServerName: "example.org",
|
||||
CertificatePath: certPem,
|
||||
},
|
||||
DialerOptions: option.DialerOptions{
|
||||
Detour: "vless-out",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: C.TypeVLESS,
|
||||
Tag: "vless-out",
|
||||
@@ -282,7 +328,7 @@ func testVLESSRealityTransport(t *testing.T, transport *option.V2RayTransportOpt
|
||||
{
|
||||
DefaultOptions: option.DefaultRule{
|
||||
Inbound: []string{"mixed-in"},
|
||||
Outbound: "trojan-out",
|
||||
Outbound: "vless-out",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
59
test/udpnat_test.go
Normal file
59
test/udpnat_test.go
Normal file
@@ -0,0 +1,59 @@
|
||||
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
|
||||
}
|
||||
@@ -119,7 +119,7 @@ func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg,
|
||||
func (t *Transport) fetchInterface() (*net.Interface, error) {
|
||||
interfaceName := t.interfaceName
|
||||
if t.autoInterface {
|
||||
if t.router.NetworkMonitor() == nil {
|
||||
if t.router.InterfaceMonitor() == nil {
|
||||
return nil, E.New("missing monitor for auto DHCP, set route.auto_detect_interface")
|
||||
}
|
||||
interfaceName = t.router.InterfaceMonitor().DefaultInterfaceName(netip.Addr{})
|
||||
|
||||
@@ -535,6 +535,10 @@ func (c *PacketConn) SetWriteDeadline(t time.Time) error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (c *PacketConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *PacketConn) Read(b []byte) (n int, err error) {
|
||||
return 0, os.ErrInvalid
|
||||
}
|
||||
|
||||
@@ -32,10 +32,10 @@ type TLSObfs struct {
|
||||
func (to *TLSObfs) read(b []byte, discardN int) (int, error) {
|
||||
buf := B.Get(discardN)
|
||||
_, err := io.ReadFull(to.Conn, buf)
|
||||
B.Put(buf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
B.Put(buf)
|
||||
|
||||
sizeBuf := make([]byte, 2)
|
||||
_, err = io.ReadFull(to.Conn, sizeBuf)
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
"github.com/sagernet/sing/common/bufio/deadline"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
@@ -55,7 +53,7 @@ func newMuxConnection0(ctx context.Context, stream net.Conn, metadata M.Metadata
|
||||
case CommandTCP:
|
||||
return handler.NewConnection(ctx, stream, metadata)
|
||||
case CommandUDP:
|
||||
return handler.NewPacketConnection(ctx, deadline.NewPacketConn(bufio.NewNetPacketConn(&PacketConn{stream})), metadata)
|
||||
return handler.NewPacketConnection(ctx, &PacketConn{stream}, metadata)
|
||||
default:
|
||||
return E.New("unknown command ", command)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
"github.com/sagernet/sing/common/bufio/deadline"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
@@ -107,7 +106,7 @@ func (s *Service[K]) NewConnection(ctx context.Context, conn net.Conn, metadata
|
||||
case CommandTCP:
|
||||
return s.handler.NewConnection(ctx, conn, metadata)
|
||||
case CommandUDP:
|
||||
return s.handler.NewPacketConnection(ctx, deadline.NewPacketConn(bufio.NewNetPacketConn(&PacketConn{conn})), metadata)
|
||||
return s.handler.NewPacketConnection(ctx, &PacketConn{conn}, metadata)
|
||||
// case CommandMux:
|
||||
default:
|
||||
return HandleMuxConnection(ctx, conn, metadata, s.handler)
|
||||
@@ -137,3 +136,11 @@ func (c *PacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) er
|
||||
func (c *PacketConn) FrontHeadroom() int {
|
||||
return M.MaxSocksaddrLength + 4
|
||||
}
|
||||
|
||||
func (c *PacketConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *PacketConn) Upstream() any {
|
||||
return c.Conn
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/bufio/deadline"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
@@ -110,5 +109,5 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
|
||||
cancel(err)
|
||||
return nil, err
|
||||
}
|
||||
return deadline.NewConn(NewGRPCConn(stream, cancel)), nil
|
||||
return NewGRPCConn(stream, cancel), nil
|
||||
}
|
||||
|
||||
@@ -81,6 +81,10 @@ func (c *GRPCConn) SetWriteDeadline(t time.Time) error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (c *GRPCConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *GRPCConn) Upstream() any {
|
||||
return c.GunService
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/bufio/deadline"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
@@ -67,7 +66,7 @@ func (s *Server) Tun(server GunService_TunServer) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
go s.handler.NewConnection(ctx, deadline.NewConn(conn), metadata)
|
||||
go s.handler.NewConnection(ctx, conn, metadata)
|
||||
<-ctx.Done()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/v2rayhttp"
|
||||
"github.com/sagernet/sing/common/bufio/deadline"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
|
||||
@@ -93,7 +92,7 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
|
||||
conn.setup(nil, err)
|
||||
}
|
||||
}()
|
||||
return deadline.NewConn(conn), nil
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (c *Client) Close() error {
|
||||
|
||||
@@ -156,3 +156,7 @@ func (c *GunConn) SetReadDeadline(t time.Time) error {
|
||||
func (c *GunConn) SetWriteDeadline(t time.Time) error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (c *GunConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/transport/v2rayhttp"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/bufio/deadline"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
@@ -82,7 +81,7 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||
var metadata M.Metadata
|
||||
metadata.Source = sHttp.SourceAddress(request)
|
||||
conn := v2rayhttp.NewHTTP2Wrapper(newGunConn(request.Body, writer, writer.(http.Flusher)))
|
||||
s.handler.NewConnection(request.Context(), deadline.NewConn(conn), metadata)
|
||||
s.handler.NewConnection(request.Context(), conn, metadata)
|
||||
conn.CloseWrapper()
|
||||
}
|
||||
|
||||
|
||||
@@ -182,32 +182,21 @@ func (c *HTTP2Conn) RemoteAddr() net.Addr {
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (c *HTTP2Conn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
type ServerHTTPConn struct {
|
||||
HTTP2Conn
|
||||
flusher http.Flusher
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common/bufio/deadline"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
@@ -79,11 +78,11 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
|
||||
if c.maxEarlyData <= 0 {
|
||||
conn, response, err := c.dialer.DialContext(ctx, c.uri, c.headers)
|
||||
if err == nil {
|
||||
return deadline.NewConn(&WebsocketConn{Conn: conn, Writer: NewWriter(conn, false)}), nil
|
||||
return &WebsocketConn{Conn: conn, Writer: NewWriter(conn, false)}, nil
|
||||
}
|
||||
return nil, wrapDialError(response, err)
|
||||
} else {
|
||||
return deadline.NewConn(&EarlyWebsocketConn{Client: c, ctx: ctx, create: make(chan struct{})}), nil
|
||||
return &EarlyWebsocketConn{Client: c, ctx: ctx, create: make(chan struct{})}, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -77,6 +77,10 @@ func (c *WebsocketConn) SetWriteDeadline(t time.Time) error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (c *WebsocketConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *WebsocketConn) Upstream() any {
|
||||
return c.Conn.NetConn()
|
||||
}
|
||||
@@ -214,6 +218,10 @@ func (c *EarlyWebsocketConn) SetWriteDeadline(t time.Time) error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (c *EarlyWebsocketConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *EarlyWebsocketConn) Upstream() any {
|
||||
return common.PtrOrNil(c.conn)
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
"github.com/sagernet/sing/common/bufio/deadline"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
@@ -108,7 +107,7 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||
if len(earlyData) > 0 {
|
||||
conn = bufio.NewCachedConn(conn, buf.As(earlyData))
|
||||
}
|
||||
s.handler.NewConnection(request.Context(), deadline.NewConn(conn), metadata)
|
||||
s.handler.NewConnection(request.Context(), conn, metadata)
|
||||
}
|
||||
|
||||
func (s *Server) fallbackRequest(ctx context.Context, writer http.ResponseWriter, request *http.Request, statusCode int, err error) {
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
"github.com/sagernet/sing/common/bufio/deadline"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
@@ -37,105 +36,156 @@ func NewClient(userId string, flow string, logger logger.Logger) (*Client, error
|
||||
return &Client{user, flow, logger}, nil
|
||||
}
|
||||
|
||||
func (c *Client) prepareConn(conn net.Conn) (net.Conn, error) {
|
||||
func (c *Client) prepareConn(conn net.Conn, tlsConn net.Conn) (net.Conn, error) {
|
||||
if c.flow == FlowVision {
|
||||
vConn, err := NewVisionConn(conn, c.key, c.logger)
|
||||
protocolConn, err := NewVisionConn(conn, tlsConn, c.key, c.logger)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "initialize vision")
|
||||
}
|
||||
conn = vConn
|
||||
conn = protocolConn
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (c *Client) DialConn(conn net.Conn, destination M.Socksaddr) (net.Conn, error) {
|
||||
vConn, err := c.prepareConn(conn)
|
||||
remoteConn := NewConn(conn, c.key, vmess.CommandTCP, destination, c.flow)
|
||||
protocolConn, err := c.prepareConn(remoteConn, conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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))
|
||||
return protocolConn, common.Error(remoteConn.Write(nil))
|
||||
}
|
||||
|
||||
func (c *Client) DialEarlyConn(conn net.Conn, destination M.Socksaddr) (net.Conn, error) {
|
||||
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
|
||||
return c.prepareConn(NewConn(conn, c.key, vmess.CommandTCP, destination, c.flow), conn)
|
||||
}
|
||||
|
||||
func (c *Client) DialPacketConn(conn net.Conn, destination M.Socksaddr) (vmess.PacketConn, error) {
|
||||
func (c *Client) DialPacketConn(conn net.Conn, destination M.Socksaddr) (*PacketConn, error) {
|
||||
serverConn := &PacketConn{Conn: conn, key: c.key, destination: destination, flow: c.flow}
|
||||
return bufio.NewBindPacketConn(deadline.NewPacketConn(bufio.NewPacketConn(serverConn)), destination), common.Error(serverConn.Write(nil))
|
||||
return serverConn, common.Error(serverConn.Write(nil))
|
||||
}
|
||||
|
||||
func (c *Client) DialEarlyPacketConn(conn net.Conn, destination M.Socksaddr) (vmess.PacketConn, error) {
|
||||
return bufio.NewBindPacketConn(deadline.NewPacketConn(bufio.NewPacketConn(&PacketConn{Conn: conn, key: c.key, destination: destination, flow: c.flow})), destination), nil
|
||||
func (c *Client) DialEarlyPacketConn(conn net.Conn, destination M.Socksaddr) (*PacketConn, error) {
|
||||
return &PacketConn{Conn: conn, key: c.key, destination: destination, flow: c.flow}, nil
|
||||
}
|
||||
|
||||
func (c *Client) DialXUDPPacketConn(conn net.Conn, destination M.Socksaddr) (vmess.PacketConn, error) {
|
||||
serverConn := &Conn{Conn: conn, protocolConn: conn, key: c.key, command: vmess.CommandMux, destination: destination, flow: c.flow}
|
||||
err := common.Error(serverConn.Write(nil))
|
||||
remoteConn := NewConn(conn, c.key, vmess.CommandTCP, destination, c.flow)
|
||||
protocolConn, err := c.prepareConn(remoteConn, conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bufio.NewBindPacketConn(deadline.NewPacketConn(vmess.NewXUDPConn(serverConn, destination)), destination), nil
|
||||
return vmess.NewXUDPConn(protocolConn, destination), common.Error(remoteConn.Write(nil))
|
||||
}
|
||||
|
||||
func (c *Client) DialEarlyXUDPPacketConn(conn net.Conn, destination M.Socksaddr) (vmess.PacketConn, error) {
|
||||
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
|
||||
remoteConn := NewConn(conn, c.key, vmess.CommandMux, destination, c.flow)
|
||||
protocolConn, err := c.prepareConn(remoteConn, conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return vmess.NewXUDPConn(protocolConn, destination), common.Error(remoteConn.Write(nil))
|
||||
}
|
||||
|
||||
var _ N.EarlyConn = (*Conn)(nil)
|
||||
var (
|
||||
_ N.EarlyConn = (*Conn)(nil)
|
||||
_ N.VectorisedWriter = (*Conn)(nil)
|
||||
)
|
||||
|
||||
type Conn struct {
|
||||
net.Conn
|
||||
protocolConn net.Conn
|
||||
key [16]byte
|
||||
command byte
|
||||
destination M.Socksaddr
|
||||
flow string
|
||||
N.ExtendedConn
|
||||
writer N.VectorisedWriter
|
||||
request Request
|
||||
requestWritten 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 {
|
||||
return !c.requestWritten
|
||||
}
|
||||
|
||||
func (c *Conn) Read(b []byte) (n int, err error) {
|
||||
if !c.responseRead {
|
||||
err = ReadResponse(c.Conn)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.responseRead = true
|
||||
func (c *Conn) FrontHeadroom() int {
|
||||
if c.requestWritten {
|
||||
return 0
|
||||
}
|
||||
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)
|
||||
return RequestLen(c.request)
|
||||
}
|
||||
|
||||
func (c *Conn) Upstream() any {
|
||||
return c.Conn
|
||||
return c.ExtendedConn
|
||||
}
|
||||
|
||||
type PacketConn struct {
|
||||
@@ -214,6 +264,10 @@ func (c *PacketConn) FrontHeadroom() int {
|
||||
return 2
|
||||
}
|
||||
|
||||
func (c *PacketConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *PacketConn) Upstream() any {
|
||||
return c.Conn
|
||||
}
|
||||
|
||||
@@ -11,10 +11,12 @@ var (
|
||||
tlsClientHandShakeStart = []byte{0x16, 0x03}
|
||||
tlsServerHandShakeStart = []byte{0x16, 0x03, 0x03}
|
||||
tlsApplicationDataStart = []byte{0x17, 0x03, 0x03}
|
||||
)
|
||||
|
||||
commandPaddingContinue byte = 0
|
||||
commandPaddingEnd byte = 1
|
||||
commandPaddingDirect byte = 2
|
||||
const (
|
||||
commandPaddingContinue byte = iota
|
||||
commandPaddingEnd
|
||||
commandPaddingDirect
|
||||
)
|
||||
|
||||
var tls13CipherSuiteDic = map[uint16]string{
|
||||
|
||||
@@ -128,7 +128,7 @@ func WriteRequest(writer io.Writer, request Request, payload []byte) error {
|
||||
requestLen += 1 // protobuf length
|
||||
|
||||
var addonsLen int
|
||||
if request.Command == vmess.CommandTCP && request.Flow != "" {
|
||||
if request.Flow != "" {
|
||||
addonsLen += 1 // protobuf header
|
||||
addonsLen += UvarintLen(uint64(len(request.Flow)))
|
||||
addonsLen += len(request.Flow)
|
||||
@@ -165,6 +165,62 @@ func WriteRequest(writer io.Writer, request Request, payload []byte) error {
|
||||
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 {
|
||||
var requestLen int
|
||||
requestLen += 1 // version
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
"github.com/sagernet/sing/common/bufio/deadline"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
@@ -69,30 +68,32 @@ func (s *Service[T]) NewConnection(ctx context.Context, conn net.Conn, metadata
|
||||
metadata.Destination = request.Destination
|
||||
|
||||
userFlow := s.userFlow[user]
|
||||
|
||||
var responseWriter io.Writer
|
||||
if request.Command == vmess.CommandTCP {
|
||||
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.Flow == FlowVision && request.Command == vmess.NetworkUDP {
|
||||
return E.New(FlowVision, " flow does not support UDP")
|
||||
} else if request.Flow != userFlow {
|
||||
return E.New("flow mismatch: expected ", flowName(userFlow), ", but got ", flowName(request.Flow))
|
||||
}
|
||||
|
||||
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 {
|
||||
case vmess.CommandTCP:
|
||||
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)
|
||||
return s.handler.NewConnection(ctx, conn, metadata)
|
||||
case vmess.CommandMux:
|
||||
return vmess.HandleMuxConnection(ctx, &serverConn{Conn: conn, responseWriter: responseWriter}, s.handler)
|
||||
return vmess.HandleMuxConnection(ctx, conn, s.handler)
|
||||
default:
|
||||
return E.New("unknown command: ", request.Command)
|
||||
}
|
||||
@@ -105,34 +106,70 @@ func flowName(value string) string {
|
||||
return value
|
||||
}
|
||||
|
||||
var _ N.VectorisedWriter = (*serverConn)(nil)
|
||||
|
||||
type serverConn struct {
|
||||
net.Conn
|
||||
responseWriter io.Writer
|
||||
N.ExtendedConn
|
||||
writer N.VectorisedWriter
|
||||
responseWritten bool
|
||||
}
|
||||
|
||||
func (c *serverConn) Read(b []byte) (n int, err error) {
|
||||
return c.Conn.Read(b)
|
||||
return c.ExtendedConn.Read(b)
|
||||
}
|
||||
|
||||
func (c *serverConn) Write(b []byte) (n int, err error) {
|
||||
if !c.responseWritten {
|
||||
if c.responseWriter == nil {
|
||||
_, err = bufio.WriteVectorised(bufio.NewVectorisedWriter(c.Conn), [][]byte{{Version, 0}, 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
|
||||
_, err = bufio.WriteVectorised(c.writer, [][]byte{{Version, 0}, b})
|
||||
if err == nil {
|
||||
n = len(b)
|
||||
}
|
||||
c.responseWritten = true
|
||||
return
|
||||
}
|
||||
return c.Conn.Write(b)
|
||||
return c.ExtendedConn.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 {
|
||||
|
||||
@@ -56,12 +56,12 @@ type VisionConn struct {
|
||||
withinPaddingBuffers bool
|
||||
remainingContent int
|
||||
remainingPadding int
|
||||
currentCommand int
|
||||
currentCommand byte
|
||||
directRead bool
|
||||
remainingReader io.Reader
|
||||
}
|
||||
|
||||
func NewVisionConn(conn net.Conn, userUUID [16]byte, logger logger.Logger) (*VisionConn, error) {
|
||||
func NewVisionConn(conn net.Conn, tlsConn net.Conn, userUUID [16]byte, logger logger.Logger) (*VisionConn, error) {
|
||||
var (
|
||||
loaded bool
|
||||
reflectType reflect.Type
|
||||
@@ -69,7 +69,7 @@ func NewVisionConn(conn net.Conn, userUUID [16]byte, logger logger.Logger) (*Vis
|
||||
netConn net.Conn
|
||||
)
|
||||
for _, tlsCreator := range tlsRegistry {
|
||||
loaded, netConn, reflectType, reflectPointer = tlsCreator(conn)
|
||||
loaded, netConn, reflectType, reflectPointer = tlsCreator(tlsConn)
|
||||
if loaded {
|
||||
break
|
||||
}
|
||||
@@ -103,6 +103,7 @@ func (c *VisionConn) Read(p []byte) (n int, err error) {
|
||||
if c.remainingReader != nil {
|
||||
n, err = c.remainingReader.Read(p)
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
c.remainingReader = nil
|
||||
}
|
||||
if n > 0 {
|
||||
@@ -113,6 +114,7 @@ func (c *VisionConn) Read(p []byte) (n int, err error) {
|
||||
return c.netConn.Read(p)
|
||||
}
|
||||
var bufferBytes []byte
|
||||
var chunkBuffer *buf.Buffer
|
||||
if len(p) > xrayChunkSize {
|
||||
n, err = c.Conn.Read(p)
|
||||
if err != nil {
|
||||
@@ -120,21 +122,26 @@ func (c *VisionConn) Read(p []byte) (n int, err error) {
|
||||
}
|
||||
bufferBytes = p[:n]
|
||||
} else {
|
||||
buffer, err := c.reader.ReadChunk()
|
||||
chunkBuffer, err = c.reader.ReadChunk()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer buffer.FullReset()
|
||||
bufferBytes = buffer.Bytes()
|
||||
bufferBytes = chunkBuffer.Bytes()
|
||||
}
|
||||
if c.withinPaddingBuffers || c.numberOfPacketToFilter > 0 {
|
||||
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.currentCommand == 1 {
|
||||
if c.currentCommand == commandPaddingEnd {
|
||||
c.withinPaddingBuffers = false
|
||||
c.remainingContent = -1
|
||||
c.remainingPadding = -1
|
||||
} else if c.currentCommand == 2 {
|
||||
} else if c.currentCommand == commandPaddingDirect {
|
||||
c.withinPaddingBuffers = false
|
||||
c.directRead = true
|
||||
|
||||
@@ -142,17 +149,17 @@ func (c *VisionConn) Read(p []byte) (n int, err error) {
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
buffers = append(buffers, inputBuffer)
|
||||
buffers = append(buffers, buf.As(inputBuffer))
|
||||
|
||||
rawInputBuffer, err := io.ReadAll(c.rawInput)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
buffers = append(buffers, rawInputBuffer)
|
||||
buffers = append(buffers, buf.As(rawInputBuffer))
|
||||
|
||||
c.logger.Trace("XtlsRead readV")
|
||||
} else if c.currentCommand == 0 {
|
||||
} else if c.currentCommand == commandPaddingContinue {
|
||||
c.withinPaddingBuffers = true
|
||||
} else {
|
||||
return 0, E.New("unknown command ", c.currentCommand)
|
||||
@@ -163,14 +170,18 @@ func (c *VisionConn) Read(p []byte) (n int, err error) {
|
||||
c.withinPaddingBuffers = false
|
||||
}
|
||||
if c.numberOfPacketToFilter > 0 {
|
||||
c.filterTLS(buffers)
|
||||
c.filterTLS(buf.ToSliceMulti(buffers))
|
||||
}
|
||||
c.remainingReader = io.MultiReader(common.Map(buffers, func(it []byte) io.Reader { return bytes.NewReader(it) })...)
|
||||
c.remainingReader = io.MultiReader(common.Map(buffers, func(it *buf.Buffer) io.Reader { return it })...)
|
||||
return c.Read(p)
|
||||
} else {
|
||||
if c.numberOfPacketToFilter > 0 {
|
||||
c.filterTLS([][]byte{bufferBytes})
|
||||
}
|
||||
if chunkBuffer != nil {
|
||||
n = copy(p, bufferBytes)
|
||||
chunkBuffer.Advance(n)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -310,7 +321,7 @@ func (c *VisionConn) padding(buffer *buf.Buffer, command byte) *buf.Buffer {
|
||||
return newBuffer
|
||||
}
|
||||
|
||||
func (c *VisionConn) unPadding(buffer []byte) [][]byte {
|
||||
func (c *VisionConn) unPadding(buffer []byte) []*buf.Buffer {
|
||||
var bufferIndex int
|
||||
if c.remainingContent == -1 && c.remainingPadding == -1 {
|
||||
if len(buffer) >= 21 && bytes.Equal(c.userUUID[:], buffer[:16]) {
|
||||
@@ -321,17 +332,17 @@ func (c *VisionConn) unPadding(buffer []byte) [][]byte {
|
||||
}
|
||||
}
|
||||
if c.remainingContent == -1 && c.remainingPadding == -1 {
|
||||
return [][]byte{buffer}
|
||||
return []*buf.Buffer{buf.As(buffer)}
|
||||
}
|
||||
var buffers [][]byte
|
||||
var buffers []*buf.Buffer
|
||||
for bufferIndex < len(buffer) {
|
||||
if c.remainingContent <= 0 && c.remainingPadding <= 0 {
|
||||
if c.currentCommand == 1 {
|
||||
buffers = append(buffers, buffer[bufferIndex:])
|
||||
buffers = append(buffers, buf.As(buffer[bufferIndex:]))
|
||||
break
|
||||
} else {
|
||||
paddingInfo := buffer[bufferIndex : bufferIndex+5]
|
||||
c.currentCommand = int(paddingInfo[0])
|
||||
c.currentCommand = paddingInfo[0]
|
||||
c.remainingContent = int(paddingInfo[1])<<8 | int(paddingInfo[2])
|
||||
c.remainingPadding = int(paddingInfo[3])<<8 | int(paddingInfo[4])
|
||||
bufferIndex += 5
|
||||
@@ -342,7 +353,7 @@ func (c *VisionConn) unPadding(buffer []byte) [][]byte {
|
||||
if end > len(buffer)-bufferIndex {
|
||||
end = len(buffer) - bufferIndex
|
||||
}
|
||||
buffers = append(buffers, buffer[bufferIndex:bufferIndex+end])
|
||||
buffers = append(buffers, buf.As(buffer[bufferIndex:bufferIndex+end]))
|
||||
c.remainingContent -= end
|
||||
bufferIndex += end
|
||||
} else {
|
||||
@@ -360,6 +371,10 @@ func (c *VisionConn) unPadding(buffer []byte) [][]byte {
|
||||
return buffers
|
||||
}
|
||||
|
||||
func (c *VisionConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *VisionConn) Upstream() any {
|
||||
return c.Conn
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/wireguard-go/conn"
|
||||
@@ -18,6 +18,7 @@ var _ conn.Bind = (*ClientBind)(nil)
|
||||
|
||||
type ClientBind struct {
|
||||
ctx context.Context
|
||||
errorHandler E.Handler
|
||||
dialer N.Dialer
|
||||
reservedForEndpoint map[M.Socksaddr][3]uint8
|
||||
connAccess sync.Mutex
|
||||
@@ -28,9 +29,10 @@ type ClientBind struct {
|
||||
reserved [3]uint8
|
||||
}
|
||||
|
||||
func NewClientBind(ctx context.Context, dialer N.Dialer, isConnect bool, connectAddr M.Socksaddr, reserved [3]uint8) *ClientBind {
|
||||
func NewClientBind(ctx context.Context, errorHandler E.Handler, dialer N.Dialer, isConnect bool, connectAddr M.Socksaddr, reserved [3]uint8) *ClientBind {
|
||||
return &ClientBind{
|
||||
ctx: ctx,
|
||||
errorHandler: errorHandler,
|
||||
dialer: dialer,
|
||||
reservedForEndpoint: make(map[M.Socksaddr][3]uint8),
|
||||
isConnect: isConnect,
|
||||
@@ -67,10 +69,10 @@ func (c *ClientBind) connect() (*wireConn, error) {
|
||||
if c.isConnect {
|
||||
udpConn, err := c.dialer.DialContext(c.ctx, N.NetworkUDP, c.connectAddr)
|
||||
if err != nil {
|
||||
return nil, &wireError{err}
|
||||
return nil, err
|
||||
}
|
||||
c.conn = &wireConn{
|
||||
NetPacketConn: &bufio.UnbindPacketConn{
|
||||
PacketConn: &bufio.UnbindPacketConn{
|
||||
ExtendedConn: bufio.NewExtendedConn(udpConn),
|
||||
Addr: c.connectAddr,
|
||||
},
|
||||
@@ -79,11 +81,11 @@ func (c *ClientBind) connect() (*wireConn, error) {
|
||||
} else {
|
||||
udpConn, err := c.dialer.ListenPacket(c.ctx, M.Socksaddr{Addr: netip.IPv4Unspecified()})
|
||||
if err != nil {
|
||||
return nil, &wireError{err}
|
||||
return nil, err
|
||||
}
|
||||
c.conn = &wireConn{
|
||||
NetPacketConn: bufio.NewPacketConn(udpConn),
|
||||
done: make(chan struct{}),
|
||||
PacketConn: bufio.NewPacketConn(udpConn),
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
return c.conn, nil
|
||||
@@ -102,30 +104,31 @@ func (c *ClientBind) Open(port uint16) (fns []conn.ReceiveFunc, actualPort uint1
|
||||
func (c *ClientBind) receive(b []byte) (n int, ep conn.Endpoint, err error) {
|
||||
udpConn, err := c.connect()
|
||||
if err != nil {
|
||||
err = &wireError{err}
|
||||
select {
|
||||
case <-c.done:
|
||||
return
|
||||
default:
|
||||
}
|
||||
c.errorHandler.NewError(context.Background(), E.Cause(err, "connect to server"))
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
buffer := buf.With(b)
|
||||
destination, err := udpConn.ReadPacket(buffer)
|
||||
n, addr, err := udpConn.ReadFrom(b)
|
||||
if err != nil {
|
||||
udpConn.Close()
|
||||
select {
|
||||
case <-c.done:
|
||||
default:
|
||||
err = &wireError{err}
|
||||
c.errorHandler.NewError(context.Background(), E.Cause(err, "read packet"))
|
||||
}
|
||||
return
|
||||
}
|
||||
n = buffer.Len()
|
||||
if buffer.Start() > 0 {
|
||||
copy(b, buffer.Bytes())
|
||||
}
|
||||
if n > 3 {
|
||||
b[1] = 0
|
||||
b[2] = 0
|
||||
b[3] = 0
|
||||
}
|
||||
ep = Endpoint(destination)
|
||||
ep = Endpoint(M.SocksaddrFromNet(addr))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -167,7 +170,7 @@ func (c *ClientBind) Send(b []byte, ep conn.Endpoint) error {
|
||||
b[2] = reserved[1]
|
||||
b[3] = reserved[2]
|
||||
}
|
||||
err = udpConn.WritePacket(buf.As(b), destination)
|
||||
_, err = udpConn.WriteTo(b, destination)
|
||||
if err != nil {
|
||||
udpConn.Close()
|
||||
}
|
||||
@@ -179,7 +182,7 @@ func (c *ClientBind) ParseEndpoint(s string) (conn.Endpoint, error) {
|
||||
}
|
||||
|
||||
type wireConn struct {
|
||||
N.NetPacketConn
|
||||
net.PacketConn
|
||||
access sync.Mutex
|
||||
done chan struct{}
|
||||
}
|
||||
@@ -192,7 +195,7 @@ func (w *wireConn) Close() error {
|
||||
return net.ErrClosed
|
||||
default:
|
||||
}
|
||||
w.NetPacketConn.Close()
|
||||
w.PacketConn.Close()
|
||||
close(w.done)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
package wireguard
|
||||
|
||||
import "net"
|
||||
|
||||
type wireError struct {
|
||||
cause error
|
||||
}
|
||||
|
||||
func (w *wireError) Error() string {
|
||||
return w.cause.Error()
|
||||
}
|
||||
|
||||
func (w *wireError) Timeout() bool {
|
||||
if cause, causeNet := w.cause.(net.Error); causeNet {
|
||||
return cause.Timeout()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (w *wireError) Temporary() bool {
|
||||
return true
|
||||
}
|
||||
Reference in New Issue
Block a user