浏览代码

Add netns support

世界 7 月之前
父节点
当前提交
e0e7a9ee96

+ 24 - 17
common/dialer/default.go

@@ -10,6 +10,7 @@ import (
 
 	"github.com/sagernet/sing-box/adapter"
 	"github.com/sagernet/sing-box/common/conntrack"
+	"github.com/sagernet/sing-box/common/listener"
 	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing-box/experimental/libbox/platform"
 	"github.com/sagernet/sing-box/option"
@@ -35,6 +36,7 @@ type DefaultDialer struct {
 	udpListener            net.ListenConfig
 	udpAddr4               string
 	udpAddr6               string
+	netns                  string
 	networkManager         adapter.NetworkManager
 	networkStrategy        *C.NetworkStrategy
 	defaultNetworkStrategy bool
@@ -198,6 +200,7 @@ func NewDefault(ctx context.Context, options option.DialerOptions) (*DefaultDial
 		udpListener:            listener,
 		udpAddr4:               udpAddr4,
 		udpAddr6:               udpAddr6,
+		netns:                  options.NetNs,
 		networkManager:         networkManager,
 		networkStrategy:        networkStrategy,
 		defaultNetworkStrategy: defaultNetworkStrategy,
@@ -214,19 +217,21 @@ func (d *DefaultDialer) DialContext(ctx context.Context, network string, address
 		return nil, E.New("domain not resolved")
 	}
 	if d.networkStrategy == nil {
-		switch N.NetworkName(network) {
-		case N.NetworkUDP:
+		return trackConn(listener.ListenNetworkNamespace[net.Conn](d.netns, func() (net.Conn, error) {
+			switch N.NetworkName(network) {
+			case N.NetworkUDP:
+				if !address.IsIPv6() {
+					return d.udpDialer4.DialContext(ctx, network, address.String())
+				} else {
+					return d.udpDialer6.DialContext(ctx, network, address.String())
+				}
+			}
 			if !address.IsIPv6() {
-				return trackConn(d.udpDialer4.DialContext(ctx, network, address.String()))
+				return DialSlowContext(&d.dialer4, ctx, network, address)
 			} else {
-				return trackConn(d.udpDialer6.DialContext(ctx, network, address.String()))
+				return DialSlowContext(&d.dialer6, ctx, network, address)
 			}
-		}
-		if !address.IsIPv6() {
-			return trackConn(DialSlowContext(&d.dialer4, ctx, network, address))
-		} else {
-			return trackConn(DialSlowContext(&d.dialer6, ctx, network, address))
-		}
+		}))
 	} else {
 		return d.DialParallelInterface(ctx, network, address, d.networkStrategy, d.networkType, d.fallbackNetworkType, d.networkFallbackDelay)
 	}
@@ -282,13 +287,15 @@ func (d *DefaultDialer) DialParallelInterface(ctx context.Context, network strin
 
 func (d *DefaultDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
 	if d.networkStrategy == nil {
-		if destination.IsIPv6() {
-			return trackPacketConn(d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr6))
-		} else if destination.IsIPv4() && !destination.Addr.IsUnspecified() {
-			return trackPacketConn(d.udpListener.ListenPacket(ctx, N.NetworkUDP+"4", d.udpAddr4))
-		} else {
-			return trackPacketConn(d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr4))
-		}
+		return trackPacketConn(listener.ListenNetworkNamespace[net.PacketConn](d.netns, func() (net.PacketConn, error) {
+			if destination.IsIPv6() {
+				return d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr6)
+			} else if destination.IsIPv4() && !destination.Addr.IsUnspecified() {
+				return d.udpListener.ListenPacket(ctx, N.NetworkUDP+"4", d.udpAddr4)
+			} else {
+				return d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr4)
+			}
+		}))
 	} else {
 		return d.ListenSerialInterfacePacket(ctx, destination, d.networkStrategy, d.networkType, d.fallbackNetworkType, d.networkFallbackDelay)
 	}

+ 31 - 0
common/listener/listener.go

@@ -4,6 +4,8 @@ import (
 	"context"
 	"net"
 	"net/netip"
+	"runtime"
+	"strings"
 	"sync/atomic"
 
 	"github.com/sagernet/sing-box/adapter"
@@ -14,6 +16,8 @@ import (
 	"github.com/sagernet/sing/common/logger"
 	M "github.com/sagernet/sing/common/metadata"
 	N "github.com/sagernet/sing/common/network"
+
+	"github.com/vishvananda/netns"
 )
 
 type Listener struct {
@@ -135,3 +139,30 @@ func (l *Listener) UDPConn() *net.UDPConn {
 func (l *Listener) ListenOptions() option.ListenOptions {
 	return l.listenOptions
 }
+
+func ListenNetworkNamespace[T any](nameOrPath string, block func() (T, error)) (T, error) {
+	if nameOrPath != "" {
+		runtime.LockOSThread()
+		defer runtime.UnlockOSThread()
+		currentNs, err := netns.Get()
+		if err != nil {
+			return common.DefaultValue[T](), E.Cause(err, "get current netns")
+		}
+		defer netns.Set(currentNs)
+		var targetNs netns.NsHandle
+		if strings.HasPrefix(nameOrPath, "/") {
+			targetNs, err = netns.GetFromPath(nameOrPath)
+		} else {
+			targetNs, err = netns.GetFromName(nameOrPath)
+		}
+		if err != nil {
+			return common.DefaultValue[T](), E.Cause(err, "get netns ", nameOrPath)
+		}
+		defer targetNs.Close()
+		err = netns.Set(targetNs)
+		if err != nil {
+			return common.DefaultValue[T](), E.Cause(err, "set netns to ", nameOrPath)
+		}
+	}
+	return block()
+}

+ 16 - 14
common/listener/listener_tcp.go

@@ -16,9 +16,12 @@ import (
 )
 
 func (l *Listener) ListenTCP() (net.Listener, error) {
+	//nolint:staticcheck
+	if l.listenOptions.ProxyProtocol || l.listenOptions.ProxyProtocolAcceptNoHeader {
+		return nil, E.New("Proxy Protocol is deprecated and removed in sing-box 1.6.0")
+	}
 	var err error
 	bindAddr := M.SocksaddrFrom(l.listenOptions.Listen.Build(netip.AddrFrom4([4]byte{127, 0, 0, 1})), l.listenOptions.ListenPort)
-	var tcpListener net.Listener
 	var listenConfig net.ListenConfig
 	if l.listenOptions.TCPKeepAlive >= 0 {
 		keepIdle := time.Duration(l.listenOptions.TCPKeepAlive)
@@ -37,20 +40,19 @@ func (l *Listener) ListenTCP() (net.Listener, error) {
 		}
 		setMultiPathTCP(&listenConfig)
 	}
-	if l.listenOptions.TCPFastOpen {
-		var tfoConfig tfo.ListenConfig
-		tfoConfig.ListenConfig = listenConfig
-		tcpListener, err = tfoConfig.Listen(l.ctx, M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.String())
-	} else {
-		tcpListener, err = listenConfig.Listen(l.ctx, M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.String())
-	}
-	if err == nil {
-		l.logger.Info("tcp server started at ", tcpListener.Addr())
-	}
-	//nolint:staticcheck
-	if l.listenOptions.ProxyProtocol || l.listenOptions.ProxyProtocolAcceptNoHeader {
-		return nil, E.New("Proxy Protocol is deprecated and removed in sing-box 1.6.0")
+	tcpListener, err := ListenNetworkNamespace[net.Listener](l.listenOptions.NetNs, func() (net.Listener, error) {
+		if l.listenOptions.TCPFastOpen {
+			var tfoConfig tfo.ListenConfig
+			tfoConfig.ListenConfig = listenConfig
+			return tfoConfig.Listen(l.ctx, M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.String())
+		} else {
+			return listenConfig.Listen(l.ctx, M.NetworkFromNetAddr(N.NetworkTCP, bindAddr.Addr), bindAddr.String())
+		}
+	})
+	if err != nil {
+		return nil, err
 	}
+	l.logger.Info("tcp server started at ", tcpListener.Addr())
 	l.tcpListener = tcpListener
 	return tcpListener, err
 }

+ 11 - 1
common/listener/listener_udp.go

@@ -1,6 +1,7 @@
 package listener
 
 import (
+	"context"
 	"net"
 	"net/netip"
 	"os"
@@ -24,7 +25,9 @@ func (l *Listener) ListenUDP() (net.PacketConn, error) {
 	if !udpFragment {
 		lc.Control = control.Append(lc.Control, control.DisableUDPFragment())
 	}
-	udpConn, err := lc.ListenPacket(l.ctx, M.NetworkFromNetAddr(N.NetworkUDP, bindAddr.Addr), bindAddr.String())
+	udpConn, err := ListenNetworkNamespace[net.PacketConn](l.listenOptions.NetNs, func() (net.PacketConn, error) {
+		return lc.ListenPacket(l.ctx, M.NetworkFromNetAddr(N.NetworkUDP, bindAddr.Addr), bindAddr.String())
+	})
 	if err != nil {
 		return nil, err
 	}
@@ -34,6 +37,13 @@ func (l *Listener) ListenUDP() (net.PacketConn, error) {
 	return udpConn, err
 }
 
+func (l *Listener) ListenPacket(ctx context.Context, network string, address string) (net.PacketConn, error) {
+	return ListenNetworkNamespace[net.PacketConn](l.listenOptions.NetNs, func() (net.PacketConn, error) {
+		var listenConfig net.ListenConfig
+		return listenConfig.ListenPacket(ctx, network, address)
+	})
+}
+
 func (l *Listener) UDPAddr() M.Socksaddr {
 	return l.udpAddr
 }

+ 28 - 16
docs/configuration/shared/dial.md

@@ -5,7 +5,8 @@ icon: material/new-box
 !!! quote "Changes in sing-box 1.12.0"
 
     :material-plus: [domain_resolver](#domain_resolver)  
-    :material-delete-clock: [domain_strategy](#domain_strategy)
+    :material-delete-clock: [domain_strategy](#domain_strategy)  
+    :material-plus: [netns](#netns)
 
 !!! quote "Changes in sing-box 1.11.0"
 
@@ -18,24 +19,25 @@ icon: material/new-box
 
 ```json
 {
-  "detour": "upstream-out",
-  "bind_interface": "en0",
-  "inet4_bind_address": "0.0.0.0",
-  "inet6_bind_address": "::",
-  "routing_mark": 1234,
+  "detour": "",
+  "bind_interface": "",
+  "inet4_bind_address": "",
+  "inet6_bind_address": "",
+  "routing_mark": 0,
   "reuse_addr": false,
-  "connect_timeout": "5s",
+  "connect_timeout": "",
   "tcp_fast_open": false,
   "tcp_multi_path": false,
   "udp_fragment": false,
+  "netns": "",
   "domain_resolver": "", // or {}
-  "network_strategy": "default",
+  "network_strategy": "",
   "network_type": [],
   "fallback_network_type": [],
-  "fallback_delay": "300ms",
+  "fallback_delay": "",
 
   // Deprecated
-  "domain_strategy": "prefer_ipv6"
+  "domain_strategy": ""
 }
 ```
 
@@ -75,6 +77,15 @@ Set netfilter routing mark.
 
 Reuse listener address.
 
+#### connect_timeout
+
+Connect timeout, in golang's Duration format.
+
+A duration string is a possibly signed sequence of
+decimal numbers, each with optional fraction and a unit suffix,
+such as "300ms", "-1.5h" or "2h45m".
+Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
+
 #### tcp_fast_open
 
 Enable TCP Fast Open.
@@ -91,14 +102,15 @@ Enable TCP Multi Path.
 
 Enable UDP fragmentation.
 
-#### connect_timeout
+#### netns
 
-Connect timeout, in golang's Duration format.
+!!! question "Since sing-box 1.12.0"
 
-A duration string is a possibly signed sequence of
-decimal numbers, each with optional fraction and a unit suffix,
-such as "300ms", "-1.5h" or "2h45m".
-Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
+!!! quote ""
+
+    Only supported on Linux.
+
+Set network namespace, name or path.
 
 #### domain_resolver
 

+ 25 - 13
docs/configuration/shared/dial.zh.md

@@ -5,7 +5,8 @@ icon: material/new-box
 !!! quote "sing-box 1.12.0 中的更改"
 
     :material-plus: [domain_resolver](#domain_resolver)  
-    :material-delete-clock: [domain_strategy](#domain_strategy)
+    :material-delete-clock: [domain_strategy](#domain_strategy)  
+    :material-plus: [netns](#netns)
 
 !!! quote "sing-box 1.11.0 中的更改"
 
@@ -18,25 +19,26 @@ icon: material/new-box
 
 ```json
 {
-  "detour": "upstream-out",
-  "bind_interface": "en0",
-  "inet4_bind_address": "0.0.0.0",
-  "inet6_bind_address": "::",
-  "routing_mark": 1234,
+  "detour": "",
+  "bind_interface": "",
+  "inet4_bind_address": "",
+  "inet6_bind_address": "",
+  "routing_mark": 0,
   "reuse_addr": false,
-  "connect_timeout": "5s",
+  "connect_timeout": "",
   "tcp_fast_open": false,
   "tcp_multi_path": false,
   "udp_fragment": false,
+  "netns": "",
   "domain_resolver": "", // 或 {}
   "network_strategy": "",
   "network_type": [],
   "fallback_network_type": [],
-  "fallback_delay": "300ms",
+  "fallback_delay": "",
   
   // 废弃的
 
-  "domain_strategy": "prefer_ipv6"
+  "domain_strategy": ""
 }
 ```
 
@@ -76,6 +78,13 @@ icon: material/new-box
 
 重用监听地址。
 
+#### connect_timeout
+
+连接超时,采用 golang 的 Duration 格式。
+
+持续时间字符串是一个可能有符号的序列十进制数,每个都有可选的分数和单位后缀, 例如 "300ms"、"-1.5h" 或 "2h45m"。
+有效时间单位为 "ns"、"us"(或 "µs")、"ms"、"s"、"m"、"h"。
+
 #### tcp_fast_open
 
 启用 TCP Fast Open。
@@ -92,12 +101,15 @@ icon: material/new-box
 
 启用 UDP 分段。
 
-#### connect_timeout
+#### netns
 
-连接超时,采用 golang 的 Duration 格式。
+!!! question "自 sing-box 1.12.0 起"
 
-持续时间字符串是一个可能有符号的序列十进制数,每个都有可选的分数和单位后缀, 例如 "300ms"、"-1.5h" 或 "2h45m"。
-有效时间单位为 "ns"、"us"(或 "µs")、"ms"、"s"、"m"、"h"。
+!!! quote ""
+
+    仅支持 Linux。
+
+设置网络命名空间,名称或路径。
 
 #### domain_resolver
 

+ 22 - 7
docs/configuration/shared/listen.md

@@ -1,7 +1,11 @@
 ---
-icon: material/delete-clock
+icon: material/new-box
 ---
 
+!!! quote "Changes in sing-box 1.12.0"
+
+    :material-plus: [netns](#netns)
+
 !!! quote "Changes in sing-box 1.11.0"
 
     :material-delete-clock: [sniff](#sniff)  
@@ -14,17 +18,18 @@ icon: material/delete-clock
 
 ```json
 {
-  "listen": "::",
-  "listen_port": 5353,
+  "listen": "",
+  "listen_port": 0,
   "tcp_fast_open": false,
   "tcp_multi_path": false,
   "udp_fragment": false,
-  "udp_timeout": "5m",
-  "detour": "another-in",
+  "udp_timeout": "",
+  "netns": "",
+  "detour": "",
   "sniff": false,
   "sniff_override_destination": false,
-  "sniff_timeout": "300ms",
-  "domain_strategy": "prefer_ipv6",
+  "sniff_timeout": "",
+  "domain_strategy": "",
   "udp_disable_domain_unmapping": false
 }
 ```
@@ -72,6 +77,16 @@ UDP NAT expiration time.
 
 `5m` will be used by default.
 
+#### netns
+
+!!! question "Since sing-box 1.12.0"
+
+!!! quote ""
+
+    Only supported on Linux.
+
+Set network namespace, name or path.
+
 #### detour
 
 If set, connections will be forwarded to the specified inbound.

+ 22 - 7
docs/configuration/shared/listen.zh.md

@@ -1,7 +1,11 @@
 ---
-icon: material/delete-clock
+icon: material/new-box
 ---
 
+!!! quote "Changes in sing-box 1.12.0"
+
+    :material-plus: [netns](#netns)
+
 !!! quote "sing-box 1.11.0 中的更改"
 
     :material-delete-clock: [sniff](#sniff)  
@@ -14,17 +18,18 @@ icon: material/delete-clock
 
 ```json
 {
-  "listen": "::",
-  "listen_port": 5353,
+  "listen": "",
+  "listen_port": 0,
   "tcp_fast_open": false,
   "tcp_multi_path": false,
   "udp_fragment": false,
-  "udp_timeout": "5m",
-  "detour": "another-in",
+  "udp_timeout": "",
+  "netns": "",
+  "detour": "",
   "sniff": false,
   "sniff_override_destination": false,
-  "sniff_timeout": "300ms",
-  "domain_strategy": "prefer_ipv6",
+  "sniff_timeout": "",
+  "domain_strategy": "",
   "udp_disable_domain_unmapping": false
 }
 ```
@@ -73,6 +78,16 @@ UDP NAT 过期时间。
 
 默认使用 `5m`。
 
+#### netns
+
+!!! question "自 sing-box 1.12.0 起"
+
+!!! quote ""
+
+    仅支持 Linux。
+
+设置网络命名空间,名称或路径。
+
 #### detour
 
 如果设置,连接将被转发到指定的入站。

+ 2 - 2
go.mod

@@ -26,7 +26,7 @@ require (
 	github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff
 	github.com/sagernet/quic-go v0.49.0-beta.1
 	github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
-	github.com/sagernet/sing v0.6.4-0.20250316065121-38f666955109
+	github.com/sagernet/sing v0.6.4-0.20250319121229-11d8838dc56d
 	github.com/sagernet/sing-mux v0.3.1
 	github.com/sagernet/sing-quic v0.4.1-beta.1
 	github.com/sagernet/sing-shadowsocks v0.2.7
@@ -41,6 +41,7 @@ require (
 	github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
 	github.com/spf13/cobra v1.8.1
 	github.com/stretchr/testify v1.10.0
+	github.com/vishvananda/netns v0.0.4
 	go.uber.org/zap v1.27.0
 	go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
 	golang.org/x/crypto v0.33.0
@@ -122,7 +123,6 @@ require (
 	github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc // indirect
 	github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 // indirect
 	github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
-	github.com/vishvananda/netns v0.0.4 // indirect
 	github.com/x448/float16 v0.8.4 // indirect
 	github.com/zeebo/blake3 v0.2.4 // indirect
 	go.uber.org/multierr v1.11.0 // indirect

+ 2 - 2
go.sum

@@ -178,8 +178,8 @@ github.com/sagernet/quic-go v0.49.0-beta.1/go.mod h1:uesWD1Ihrldq1M3XtjuEvIUqi8W
 github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
 github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
 github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
-github.com/sagernet/sing v0.6.4-0.20250316065121-38f666955109 h1:clwEzQu0oiapGllEDtbGQjcmQaIAt8DH3EeOHAWyiKs=
-github.com/sagernet/sing v0.6.4-0.20250316065121-38f666955109/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
+github.com/sagernet/sing v0.6.4-0.20250319121229-11d8838dc56d h1:8GJnvXlOBdgCa0spumUzPbMamkEbud4sfNTd8+1YaEg=
+github.com/sagernet/sing v0.6.4-0.20250319121229-11d8838dc56d/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
 github.com/sagernet/sing-mux v0.3.1 h1:kvCc8HyGAskDHDQ0yQvoTi/7J4cZPB/VJMsAM3MmdQI=
 github.com/sagernet/sing-mux v0.3.1/go.mod h1:Mkdz8LnDstthz0HWuA/5foncnDIdcNN5KZ6AdJX+x78=
 github.com/sagernet/sing-quic v0.4.1-beta.1 h1:V2VfMckT3EQR3ZdfSzJgZZDsvfZZH42QAZpnOnHKa0s=

+ 1 - 0
option/inbound.go

@@ -68,6 +68,7 @@ type ListenOptions struct {
 	UDPFragment          *bool              `json:"udp_fragment,omitempty"`
 	UDPFragmentDefault   bool               `json:"-"`
 	UDPTimeout           UDPTimeoutCompat   `json:"udp_timeout,omitempty"`
+	NetNs                string             `json:"netns,omitempty"`
 
 	// Deprecated: removed
 	ProxyProtocol bool `json:"proxy_protocol,omitempty"`

+ 1 - 0
option/outbound.go

@@ -77,6 +77,7 @@ type DialerOptions struct {
 	TCPMultiPath        bool                              `json:"tcp_multi_path,omitempty"`
 	UDPFragment         *bool                             `json:"udp_fragment,omitempty"`
 	UDPFragmentDefault  bool                              `json:"-"`
+	NetNs               string                            `json:"netns,omitempty"`
 	DomainResolver      *DomainResolveOptions             `json:"domain_resolver,omitempty"`
 	NetworkStrategy     *NetworkStrategy                  `json:"network_strategy,omitempty"`
 	NetworkType         badoption.Listable[InterfaceType] `json:"network_type,omitempty"`

+ 5 - 3
option/simple.go

@@ -7,13 +7,15 @@ import (
 
 type SocksInboundOptions struct {
 	ListenOptions
-	Users []auth.User `json:"users,omitempty"`
+	Users          []auth.User           `json:"users,omitempty"`
+	DomainResolver *DomainResolveOptions `json:"domain_resolver,omitempty"`
 }
 
 type HTTPMixedInboundOptions struct {
 	ListenOptions
-	Users          []auth.User `json:"users,omitempty"`
-	SetSystemProxy bool        `json:"set_system_proxy,omitempty"`
+	Users          []auth.User           `json:"users,omitempty"`
+	DomainResolver *DomainResolveOptions `json:"domain_resolver,omitempty"`
+	SetSystemProxy bool                  `json:"set_system_proxy,omitempty"`
 	InboundTLSOptionsContainer
 }
 

+ 1 - 1
protocol/mixed/inbound.go

@@ -85,7 +85,7 @@ func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata ada
 	}
 	switch headerBytes[0] {
 	case socks4.Version, socks5.Version:
-		return socks.HandleConnectionEx(ctx, conn, reader, h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, onClose)
+		return socks.HandleConnectionEx(ctx, conn, reader, h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), h.listener, metadata.Source, onClose)
 	default:
 		return http.HandleConnectionEx(ctx, conn, reader, h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, onClose)
 	}

+ 1 - 1
protocol/socks/inbound.go

@@ -62,7 +62,7 @@ func (h *Inbound) Close() error {
 }
 
 func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
-	err := socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, onClose)
+	err := socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), h.listener, metadata.Source, onClose)
 	N.CloseOnHandshakeFailure(conn, onClose, err)
 	if err != nil {
 		if E.IsClosedOrCanceled(err) {

+ 1 - 1
protocol/tor/proxy.go

@@ -99,7 +99,7 @@ func (l *ProxyListener) acceptLoop() {
 }
 
 func (l *ProxyListener) accept(ctx context.Context, conn *net.TCPConn) error {
-	return socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), l.authenticator, l, M.SocksaddrFromNet(conn.RemoteAddr()), nil)
+	return socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), l.authenticator, l, nil, M.SocksaddrFromNet(conn.RemoteAddr()), nil)
 }
 
 func (l *ProxyListener) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {