Add TCP MultiPath support

This commit is contained in:
世界 2023-08-08 16:14:03 +08:00
parent 81b847faca
commit 1019ecfdcf
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
33 changed files with 225 additions and 43 deletions

View file

@ -26,7 +26,7 @@ type DefaultDialer struct {
udpAddr6 string udpAddr6 string
} }
func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDialer { func NewDefault(router adapter.Router, options option.DialerOptions) (*DefaultDialer, error) {
var dialer net.Dialer var dialer net.Dialer
var listener net.ListenConfig var listener net.ListenConfig
if options.BindInterface != "" { if options.BindInterface != "" {
@ -93,6 +93,12 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
udpDialer6.LocalAddr = &net.UDPAddr{IP: bindAddr.AsSlice()} udpDialer6.LocalAddr = &net.UDPAddr{IP: bindAddr.AsSlice()}
udpAddr6 = M.SocksaddrFrom(bindAddr, 0).String() udpAddr6 = M.SocksaddrFrom(bindAddr, 0).String()
} }
if options.TCPMultiPath {
if !multipathTCPAvailable {
return nil, E.New("MultiPath TCP requires go1.21, please recompile your binary.")
}
setMultiPathTCP(&dialer4)
}
return &DefaultDialer{ return &DefaultDialer{
tfo.Dialer{Dialer: dialer4, DisableTFO: !options.TCPFastOpen}, tfo.Dialer{Dialer: dialer4, DisableTFO: !options.TCPFastOpen},
tfo.Dialer{Dialer: dialer6, DisableTFO: !options.TCPFastOpen}, tfo.Dialer{Dialer: dialer6, DisableTFO: !options.TCPFastOpen},
@ -101,7 +107,7 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
listener, listener,
udpAddr4, udpAddr4,
udpAddr6, udpAddr6,
} }, nil
} }
func (d *DefaultDialer) DialContext(ctx context.Context, network string, address M.Socksaddr) (net.Conn, error) { func (d *DefaultDialer) DialContext(ctx context.Context, network string, address M.Socksaddr) (net.Conn, error) {

View file

@ -0,0 +1,11 @@
//go:build go1.21
package dialer
import "net"
const multipathTCPAvailable = true
func setMultiPathTCP(dialer *net.Dialer) {
dialer.SetMultipathTCP(true)
}

View file

@ -0,0 +1,12 @@
//go:build !go1.21
package dialer
import (
"net"
)
const multipathTCPAvailable = false
func setMultiPathTCP(dialer *net.Dialer) {
}

View file

@ -6,13 +6,24 @@ import (
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-dns" "github.com/sagernet/sing-dns"
"github.com/sagernet/sing/common"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
) )
func New(router adapter.Router, options option.DialerOptions) N.Dialer { func MustNew(router adapter.Router, options option.DialerOptions) N.Dialer {
var dialer N.Dialer return common.Must1(New(router, options))
}
func New(router adapter.Router, options option.DialerOptions) (N.Dialer, error) {
var (
dialer N.Dialer
err error
)
if options.Detour == "" { if options.Detour == "" {
dialer = NewDefault(router, options) dialer, err = NewDefault(router, options)
if err != nil {
return nil, err
}
} else { } else {
dialer = NewDetour(router, options.Detour) dialer = NewDetour(router, options.Detour)
} }
@ -20,5 +31,5 @@ func New(router adapter.Router, options option.DialerOptions) N.Dialer {
if domainStrategy != dns.DomainStrategyAsIS || options.Detour == "" { if domainStrategy != dns.DomainStrategyAsIS || options.Detour == "" {
dialer = NewResolveDialer(router, dialer, domainStrategy, time.Duration(options.FallbackDelay)) dialer = NewResolveDialer(router, dialer, domainStrategy, time.Duration(options.FallbackDelay))
} }
return dialer return dialer, nil
} }

View file

@ -101,7 +101,10 @@ func NewRealityServer(ctx context.Context, router adapter.Router, logger log.Log
tlsConfig.ShortIds[shortID] = true tlsConfig.ShortIds[shortID] = true
} }
handshakeDialer := dialer.New(router, options.Reality.Handshake.DialerOptions) handshakeDialer, err := dialer.New(router, options.Reality.Handshake.DialerOptions)
if err != nil {
return nil, err
}
tlsConfig.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) { tlsConfig.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
return handshakeDialer.DialContext(ctx, network, M.ParseSocksaddr(addr)) return handshakeDialer.DialContext(ctx, network, M.ParseSocksaddr(addr))
} }

