From 1019ecfdcfb7c2e93f76654e1dc885a07717b483 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Tue, 8 Aug 2023 16:14:03 +0800 Subject: [PATCH] Add TCP MultiPath support --- common/dialer/default.go | 10 ++++++++-- common/dialer/default_go1.21.go | 11 +++++++++++ common/dialer/default_nongo1.21.go | 12 ++++++++++++ common/dialer/dialer.go | 19 +++++++++++++++---- common/tls/reality_server.go | 5 ++++- docs/configuration/shared/dial.md | 15 ++++++++++++--- docs/configuration/shared/dial.zh.md | 15 ++++++++++++--- docs/configuration/shared/listen.md | 10 ++++++++++ docs/configuration/shared/listen.zh.md | 10 ++++++++++ inbound/default_tcp.go | 9 ++++++++- inbound/default_tcp_go1.21.go | 11 +++++++++++ inbound/default_tcp_nongo1.21.go | 10 ++++++++++ inbound/shadowtls.go | 12 ++++++++++-- ntp/service.go | 10 +++++++--- option/inbound.go | 1 + option/outbound.go | 1 + outbound/direct.go | 6 +++++- outbound/http.go | 6 +++++- outbound/hysteria.go | 6 +++++- outbound/shadowsocks.go | 6 +++++- outbound/shadowsocksr.go | 7 +++++-- outbound/shadowtls.go | 6 +++++- outbound/socks.go | 6 +++++- outbound/ssh.go | 6 +++++- outbound/tor.go | 6 +++++- outbound/trojan.go | 7 +++++-- outbound/tuic.go | 6 +++++- outbound/vless.go | 7 +++++-- outbound/vmess.go | 7 +++++-- outbound/wireguard.go | 7 +++++-- route/router.go | 9 ++++++++- transport/dhcp/server.go | 4 ++-- transport/wireguard/device_system.go | 5 +++-- 33 files changed, 225 insertions(+), 43 deletions(-) create mode 100644 common/dialer/default_go1.21.go create mode 100644 common/dialer/default_nongo1.21.go create mode 100644 inbound/default_tcp_go1.21.go create mode 100644 inbound/default_tcp_nongo1.21.go diff --git a/common/dialer/default.go b/common/dialer/default.go index f45b7809..f4148f36 100644 --- a/common/dialer/default.go +++ b/common/dialer/default.go @@ -26,7 +26,7 @@ type DefaultDialer struct { 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 listener net.ListenConfig if options.BindInterface != "" { @@ -93,6 +93,12 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia udpDialer6.LocalAddr = &net.UDPAddr{IP: bindAddr.AsSlice()} 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{ tfo.Dialer{Dialer: dialer4, DisableTFO: !options.TCPFastOpen}, tfo.Dialer{Dialer: dialer6, DisableTFO: !options.TCPFastOpen}, @@ -101,7 +107,7 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia listener, udpAddr4, udpAddr6, - } + }, nil } func (d *DefaultDialer) DialContext(ctx context.Context, network string, address M.Socksaddr) (net.Conn, error) { diff --git a/common/dialer/default_go1.21.go b/common/dialer/default_go1.21.go new file mode 100644 index 00000000..360826c8 --- /dev/null +++ b/common/dialer/default_go1.21.go @@ -0,0 +1,11 @@ +//go:build go1.21 + +package dialer + +import "net" + +const multipathTCPAvailable = true + +func setMultiPathTCP(dialer *net.Dialer) { + dialer.SetMultipathTCP(true) +} diff --git a/common/dialer/default_nongo1.21.go b/common/dialer/default_nongo1.21.go new file mode 100644 index 00000000..6e564673 --- /dev/null +++ b/common/dialer/default_nongo1.21.go @@ -0,0 +1,12 @@ +//go:build !go1.21 + +package dialer + +import ( + "net" +) + +const multipathTCPAvailable = false + +func setMultiPathTCP(dialer *net.Dialer) { +} diff --git a/common/dialer/dialer.go b/common/dialer/dialer.go index 5b1750cc..0f5c913a 100644 --- a/common/dialer/dialer.go +++ b/common/dialer/dialer.go @@ -6,13 +6,24 @@ import ( "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing-dns" + "github.com/sagernet/sing/common" N "github.com/sagernet/sing/common/network" ) -func New(router adapter.Router, options option.DialerOptions) N.Dialer { - var dialer N.Dialer +func MustNew(router adapter.Router, options option.DialerOptions) 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 == "" { - dialer = NewDefault(router, options) + dialer, err = NewDefault(router, options) + if err != nil { + return nil, err + } } else { 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 == "" { dialer = NewResolveDialer(router, dialer, domainStrategy, time.Duration(options.FallbackDelay)) } - return dialer + return dialer, nil } diff --git a/common/tls/reality_server.go b/common/tls/reality_server.go index 0cd339c9..fd1a6815 100644 --- a/common/tls/reality_server.go +++ b/common/tls/reality_server.go @@ -101,7 +101,10 @@ func NewRealityServer(ctx context.Context, router adapter.Router, logger log.Log 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) { return handshakeDialer.DialContext(ctx, network, M.ParseSocksaddr(addr)) } diff --git a/docs/configuration/shared/dial.md b/docs/configuration/shared/dial.md index f4c5b666..1f524f08 100644 --- a/docs/configuration/shared/dial.md +++ b/docs/configuration/shared/dial.md @@ -10,6 +10,7 @@ "reuse_addr": false, "connect_timeout": "5s", "tcp_fast_open": false, + "tcp_multi_path": false, "udp_fragment": false, "domain_strategy": "prefer_ipv6", "fallback_delay": "300ms" @@ -18,9 +19,9 @@ ### Fields -| Field | Available Context | -|----------------------------------------------------------------------------------------------------------------------|-------------------| -| `bind_interface` /`*bind_address` /`routing_mark` /`reuse_addr` / `tcp_fast_open`/ `udp_fragment` /`connect_timeout` | `detour` not set | +| Field | Available Context | +|------------------------------------------------------------------------------------------------------------------------------------------|-------------------| +| `bind_interface` /`*bind_address` /`routing_mark` /`reuse_addr` / `tcp_fast_open` / `tcp_multi_path` / `udp_fragment` /`connect_timeout` | `detour` not set | #### detour @@ -54,6 +55,14 @@ Reuse listener address. Enable TCP Fast Open. +#### tcp_multi_path + +!!! warning "" + + Go 1.21 required. + +Enable TCP Multi Path. + #### udp_fragment Enable UDP fragmentation. diff --git a/docs/configuration/shared/dial.zh.md b/docs/configuration/shared/dial.zh.md index e084f15e..62b094f3 100644 --- a/docs/configuration/shared/dial.zh.md +++ b/docs/configuration/shared/dial.zh.md @@ -10,6 +10,7 @@ "reuse_addr": false, "connect_timeout": "5s", "tcp_fast_open": false, + "tcp_multi_path": false, "udp_fragment": false, "domain_strategy": "prefer_ipv6", "fallback_delay": "300ms" @@ -18,9 +19,9 @@ ### 字段 -| 字段 | 可用上下文 | -|----------------------------------------------------------------------------------------------------------------------|--------------| -| `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 @@ -57,6 +58,14 @@ 启用 TCP Fast Open。 +#### tcp_multi_path + +!!! warning "" + + 需要 Go 1.21。 + +启用 TCP Multi Path。 + #### udp_fragment 启用 UDP 分段。 diff --git a/docs/configuration/shared/listen.md b/docs/configuration/shared/listen.md index f20d42a3..d4a0e58e 100644 --- a/docs/configuration/shared/listen.md +++ b/docs/configuration/shared/listen.md @@ -5,6 +5,7 @@ "listen": "::", "listen_port": 5353, "tcp_fast_open": false, + "tcp_multi_path": false, "udp_fragment": false, "sniff": false, "sniff_override_destination": false, @@ -24,6 +25,7 @@ | `listen` | 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_multi_path` | Needs to listen on TCP. | | `udp_timeout` | Needs to assemble UDP connections, currently Tun and Shadowsocks. | | `proxy_protocol` | Needs to listen on TCP. | | `proxy_protocol_accept_no_header` | When `proxy_protocol` enabled | @@ -42,6 +44,14 @@ Listen port. Enable TCP Fast Open. +#### tcp_multi_path + +!!! warning "" + + Go 1.21 required. + +Enable TCP Multi Path. + #### udp_fragment Enable UDP fragmentation. diff --git a/docs/configuration/shared/listen.zh.md b/docs/configuration/shared/listen.zh.md index f9f469d2..b25ce295 100644 --- a/docs/configuration/shared/listen.zh.md +++ b/docs/configuration/shared/listen.zh.md @@ -5,6 +5,7 @@ "listen": "::", "listen_port": 5353, "tcp_fast_open": false, + "tcp_multi_path": false, "udp_fragment": false, "sniff": false, "sniff_override_destination": false, @@ -23,6 +24,7 @@ | `listen` | 需要监听 TCP 或 UDP。 | | `listen_port` | 需要监听 TCP 或 UDP。 | | `tcp_fast_open` | 需要监听 TCP。 | +| `tcp_multi_path` | 需要监听 TCP。 | | `udp_timeout` | 需要组装 UDP 连接, 当前为 Tun 和 Shadowsocks。 | | `proxy_protocol` | 需要监听 TCP。 | | `proxy_protocol_accept_no_header` | `proxy_protocol` 启用时 | @@ -43,6 +45,14 @@ 启用 TCP Fast Open。 +#### tcp_multi_path + +!!! warning "" + + 需要 Go 1.21。 + +启用 TCP Multi Path。 + #### udp_fragment 启用 UDP 分段。 diff --git a/inbound/default_tcp.go b/inbound/default_tcp.go index 238762dc..8de81ba4 100644 --- a/inbound/default_tcp.go +++ b/inbound/default_tcp.go @@ -18,7 +18,14 @@ func (a *myInboundAdapter) ListenTCP() (net.Listener, error) { bindAddr := M.SocksaddrFrom(a.listenOptions.Listen.Build(), a.listenOptions.ListenPort) var tcpListener net.Listener 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 { tcpListener, err = tfo.ListenTCP(M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.TCPAddr()) } diff --git a/inbound/default_tcp_go1.21.go b/inbound/default_tcp_go1.21.go new file mode 100644 index 00000000..1352e056 --- /dev/null +++ b/inbound/default_tcp_go1.21.go @@ -0,0 +1,11 @@ +//go:build go1.21 + +package inbound + +import "net" + +const multipathTCPAvailable = true + +func setMultiPathTCP(listenConfig *net.ListenConfig) { + listenConfig.SetMultipathTCP(true) +} diff --git a/inbound/default_tcp_nongo1.21.go b/inbound/default_tcp_nongo1.21.go new file mode 100644 index 00000000..62dc5b9f --- /dev/null +++ b/inbound/default_tcp_nongo1.21.go @@ -0,0 +1,10 @@ +//go:build !go1.21 + +package inbound + +import "net" + +const multipathTCPAvailable = false + +func setMultiPathTCP(listenConfig *net.ListenConfig) { +} diff --git a/inbound/shadowtls.go b/inbound/shadowtls.go index cd7a4db8..59b7c107 100644 --- a/inbound/shadowtls.go +++ b/inbound/shadowtls.go @@ -40,12 +40,20 @@ func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.Context if options.Version > 1 { handshakeForServerName = make(map[string]shadowtls.HandshakeConfig) for serverName, serverOptions := range options.HandshakeForServerName { + handshakeDialer, err := dialer.New(router, serverOptions.DialerOptions) + if err != nil { + return nil, err + } handshakeForServerName[serverName] = shadowtls.HandshakeConfig{ 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{ Version: options.Version, Password: options.Password, @@ -54,7 +62,7 @@ func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.Context }), Handshake: shadowtls.HandshakeConfig{ Server: options.Handshake.ServerOptions.Build(), - Dialer: dialer.New(router, options.Handshake.DialerOptions), + Dialer: handshakeDialer, }, HandshakeForServerName: handshakeForServerName, StrictMode: options.StrictMode, diff --git a/ntp/service.go b/ntp/service.go index a8dc6ea2..3ca1de3c 100644 --- a/ntp/service.go +++ b/ntp/service.go @@ -31,7 +31,7 @@ type Service struct { 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) server := options.ServerOptions.Build() if server.Port == 0 { @@ -43,15 +43,19 @@ func NewService(ctx context.Context, router adapter.Router, logger logger.Logger } else { interval = 30 * time.Minute } + outboundDialer, err := dialer.New(router, options.DialerOptions) + if err != nil { + return nil, err + } return &Service{ ctx: ctx, cancel: cancel, server: server, writeToSystem: options.WriteToSystem, - dialer: dialer.New(router, options.DialerOptions), + dialer: outboundDialer, logger: logger, ticker: time.NewTicker(interval), - } + }, nil } func (s *Service) Start() error { diff --git a/option/inbound.go b/option/inbound.go index b09b3a65..64b45e6c 100644 --- a/option/inbound.go +++ b/option/inbound.go @@ -125,6 +125,7 @@ type ListenOptions struct { Listen *ListenAddress `json:"listen,omitempty"` ListenPort uint16 `json:"listen_port,omitempty"` TCPFastOpen bool `json:"tcp_fast_open,omitempty"` + TCPMultiPath bool `json:"tcp_multi_path,omitempty"` UDPFragment *bool `json:"udp_fragment,omitempty"` UDPFragmentDefault bool `json:"-"` UDPTimeout int64 `json:"udp_timeout,omitempty"` diff --git a/option/outbound.go b/option/outbound.go index ab7aa0eb..5e837741 100644 --- a/option/outbound.go +++ b/option/outbound.go @@ -134,6 +134,7 @@ type DialerOptions struct { ReuseAddr bool `json:"reuse_addr,omitempty"` ConnectTimeout Duration `json:"connect_timeout,omitempty"` TCPFastOpen bool `json:"tcp_fast_open,omitempty"` + TCPMultiPath bool `json:"tcp_multi_path,omitempty"` UDPFragment *bool `json:"udp_fragment,omitempty"` UDPFragmentDefault bool `json:"-"` DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"` diff --git a/outbound/direct.go b/outbound/direct.go index 5a0cd34d..ed126830 100644 --- a/outbound/direct.go +++ b/outbound/direct.go @@ -38,6 +38,10 @@ type Direct struct { func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, options option.DirectOutboundOptions) (*Direct, error) { options.UDPFragmentDefault = true + outboundDialer, err := dialer.New(router, options.DialerOptions) + if err != nil { + return nil, err + } outbound := &Direct{ myOutboundAdapter: myOutboundAdapter{ protocol: C.TypeDirect, @@ -49,7 +53,7 @@ func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, opti }, domainStrategy: dns.DomainStrategy(options.DomainStrategy), fallbackDelay: time.Duration(options.FallbackDelay), - dialer: dialer.New(router, options.DialerOptions), + dialer: outboundDialer, proxyProto: options.ProxyProtocol, } if options.ProxyProtocol > 2 { diff --git a/outbound/http.go b/outbound/http.go index e07f2f01..2e265da1 100644 --- a/outbound/http.go +++ b/outbound/http.go @@ -26,7 +26,11 @@ type HTTP struct { } 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 { return nil, err } diff --git a/outbound/hysteria.go b/outbound/hysteria.go index d055c8f9..9ed5b6d8 100644 --- a/outbound/hysteria.go +++ b/outbound/hysteria.go @@ -117,6 +117,10 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL if down < hysteria.MinSpeedBPS { return nil, E.New("invalid down speed") } + outboundDialer, err := dialer.New(router, options.DialerOptions) + if err != nil { + return nil, err + } return &Hysteria{ myOutboundAdapter: myOutboundAdapter{ protocol: C.TypeHysteria, @@ -127,7 +131,7 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL dependencies: withDialerDependency(options.DialerOptions), }, ctx: ctx, - dialer: dialer.New(router, options.DialerOptions), + dialer: outboundDialer, serverAddr: options.ServerOptions.Build(), tlsConfig: tlsConfig, quicConfig: quicConfig, diff --git a/outbound/shadowsocks.go b/outbound/shadowsocks.go index 2f7d92cb..c8a9b0a8 100644 --- a/outbound/shadowsocks.go +++ b/outbound/shadowsocks.go @@ -39,6 +39,10 @@ func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte if err != nil { return nil, err } + outboundDialer, err := dialer.New(router, options.DialerOptions) + if err != nil { + return nil, err + } outbound := &Shadowsocks{ myOutboundAdapter: myOutboundAdapter{ protocol: C.TypeShadowsocks, @@ -48,7 +52,7 @@ func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte tag: tag, dependencies: withDialerDependency(options.DialerOptions), }, - dialer: dialer.New(router, options.DialerOptions), + dialer: outboundDialer, method: method, serverAddr: options.ServerOptions.Build(), } diff --git a/outbound/shadowsocksr.go b/outbound/shadowsocksr.go index 6b30595d..f8e4e4b3 100644 --- a/outbound/shadowsocksr.go +++ b/outbound/shadowsocksr.go @@ -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) { + outboundDialer, err := dialer.New(router, options.DialerOptions) + if err != nil { + return nil, err + } outbound := &ShadowsocksR{ myOutboundAdapter: myOutboundAdapter{ protocol: C.TypeShadowsocksR, @@ -46,11 +50,10 @@ func NewShadowsocksR(ctx context.Context, router adapter.Router, logger log.Cont tag: tag, dependencies: withDialerDependency(options.DialerOptions), }, - dialer: dialer.New(router, options.DialerOptions), + dialer: outboundDialer, serverAddr: options.ServerOptions.Build(), } var cipher string - var err error switch options.Method { case "none": cipher = "dummy" diff --git a/outbound/shadowtls.go b/outbound/shadowtls.go index 7eeb1bae..9f02124d 100644 --- a/outbound/shadowtls.go +++ b/outbound/shadowtls.go @@ -72,11 +72,15 @@ func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.Context 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{ Version: options.Version, Password: options.Password, Server: options.ServerOptions.Build(), - Dialer: dialer.New(router, options.DialerOptions), + Dialer: outboundDialer, TLSHandshake: tlsHandshakeFunc, Logger: logger, }) diff --git a/outbound/socks.go b/outbound/socks.go index 97579fd3..48107480 100644 --- a/outbound/socks.go +++ b/outbound/socks.go @@ -37,6 +37,10 @@ func NewSocks(router adapter.Router, logger log.ContextLogger, tag string, optio if err != nil { return nil, err } + outboundDialer, err := dialer.New(router, options.DialerOptions) + if err != nil { + return nil, err + } outbound := &Socks{ myOutboundAdapter: myOutboundAdapter{ protocol: C.TypeSOCKS, @@ -46,7 +50,7 @@ func NewSocks(router adapter.Router, logger log.ContextLogger, tag string, optio tag: tag, 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, } uotOptions := common.PtrValueOrDefault(options.UDPOverTCPOptions) diff --git a/outbound/ssh.go b/outbound/ssh.go index 93e5a7bc..0c6a9894 100644 --- a/outbound/ssh.go +++ b/outbound/ssh.go @@ -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) { + outboundDialer, err := dialer.New(router, options.DialerOptions) + if err != nil { + return nil, err + } outbound := &SSH{ myOutboundAdapter: myOutboundAdapter{ protocol: C.TypeSSH, @@ -54,7 +58,7 @@ func NewSSH(ctx context.Context, router adapter.Router, logger log.ContextLogger dependencies: withDialerDependency(options.DialerOptions), }, ctx: ctx, - dialer: dialer.New(router, options.DialerOptions), + dialer: outboundDialer, serverAddr: options.ServerOptions.Build(), user: options.User, hostKeyAlgorithms: options.HostKeyAlgorithms, diff --git a/outbound/tor.go b/outbound/tor.go index 0e81066e..76c7955d 100644 --- a/outbound/tor.go +++ b/outbound/tor.go @@ -66,6 +66,10 @@ func NewTor(ctx context.Context, router adapter.Router, logger log.ContextLogger } startConf.TorrcFile = torrcFile } + outboundDialer, err := dialer.New(router, options.DialerOptions) + if err != nil { + return nil, err + } return &Tor{ myOutboundAdapter: myOutboundAdapter{ protocol: C.TypeTor, @@ -76,7 +80,7 @@ func NewTor(ctx context.Context, router adapter.Router, logger log.ContextLogger dependencies: withDialerDependency(options.DialerOptions), }, ctx: ctx, - proxy: NewProxyListener(ctx, logger, dialer.New(router, options.DialerOptions)), + proxy: NewProxyListener(ctx, logger, outboundDialer), startConf: &startConf, options: options.Options, }, nil diff --git a/outbound/trojan.go b/outbound/trojan.go index a12041e4..db11d105 100644 --- a/outbound/trojan.go +++ b/outbound/trojan.go @@ -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) { + outboundDialer, err := dialer.New(router, options.DialerOptions) + if err != nil { + return nil, err + } outbound := &Trojan{ myOutboundAdapter: myOutboundAdapter{ protocol: C.TypeTrojan, @@ -42,11 +46,10 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog tag: tag, dependencies: withDialerDependency(options.DialerOptions), }, - dialer: dialer.New(router, options.DialerOptions), + dialer: outboundDialer, serverAddr: options.ServerOptions.Build(), key: trojan.Key(options.Password), } - var err error if options.TLS != nil { outbound.tlsConfig, err = tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS)) if err != nil { diff --git a/outbound/tuic.go b/outbound/tuic.go index 42585ab3..3b0ff157 100644 --- a/outbound/tuic.go +++ b/outbound/tuic.go @@ -57,9 +57,13 @@ func NewTUIC(ctx context.Context, router adapter.Router, logger log.ContextLogge case "quic": udpStream = true } + outboundDialer, err := dialer.New(router, options.DialerOptions) + if err != nil { + return nil, err + } client, err := tuic.NewClient(tuic.ClientOptions{ Context: ctx, - Dialer: dialer.New(router, options.DialerOptions), + Dialer: outboundDialer, ServerAddress: options.ServerOptions.Build(), TLSConfig: tlsConfig, UUID: userUUID, diff --git a/outbound/vless.go b/outbound/vless.go index 12574467..4a130403 100644 --- a/outbound/vless.go +++ b/outbound/vless.go @@ -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) { + outboundDialer, err := dialer.New(router, options.DialerOptions) + if err != nil { + return nil, err + } outbound := &VLESS{ myOutboundAdapter: myOutboundAdapter{ protocol: C.TypeVLESS, @@ -45,10 +49,9 @@ func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogg tag: tag, dependencies: withDialerDependency(options.DialerOptions), }, - dialer: dialer.New(router, options.DialerOptions), + dialer: outboundDialer, serverAddr: options.ServerOptions.Build(), } - var err error if options.TLS != nil { outbound.tlsConfig, err = tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS)) if err != nil { diff --git a/outbound/vmess.go b/outbound/vmess.go index 6f7735fc..b07f13d7 100644 --- a/outbound/vmess.go +++ b/outbound/vmess.go @@ -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) { + outboundDialer, err := dialer.New(router, options.DialerOptions) + if err != nil { + return nil, err + } outbound := &VMess{ myOutboundAdapter: myOutboundAdapter{ protocol: C.TypeVMess, @@ -44,10 +48,9 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg tag: tag, dependencies: withDialerDependency(options.DialerOptions), }, - dialer: dialer.New(router, options.DialerOptions), + dialer: outboundDialer, serverAddr: options.ServerOptions.Build(), } - var err error if options.TLS != nil { outbound.tlsConfig, err = tls.NewClient(router, options.Server, common.PtrValueOrDefault(options.TLS)) if err != nil { diff --git a/outbound/wireguard.go b/outbound/wireguard.go index 3627e674..bc24e2e7 100644 --- a/outbound/wireguard.go +++ b/outbound/wireguard.go @@ -65,7 +65,11 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context connectAddr = options.ServerOptions.Build() } } - outbound.bind = wireguard.NewClientBind(ctx, outbound, dialer.New(router, options.DialerOptions), isConnect, connectAddr, reserved) + 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) if len(localPrefixes) == 0 { return nil, E.New("missing local address") @@ -157,7 +161,6 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context mtu = 1408 } var wireTunDevice wireguard.Device - var err error if !options.SystemInterface && tun.WithGVisor { wireTunDevice, err = wireguard.NewStackDevice(localPrefixes, mtu) } else { diff --git a/route/router.go b/route/router.go index ceaedb7f..bf668dd8 100644 --- a/route/router.go +++ b/route/router.go @@ -38,7 +38,9 @@ import ( F "github.com/sagernet/sing/common/format" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" + serviceNTP "github.com/sagernet/sing/common/ntp" "github.com/sagernet/sing/common/uot" + "github.com/sagernet/sing/service" "github.com/sagernet/sing/service/pause" ) @@ -319,7 +321,12 @@ func NewRouter( } } 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 } diff --git a/transport/dhcp/server.go b/transport/dhcp/server.go index e3f1aed8..35e8783f 100644 --- a/transport/dhcp/server.go +++ b/transport/dhcp/server.go @@ -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, UDPFragmentDefault: true, - }) + })) var transports []dns.Transport for _, serverAddr := range serverAddrs { serverTransport, err := dns.NewUDPTransport(t.name, t.ctx, serverDialer, M.Socksaddr{Addr: serverAddr, Port: 53}) diff --git a/transport/wireguard/device_system.go b/transport/wireguard/device_system.go index f2325a9c..98626404 100644 --- a/transport/wireguard/device_system.go +++ b/transport/wireguard/device_system.go @@ -10,6 +10,7 @@ import ( "github.com/sagernet/sing-box/common/dialer" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing-tun" + "github.com/sagernet/sing/common" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" wgTun "github.com/sagernet/wireguard-go/tun" @@ -58,9 +59,9 @@ func NewSystemDevice(router adapter.Router, interfaceName string, localPrefixes inet6Address = inet6Addresses[0].Addr() } return &SystemDevice{ - dialer: dialer.NewDefault(router, option.DialerOptions{ + dialer: common.Must1(dialer.NewDefault(router, option.DialerOptions{ BindInterface: interfaceName, - }), + })), device: tunInterface, name: interfaceName, mtu: int(mtu),