View file

@ -10,6 +10,7 @@
"reuse_addr": false, "reuse_addr": false,
"connect_timeout": "5s", "connect_timeout": "5s",
"tcp_fast_open": false, "tcp_fast_open": false,
"tcp_multi_path": false,
"udp_fragment": false, "udp_fragment": false,
"domain_strategy": "prefer_ipv6", "domain_strategy": "prefer_ipv6",
"fallback_delay": "300ms" "fallback_delay": "300ms"
@ -19,8 +20,8 @@
### Fields ### Fields
| Field | Available Context | | Field | Available Context |
|----------------------------------------------------------------------------------------------------------------------|-------------------| |------------------------------------------------------------------------------------------------------------------------------------------|-------------------|
| `bind_interface` /`*bind_address` /`routing_mark` /`reuse_addr` / `tcp_fast_open`/ `udp_fragment` /`connect_timeout` | `detour` not set | | `bind_interface` /`*bind_address` /`routing_mark` /`reuse_addr` / `tcp_fast_open` / `tcp_multi_path` / `udp_fragment` /`connect_timeout` | `detour` not set |
#### detour #### detour
@ -54,6 +55,14 @@ Reuse listener address.
Enable TCP Fast Open. Enable TCP Fast Open.
#### tcp_multi_path
!!! warning ""
Go 1.21 required.
Enable TCP Multi Path.
#### udp_fragment #### udp_fragment
Enable UDP fragmentation. Enable UDP fragmentation.

View file

@ -10,6 +10,7 @@
"reuse_addr": false, "reuse_addr": false,
"connect_timeout": "5s", "connect_timeout": "5s",
"tcp_fast_open": false, "tcp_fast_open": false,
"tcp_multi_path": false,
"udp_fragment": false, "udp_fragment": false,
"domain_strategy": "prefer_ipv6", "domain_strategy": "prefer_ipv6",
"fallback_delay": "300ms" "fallback_delay": "300ms"
@ -19,8 +20,8 @@
### 字段 ### 字段
| 字段 | 可用上下文 | | 字段 | 可用上下文 |
|----------------------------------------------------------------------------------------------------------------------|--------------| |------------------------------------------------------------------------------------------------------------------------------------------|--------------|
| `bind_interface` /`*bind_address` /`routing_mark` /`reuse_addr` / `tcp_fast_open`/ `udp_fragment` /`connect_timeout` | `detour` 未设置 | | `bind_interface` /`*bind_address` /`routing_mark` /`reuse_addr` / `tcp_fast_open` / `tcp_mutli_path` / `udp_fragment` /`connect_timeout` | `detour` 未设置 |
#### detour #### detour
@ -57,6 +58,14 @@
启用 TCP Fast Open。 启用 TCP Fast Open。
#### tcp_multi_path
!!! warning ""
需要 Go 1.21。
启用 TCP Multi Path。
#### udp_fragment #### udp_fragment
启用 UDP 分段。 启用 UDP 分段。

View file

@ -5,6 +5,7 @@
"listen": "::", "listen": "::",
"listen_port": 5353, "listen_port": 5353,
"tcp_fast_open": false, "tcp_fast_open": false,
"tcp_multi_path": false,
"udp_fragment": false, "udp_fragment": false,
"sniff": false, "sniff": false,
"sniff_override_destination": false, "sniff_override_destination": false,
@ -24,6 +25,7 @@
| `listen` | Needs to listen on TCP or UDP. | | `listen` | Needs to listen on TCP or UDP. |
| `listen_port` | Needs to listen on TCP or UDP. | | `listen_port` | Needs to listen on TCP or UDP. |
| `tcp_fast_open` | Needs to listen on TCP. | | `tcp_fast_open` | Needs to listen on TCP. |
| `tcp_multi_path` | Needs to listen on TCP. |
| `udp_timeout` | Needs to assemble UDP connections, currently Tun and Shadowsocks. | | `udp_timeout` | Needs to assemble UDP connections, currently Tun and Shadowsocks. |
| `proxy_protocol` | Needs to listen on TCP. | | `proxy_protocol` | Needs to listen on TCP. |
| `proxy_protocol_accept_no_header` | When `proxy_protocol` enabled | | `proxy_protocol_accept_no_header` | When `proxy_protocol` enabled |
@ -42,6 +44,14 @@ Listen port.
Enable TCP Fast Open. Enable TCP Fast Open.
#### tcp_multi_path
!!! warning ""
Go 1.21 required.
Enable TCP Multi Path.
#### udp_fragment #### udp_fragment
Enable UDP fragmentation. Enable UDP fragmentation.

View file

@ -5,6 +5,7 @@
"listen": "::", "listen": "::",
"listen_port": 5353, "listen_port": 5353,
"tcp_fast_open": false, "tcp_fast_open": false,
"tcp_multi_path": false,
"udp_fragment": false, "udp_fragment": false,
"sniff": false, "sniff": false,
"sniff_override_destination": false, "sniff_override_destination": false,
@ -23,6 +24,7 @@
| `listen` | 需要监听 TCP 或 UDP。 | | `listen` | 需要监听 TCP 或 UDP。 |
| `listen_port` | 需要监听 TCP 或 UDP。 | | `listen_port` | 需要监听 TCP 或 UDP。 |
| `tcp_fast_open` | 需要监听 TCP。 | | `tcp_fast_open` | 需要监听 TCP。 |
| `tcp_multi_path` | 需要监听 TCP。 |
| `udp_timeout` | 需要组装 UDP 连接, 当前为 Tun 和 Shadowsocks。 | | `udp_timeout` | 需要组装 UDP 连接, 当前为 Tun 和 Shadowsocks。 |
| `proxy_protocol` | 需要监听 TCP。 | | `proxy_protocol` | 需要监听 TCP。 |
| `proxy_protocol_accept_no_header` | `proxy_protocol` 启用时 | | `proxy_protocol_accept_no_header` | `proxy_protocol` 启用时 |
@ -43,6 +45,14 @@
启用 TCP Fast Open。 启用 TCP Fast Open。
#### tcp_multi_path
!!! warning ""
需要 Go 1.21。
启用 TCP Multi Path。
#### udp_fragment #### udp_fragment
启用 UDP 分段。 启用 UDP 分段。

View file

@ -18,7 +18,14 @@ func (a *myInboundAdapter) ListenTCP() (net.Listener, error) {
bindAddr := M.SocksaddrFrom(a.listenOptions.Listen.Build(), a.listenOptions.ListenPort) bindAddr := M.SocksaddrFrom(a.listenOptions.Listen.Build(), a.listenOptions.ListenPort)
var tcpListener net.Listener var tcpListener net.Listener
if !a.listenOptions.TCPFastOpen { if !a.listenOptions.TCPFastOpen {
tcpListener, err = net.ListenTCP(M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.TCPAddr()) var listenConfig net.ListenConfig
if a.listenOptions.TCPMultiPath {
if !multipathTCPAvailable {
return nil, E.New("MultiPath TCP requires go1.21, please recompile your binary.")
}
setMultiPathTCP(&listenConfig)
}
tcpListener, err = listenConfig.Listen(a.ctx, M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.String())
} else { } else {
tcpListener, err = tfo.ListenTCP(M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.TCPAddr()) tcpListener, err = tfo.ListenTCP(M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.TCPAddr())
} }

View file

@ -0,0 +1,11 @@
//go:build go1.21
package inbound
import "net"
const multipathTCPAvailable = true
func setMultiPathTCP(listenConfig *net.ListenConfig) {
listenConfig.SetMultipathTCP(true)
}

View file

@ -0,0 +1,10 @@
//go:build !go1.21
package inbound
import "net"
const multipathTCPAvailable = false
func setMultiPathTCP(listenConfig *net.ListenConfig) {
}

View file

@ -40,12 +40,20 @@ func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.Context
if options.Version > 1 { if options.Version > 1 {
handshakeForServerName = make(map[string]shadowtls.HandshakeConfig) handshakeForServerName = make(map[string]shadowtls.HandshakeConfig)
for serverName, serverOptions := range options.HandshakeForServerName { for serverName, serverOptions := range options.HandshakeForServerName {
handshakeDialer, err := dialer.New(router, serverOptions.DialerOptions)
if err != nil {
return nil, err
}
handshakeForServerName[serverName] = shadowtls.HandshakeConfig{ handshakeForServerName[serverName] = shadowtls.HandshakeConfig{
Server: serverOptions.ServerOptions.Build(), Server: serverOptions.ServerOptions.Build(),
Dialer: dialer.New(router, serverOptions.DialerOptions), Dialer: handshakeDialer,
} }
} }
} }
handshakeDialer, err := dialer.New(router, options.Handshake.DialerOptions)
if err != nil {
return nil, err
}
service, err := shadowtls.NewService(shadowtls.ServiceConfig{ service, err := shadowtls.NewService(shadowtls.ServiceConfig{
Version: options.Version, Version: options.Version,
Password: options.Password, Password: options.Password,
@ -54,7 +62,7 @@ func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.Context
}), }),
Handshake: shadowtls.HandshakeConfig{ Handshake: shadowtls.HandshakeConfig{
Server: options.Handshake.ServerOptions.Build(), Server: options.Handshake.ServerOptions.Build(),
Dialer: dialer.New(router, options.Handshake.DialerOptions), Dialer: handshakeDialer,
}, },
HandshakeForServerName: handshakeForServerName, HandshakeForServerName: handshakeForServerName,
StrictMode: options.StrictMode, StrictMode: options.StrictMode,

View file

@ -31,7 +31,7 @@ type Service struct {
clockOffset time.Duration clockOffset time.Duration
} }
func NewService(ctx context.Context, router adapter.Router, logger logger.Logger, options option.NTPOptions) *Service { func NewService(ctx context.Context, router adapter.Router, logger logger.Logger, options option.NTPOptions) (*Service, error) {
ctx, cancel := common.ContextWithCancelCause(ctx) ctx, cancel := common.ContextWithCancelCause(ctx)
server := options.ServerOptions.Build() server := options.ServerOptions.Build()
if server.Port == 0 { if server.Port == 0 {
@ -43,15 +43,19 @@ func NewService(ctx context.Context, router adapter.Router, logger logger.Logger
} else { } else {
interval = 30 * time.Minute interval = 30 * time.Minute
} }
outboundDialer, err := dialer.New(router, options.DialerOptions)
if err != nil {
return nil, err
}
return &Service{ return &Service{
ctx: ctx, ctx: ctx,
cancel: cancel, cancel: cancel,
server: server, server: server,
writeToSystem: options.WriteToSystem, writeToSystem: options.WriteToSystem,
dialer: dialer.New(router, options.DialerOptions), dialer: outboundDialer,
logger: logger, logger: logger,
ticker: time.NewTicker(interval), ticker: time.NewTicker(interval),
} }, nil
} }
func (s *Service) Start() error { func (s *Service) Start() error {

View file

@ -125,6 +125,7 @@ type ListenOptions struct {
Listen *ListenAddress `json:"listen,omitempty"` Listen *ListenAddress `json:"listen,omitempty"`
ListenPort uint16 `json:"listen_port,omitempty"` ListenPort uint16 `json:"listen_port,omitempty"`
TCPFastOpen bool `json:"tcp_fast_open,omitempty"` TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
TCPMultiPath bool `json:"tcp_multi_path,omitempty"`
UDPFragment *bool `json:"udp_fragment,omitempty"` UDPFragment *bool `json:"udp_fragment,omitempty"`
UDPFragmentDefault bool `json:"-"` UDPFragmentDefault bool `json:"-"`
UDPTimeout int64 `json:"udp_timeout,omitempty"` UDPTimeout int64 `json:"udp_timeout,omitempty"`

View file

@ -134,6 +134,7 @@ type DialerOptions struct {
ReuseAddr bool `json:"reuse_addr,omitempty"` ReuseAddr bool `json:"reuse_addr,omitempty"`
ConnectTimeout Duration `json:"connect_timeout,omitempty"` ConnectTimeout Duration `json:"connect_timeout,omitempty"`
TCPFastOpen bool `json:"tcp_fast_open,omitempty"` TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
TCPMultiPath bool `json:"tcp_multi_path,omitempty"`
UDPFragment *bool `json:"udp_fragment,omitempty"` UDPFragment *bool `json:"udp_fragment,omitempty"`
UDPFragmentDefault bool `json:"-"` UDPFragmentDefault bool `json:"-"`
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"` DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`

View file

@ -38,6 +38,10 @@ type Direct struct {
func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, options option.DirectOutboundOptions) (*Direct, error) { func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, options option.DirectOutboundOptions) (*Direct, error) {
options.UDPFragmentDefault = true options.UDPFragmentDefault = true
outboundDialer, err := dialer.New(router, options.DialerOptions)
if err != nil {
return nil, err
}
outbound := &Direct{ outbound := &Direct{
myOutboundAdapter: myOutboundAdapter{ myOutboundAdapter: myOutboundAdapter{
protocol: C.TypeDirect, protocol: C.TypeDirect,
@ -49,7 +53,7 @@ func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, opti
}, },
domainStrategy: dns.DomainStrategy(options.DomainStrategy), domainStrategy: dns.DomainStrategy(options.DomainStrategy),
fallbackDelay: time.Duration(options.FallbackDelay), fallbackDelay: time.Duration(options.FallbackDelay),
dialer: dialer.New(router, options.DialerOptions), dialer: outboundDialer,
proxyProto: options.ProxyProtocol, proxyProto: options.ProxyProtocol,
} }
if options.ProxyProtocol > 2 { if options.ProxyProtocol > 2 {

View file

@ -26,7 +26,11 @@ type HTTP struct {
} }
func NewHTTP(router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPOutboundOptions) (*HTTP, error) { func NewHTTP(router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPOutboundOptions) (*HTTP, error) {
detour, err := tls.NewDialerFromOptions(router, dialer.New(router, options.DialerOptions), options.Server, common.PtrValueOrDefault(options.TLS)) outboundDialer, err := dialer.New(router, options.DialerOptions)
if err != nil {
return nil, err
}
detour, err := tls.NewDialerFromOptions(router, outboundDialer, options.Server, common.PtrValueOrDefault(options.TLS))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -117,6 +117,10 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL
if down < hysteria.MinSpeedBPS { if down < hysteria.MinSpeedBPS {
return nil, E.New("invalid down speed") return nil, E.New("invalid down speed")
} }
outboundDialer, err := dialer.New(router, options.DialerOptions)
if err != nil {
return nil, err
}
return &Hysteria{ return &Hysteria{
myOutboundAdapter: myOutboundAdapter{ myOutboundAdapter: myOutboundAdapter{
protocol: C.TypeHysteria, protocol: C.TypeHysteria,
@ -127,7 +131,7 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL
dependencies: withDialerDependency(options.DialerOptions), dependencies: withDialerDependency(options.DialerOptions),
}, },
ctx: ctx, ctx: ctx,
dialer: dialer.New(router, options.DialerOptions), dialer: outboundDialer,
serverAddr: options.ServerOptions.Build(), serverAddr: options.ServerOptions.Build(),
tlsConfig: tlsConfig, tlsConfig: tlsConfig,
quicConfig: quicConfig, quicConfig: quicConfig,

View file

@ -39,6 +39,10 @@ func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
if err != nil { if err != nil {
return nil, err return nil, err
} }
outboundDialer, err := dialer.New(router, options.DialerOptions)
if err != nil {
return nil, err
}
outbound := &Shadowsocks{ outbound := &Shadowsocks{
myOutboundAdapter: myOutboundAdapter{ myOutboundAdapter: myOutboundAdapter{
protocol: C.TypeShadowsocks, protocol: C.TypeShadowsocks,
@ -48,7 +52,7 @@ func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte
tag: tag, tag: tag,
dependencies: withDialerDependency(options.DialerOptions), dependencies: withDialerDependency(options.DialerOptions),
}, },
dialer: dialer.New(router, options.DialerOptions), dialer: outboundDialer,
method: method, method: method,
serverAddr: options.ServerOptions.Build(), serverAddr: options.ServerOptions.Build(),
} }

View file

@ -37,6 +37,10 @@ type ShadowsocksR struct {
} }
func NewShadowsocksR(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksROutboundOptions) (*ShadowsocksR, error) { func NewShadowsocksR(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksROutboundOptions) (*ShadowsocksR, error) {
outboundDialer, err := dialer.New(router, options.DialerOptions)
if err != nil {
return nil, err
}
outbound := &ShadowsocksR{ outbound := &ShadowsocksR{
myOutboundAdapter: myOutboundAdapter{ myOutboundAdapter: myOutboundAdapter{
protocol: C.TypeShadowsocksR, protocol: C.TypeShadowsocksR,
@ -46,11 +50,10 @@ func NewShadowsocksR(ctx context.Context, router adapter.Router, logger log.Cont
tag: tag, tag: tag,
dependencies: withDialerDependency(options.DialerOptions), dependencies: withDialerDependency(options.DialerOptions),
}, },
dialer: dialer.New(router, options.DialerOptions), dialer: outboundDialer,
serverAddr: options.ServerOptions.Build(), serverAddr: options.ServerOptions.Build(),
} }
var cipher string var cipher string
var err error
switch options.Method { switch options.Method {
case "none": case "none":
cipher = "dummy" cipher = "dummy"

View file

@ -72,11 +72,15 @@ func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.Context
tlsHandshakeFunc = shadowtls.DefaultTLSHandshakeFunc(options.Password, stdTLSConfig) tlsHandshakeFunc = shadowtls.DefaultTLSHandshakeFunc(options.Password, stdTLSConfig)
} }
} }
outboundDialer, err := dialer.New(router, options.DialerOptions)
if err != nil {
return nil, err
}
client, err := shadowtls.NewClient(shadowtls.ClientConfig{ client, err := shadowtls.NewClient(shadowtls.ClientConfig{
Version: options.Version, Version: options.Version,
Password: options.Password, Password: options.Password,
Server: options.ServerOptions.Build(), Server: options.ServerOptions.Build(),
Dialer: dialer.New(router, options.DialerOptions), Dialer: outboundDialer,
TLSHandshake: tlsHandshakeFunc, TLSHandshake: tlsHandshakeFunc,
Logger: logger, Logger: logger,
}) })

View file

@ -37,6 +37,10 @@ func NewSocks(router adapter.Router, logger log.ContextLogger, tag string, optio
if err != nil { if err != nil {
return nil, err return nil, err
} }
outboundDialer, err := dialer.New(router, options.DialerOptions)
if err != nil {
return nil, err
}
outbound := &Socks{ outbound := &Socks{
myOutboundAdapter: myOutboundAdapter{ myOutboundAdapter: myOutboundAdapter{
protocol: C.TypeSOCKS, protocol: C.TypeSOCKS,
@ -46,7 +50,7 @@ func NewSocks(router adapter.Router, logger log.ContextLogger, tag string, optio
tag: tag, tag: tag,
dependencies: withDialerDependency(options.DialerOptions), dependencies: withDialerDependency(options.DialerOptions),
}, },
client: socks.NewClient(dialer.New(router, options.DialerOptions), options.ServerOptions.Build(), version, options.Username, options.Password), client: socks.NewClient(outboundDialer, options.ServerOptions.Build(), version, options.Username, options.Password),
resolve: version == socks.Version4, resolve: version == socks.Version4,
} }
uotOptions := common.PtrValueOrDefault(options.UDPOverTCPOptions) uotOptions := common.PtrValueOrDefault(options.UDPOverTCPOptions)

View file

@ -44,6 +44,10 @@ type SSH struct {
} }
func NewSSH(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SSHOutboundOptions) (*SSH, error) { func NewSSH(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SSHOutboundOptions) (*SSH, error) {
outboundDialer, err := dialer.New(router, options.DialerOptions)
if err != nil {
return nil, err
}
outbound := &SSH{ outbound := &SSH{
myOutboundAdapter: myOutboundAdapter{ myOutboundAdapter: myOutboundAdapter{
protocol: C.TypeSSH, protocol: C.TypeSSH,
@ -54,7 +58,7 @@ func NewSSH(ctx context.Context, router adapter.Router, logger log.ContextLogger
dependencies: withDialerDependency(options.DialerOptions), dependencies: withDialerDependency(options.DialerOptions),
}, },
ctx: ctx, ctx: ctx,
dialer: dialer.New(router, options.DialerOptions), dialer: outboundDialer,
serverAddr: options.ServerOptions.Build(), serverAddr: options.ServerOptions.Build(),
user: options.User, user: options.User,
hostKeyAlgorithms: options.HostKeyAlgorithms, hostKeyAlgorithms: options.HostKeyAlgorithms,

View file

@ -66,6 +66,10 @@ func NewTor(ctx context.Context, router adapter.Router, logger log.ContextLogger
} }
startConf.TorrcFile = torrcFile startConf.TorrcFile = torrcFile
} }
outboundDialer, err := dialer.New(router, options.DialerOptions)
if err != nil {
return nil, err
}
return &Tor{ return &Tor{
myOutboundAdapter: myOutboundAdapter{ myOutboundAdapter: myOutboundAdapter{
protocol: C.TypeTor, protocol: C.TypeTor,
@ -76,7 +80,7 @@ func NewTor(ctx context.Context, router adapter.Router, logger log.ContextLogger
dependencies: withDialerDependency(options.DialerOptions), dependencies: withDialerDependency(options.DialerOptions),
}, },
ctx: ctx, ctx: ctx,
proxy: NewProxyListener(ctx, logger, dialer.New(router, options.DialerOptions)), proxy: NewProxyListener(ctx, logger, outboundDialer),
startConf: &startConf, startConf: &startConf,
options: options.Options, options: options.Options,
}, nil }, nil

View file

@ -33,6 +33,10 @@ type Trojan struct {
} }
func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TrojanOutboundOptions) (*Trojan, error) { func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TrojanOutboundOptions) (*Trojan, error) {
outboundDialer, err := dialer.New(router, options.DialerOptions)
if err != nil {
return nil, err
}
outbound := &Trojan{ outbound := &Trojan{
myOutboundAdapter: myOutboundAdapter{ myOutboundAdapter: myOutboundAdapter{
protocol: C.TypeTrojan, protocol: C.TypeTrojan,
@ -42,11 +46,10 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog
tag: tag, tag: tag,
dependencies: withDialerDependency(options.DialerOptions), dependencies: withDialerDependency(options.DialerOptions),
}, },
dialer: dialer.New(router, options.DialerOptions), dialer: outboundDialer,
serverAddr: options.ServerOptions.Build(), serverAddr: options.ServerOptions.Build(),
key: trojan.Key(options.Password), key: trojan.Key(options.Password),
} }
var err error
if options.TLS != nil { if options.TLS != nil {
outbound.tlsConfig, err = tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS)) outbound.tlsConfig, err = tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS))
if err != nil { if err != nil {

View file

@ -57,9 +57,13 @@ func NewTUIC(ctx context.Context, router adapter.Router, logger log.ContextLogge
case "quic": case "quic":
udpStream = true udpStream = true
} }
outboundDialer, err := dialer.New(router, options.DialerOptions)
if err != nil {
return nil, err
}
client, err := tuic.NewClient(tuic.ClientOptions{ client, err := tuic.NewClient(tuic.ClientOptions{
Context: ctx, Context: ctx,
Dialer: dialer.New(router, options.DialerOptions), Dialer: outboundDialer,
ServerAddress: options.ServerOptions.Build(), ServerAddress: options.ServerOptions.Build(),
TLSConfig: tlsConfig, TLSConfig: tlsConfig,
UUID: userUUID, UUID: userUUID,

View file

@ -36,6 +36,10 @@ type VLESS struct {
} }
func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VLESSOutboundOptions) (*VLESS, error) { func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VLESSOutboundOptions) (*VLESS, error) {
outboundDialer, err := dialer.New(router, options.DialerOptions)
if err != nil {
return nil, err
}
outbound := &VLESS{ outbound := &VLESS{
myOutboundAdapter: myOutboundAdapter{ myOutboundAdapter: myOutboundAdapter{
protocol: C.TypeVLESS, protocol: C.TypeVLESS,
@ -45,10 +49,9 @@ func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogg
tag: tag, tag: tag,
dependencies: withDialerDependency(options.DialerOptions), dependencies: withDialerDependency(options.DialerOptions),
}, },
dialer: dialer.New(router, options.DialerOptions), dialer: outboundDialer,
serverAddr: options.ServerOptions.Build(), serverAddr: options.ServerOptions.Build(),
} }
var err error
if options.TLS != nil { if options.TLS != nil {
outbound.tlsConfig, err = tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS)) outbound.tlsConfig, err = tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS))
if err != nil { if err != nil {

View file

@ -35,6 +35,10 @@ type VMess struct {
} }
func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VMessOutboundOptions) (*VMess, error) { func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VMessOutboundOptions) (*VMess, error) {
outboundDialer, err := dialer.New(router, options.DialerOptions)
if err != nil {
return nil, err
}
outbound := &VMess{ outbound := &VMess{
myOutboundAdapter: myOutboundAdapter{ myOutboundAdapter: myOutboundAdapter{
protocol: C.TypeVMess, protocol: C.TypeVMess,
@ -44,10 +48,9 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg
tag: tag, tag: tag,
dependencies: withDialerDependency(options.DialerOptions), dependencies: withDialerDependency(options.DialerOptions),
}, },
dialer: dialer.New(router, options.DialerOptions), dialer: outboundDialer,
serverAddr: options.ServerOptions.Build(), serverAddr: options.ServerOptions.Build(),
} }
var err error
if options.TLS != nil { if options.TLS != nil {
outbound.tlsConfig, err = tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS)) outbound.tlsConfig, err = tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS))
if err != nil { if err != nil {

View file

@ -65,7 +65,11 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
connectAddr = options.ServerOptions.Build() connectAddr = options.ServerOptions.Build()
} }
} }
outbound.bind = wireguard.NewClientBind(ctx, outbound, dialer.New(router, options.DialerOptions), isConnect, connectAddr, reserved) outboundDialer, err := dialer.New(router, options.DialerOptions)
if err != nil {
return nil, err
}
outbound.bind = wireguard.NewClientBind(ctx, outbound, outboundDialer, isConnect, connectAddr, reserved)
localPrefixes := common.Map(options.LocalAddress, option.ListenPrefix.Build) localPrefixes := common.Map(options.LocalAddress, option.ListenPrefix.Build)
if len(localPrefixes) == 0 { if len(localPrefixes) == 0 {
return nil, E.New("missing local address") return nil, E.New("missing local address")
@ -157,7 +161,6 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
mtu = 1408 mtu = 1408
} }
var wireTunDevice wireguard.Device var wireTunDevice wireguard.Device
var err error
if !options.SystemInterface && tun.WithGVisor { if !options.SystemInterface && tun.WithGVisor {
wireTunDevice, err = wireguard.NewStackDevice(localPrefixes, mtu) wireTunDevice, err = wireguard.NewStackDevice(localPrefixes, mtu)
} else { } else {

View file

@ -38,7 +38,9 @@ import (
F "github.com/sagernet/sing/common/format" F "github.com/sagernet/sing/common/format"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
serviceNTP "github.com/sagernet/sing/common/ntp"
"github.com/sagernet/sing/common/uot" "github.com/sagernet/sing/common/uot"
"github.com/sagernet/sing/service"
"github.com/sagernet/sing/service/pause" "github.com/sagernet/sing/service/pause"
) )
@ -319,7 +321,12 @@ func NewRouter(
} }
} }
if ntpOptions.Enabled { if ntpOptions.Enabled {
router.timeService = ntp.NewService(ctx, router, logFactory.NewLogger("ntp"), ntpOptions) timeService, err := ntp.NewService(ctx, router, logFactory.NewLogger("ntp"), ntpOptions)
if err != nil {
return nil, err
}
service.ContextWith[serviceNTP.TimeService](ctx, timeService)
router.timeService = timeService
} }
return router, nil return router, nil
} }

View file

@ -248,10 +248,10 @@ func (t *Transport) recreateServers(iface *net.Interface, serverAddrs []netip.Ad
}), ","), "]") }), ","), "]")
} }
serverDialer := dialer.NewDefault(t.router, option.DialerOptions{ serverDialer := common.Must1(dialer.NewDefault(t.router, option.DialerOptions{
BindInterface: iface.Name, BindInterface: iface.Name,
UDPFragmentDefault: true, UDPFragmentDefault: true,
}) }))
var transports []dns.Transport var transports []dns.Transport
for _, serverAddr := range serverAddrs { for _, serverAddr := range serverAddrs {
serverTransport, err := dns.NewUDPTransport(t.name, t.ctx, serverDialer, M.Socksaddr{Addr: serverAddr, Port: 53}) serverTransport, err := dns.NewUDPTransport(t.name, t.ctx, serverDialer, M.Socksaddr{Addr: serverAddr, Port: 53})

View file

@ -10,6 +10,7 @@ import (
"github.com/sagernet/sing-box/common/dialer" "github.com/sagernet/sing-box/common/dialer"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-tun" "github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
wgTun "github.com/sagernet/wireguard-go/tun" wgTun "github.com/sagernet/wireguard-go/tun"
@ -58,9 +59,9 @@ func NewSystemDevice(router adapter.Router, interfaceName string, localPrefixes
inet6Address = inet6Addresses[0].Addr() inet6Address = inet6Addresses[0].Addr()
} }
return &SystemDevice{ return &SystemDevice{
dialer: dialer.NewDefault(router, option.DialerOptions{ dialer: common.Must1(dialer.NewDefault(router, option.DialerOptions{
BindInterface: interfaceName, BindInterface: interfaceName,
}), })),
device: tunInterface, device: tunInterface,
name: interfaceName, name: interfaceName,
mtu: int(mtu), mtu: int(mtu),