世界 11 months ago
parent
commit
5821b974bd

+ 3 - 0
adapter/handler.go

@@ -46,6 +46,9 @@ type PacketConnectionHandlerEx interface {
 	NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata InboundContext, onClose N.CloseHandlerFunc)
 }
 
+// Deprecated: use TCPConnectionHandlerEx instead
+//
+//nolint:staticcheck
 type UpstreamHandlerAdapter interface {
 	N.TCPConnectionHandler
 	N.UDPConnectionHandler

+ 2 - 1
adapter/inbound.go

@@ -65,7 +65,8 @@ type InboundContext struct {
 	LastInbound              string
 	OriginDestination        M.Socksaddr
 	RouteOriginalDestination M.Socksaddr
-	// Deprecated
+	// Deprecated: to be removed
+	//nolint:staticcheck
 	InboundOptions            option.InboundOptions
 	UDPDisableDomainUnmapping bool
 	UDPConnect                bool

+ 22 - 4
adapter/upstream_legacy.go

@@ -18,6 +18,8 @@ type (
 )
 
 // Deprecated
+//
+//nolint:staticcheck
 func NewUpstreamHandler(
 	metadata InboundContext,
 	connectionHandler ConnectionHandlerFunc,
@@ -34,7 +36,9 @@ func NewUpstreamHandler(
 
 var _ UpstreamHandlerAdapter = (*myUpstreamHandlerWrapper)(nil)
 
-// Deprecated
+// Deprecated: use myUpstreamHandlerWrapperEx instead.
+//
+//nolint:staticcheck
 type myUpstreamHandlerWrapper struct {
 	metadata          InboundContext
 	connectionHandler ConnectionHandlerFunc
@@ -42,6 +46,7 @@ type myUpstreamHandlerWrapper struct {
 	errorHandler      E.Handler
 }
 
+// Deprecated: use myUpstreamHandlerWrapperEx instead.
 func (w *myUpstreamHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
 	myMetadata := w.metadata
 	if metadata.Source.IsValid() {
@@ -53,6 +58,7 @@ func (w *myUpstreamHandlerWrapper) NewConnection(ctx context.Context, conn net.C
 	return w.connectionHandler(ctx, conn, myMetadata)
 }
 
+// Deprecated: use myUpstreamHandlerWrapperEx instead.
 func (w *myUpstreamHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
 	myMetadata := w.metadata
 	if metadata.Source.IsValid() {
@@ -64,11 +70,12 @@ func (w *myUpstreamHandlerWrapper) NewPacketConnection(ctx context.Context, conn
 	return w.packetHandler(ctx, conn, myMetadata)
 }
 
+// Deprecated: use myUpstreamHandlerWrapperEx instead.
 func (w *myUpstreamHandlerWrapper) NewError(ctx context.Context, err error) {
 	w.errorHandler.NewError(ctx, err)
 }
 
-// Deprecated
+// Deprecated: removed
 func UpstreamMetadata(metadata InboundContext) M.Metadata {
 	return M.Metadata{
 		Source:      metadata.Source,
@@ -76,14 +83,14 @@ func UpstreamMetadata(metadata InboundContext) M.Metadata {
 	}
 }
 
-// Deprecated
+// Deprecated: Use NewUpstreamContextHandlerEx instead.
 type myUpstreamContextHandlerWrapper struct {
 	connectionHandler ConnectionHandlerFunc
 	packetHandler     PacketConnectionHandlerFunc
 	errorHandler      E.Handler
 }
 
-// Deprecated
+// Deprecated: Use NewUpstreamContextHandlerEx instead.
 func NewUpstreamContextHandler(
 	connectionHandler ConnectionHandlerFunc,
 	packetHandler PacketConnectionHandlerFunc,
@@ -96,6 +103,7 @@ func NewUpstreamContextHandler(
 	}
 }
 
+// Deprecated: Use NewUpstreamContextHandlerEx instead.
 func (w *myUpstreamContextHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
 	myMetadata := ContextFrom(ctx)
 	if metadata.Source.IsValid() {
@@ -107,6 +115,7 @@ func (w *myUpstreamContextHandlerWrapper) NewConnection(ctx context.Context, con
 	return w.connectionHandler(ctx, conn, *myMetadata)
 }
 
+// Deprecated: Use NewUpstreamContextHandlerEx instead.
 func (w *myUpstreamContextHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
 	myMetadata := ContextFrom(ctx)
 	if metadata.Source.IsValid() {
@@ -118,6 +127,7 @@ func (w *myUpstreamContextHandlerWrapper) NewPacketConnection(ctx context.Contex
 	return w.packetHandler(ctx, conn, *myMetadata)
 }
 
+// Deprecated: Use NewUpstreamContextHandlerEx instead.
 func (w *myUpstreamContextHandlerWrapper) NewError(ctx context.Context, err error) {
 	w.errorHandler.NewError(ctx, err)
 }
@@ -149,12 +159,15 @@ func NewRouteContextHandler(
 var _ UpstreamHandlerAdapter = (*routeHandlerWrapper)(nil)
 
 // Deprecated: Use ConnectionRouterEx instead.
+//
+//nolint:staticcheck
 type routeHandlerWrapper struct {
 	metadata InboundContext
 	router   ConnectionRouter
 	logger   logger.ContextLogger
 }
 
+// Deprecated: Use ConnectionRouterEx instead.
 func (w *routeHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
 	myMetadata := w.metadata
 	if metadata.Source.IsValid() {
@@ -166,6 +179,7 @@ func (w *routeHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn,
 	return w.router.RouteConnection(ctx, conn, myMetadata)
 }
 
+// Deprecated: Use ConnectionRouterEx instead.
 func (w *routeHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
 	myMetadata := w.metadata
 	if metadata.Source.IsValid() {
@@ -177,6 +191,7 @@ func (w *routeHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.Pa
 	return w.router.RoutePacketConnection(ctx, conn, myMetadata)
 }
 
+// Deprecated: Use ConnectionRouterEx instead.
 func (w *routeHandlerWrapper) NewError(ctx context.Context, err error) {
 	w.logger.ErrorContext(ctx, err)
 }
@@ -189,6 +204,7 @@ type routeContextHandlerWrapper struct {
 	logger logger.ContextLogger
 }
 
+// Deprecated: Use ConnectionRouterEx instead.
 func (w *routeContextHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
 	myMetadata := ContextFrom(ctx)
 	if metadata.Source.IsValid() {
@@ -200,6 +216,7 @@ func (w *routeContextHandlerWrapper) NewConnection(ctx context.Context, conn net
 	return w.router.RouteConnection(ctx, conn, *myMetadata)
 }
 
+// Deprecated: Use ConnectionRouterEx instead.
 func (w *routeContextHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
 	myMetadata := ContextFrom(ctx)
 	if metadata.Source.IsValid() {
@@ -211,6 +228,7 @@ func (w *routeContextHandlerWrapper) NewPacketConnection(ctx context.Context, co
 	return w.router.RoutePacketConnection(ctx, conn, *myMetadata)
 }
 
+// Deprecated: Use ConnectionRouterEx instead.
 func (w *routeContextHandlerWrapper) NewError(ctx context.Context, err error) {
 	w.logger.ErrorContext(ctx, err)
 }

+ 0 - 3
common/dialer/default_parallel_interface.go

@@ -149,9 +149,6 @@ func (d *DefaultDialer) listenSerialInterfacePacket(ctx context.Context, listene
 	if len(primaryInterfaces)+len(fallbackInterfaces) == 0 {
 		return nil, E.New("no available network interface")
 	}
-	if fallbackDelay == 0 {
-		fallbackDelay = N.DefaultFallbackDelay
-	}
 	var errors []error
 	for _, primaryInterface := range primaryInterfaces {
 		perNetListener := listener

+ 6 - 4
common/mux/router.go

@@ -41,10 +41,10 @@ func NewRouterWithOptions(router adapter.ConnectionRouterEx, logger logger.Conte
 		NewStreamContext: func(ctx context.Context, conn net.Conn) context.Context {
 			return log.ContextWithNewID(ctx)
 		},
-		Logger:  logger,
-		Handler: adapter.NewRouteContextHandler(router, logger),
-		Padding: options.Padding,
-		Brutal:  brutalOptions,
+		Logger:    logger,
+		HandlerEx: adapter.NewRouteContextHandlerEx(router),
+		Padding:   options.Padding,
+		Brutal:    brutalOptions,
 	})
 	if err != nil {
 		return nil, err
@@ -52,6 +52,7 @@ func NewRouterWithOptions(router adapter.ConnectionRouterEx, logger logger.Conte
 	return &Router{router, service}, nil
 }
 
+// Deprecated: Use RouteConnectionEx instead.
 func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
 	if metadata.Destination == mux.Destination {
 		// TODO: check if WithContext is necessary
@@ -61,6 +62,7 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
 	}
 }
 
+// Deprecated: Use RoutePacketConnectionEx instead.
 func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
 	return r.router.RoutePacketConnection(ctx, conn, metadata)
 }

+ 1 - 1
go.mod

@@ -34,7 +34,7 @@ require (
 	github.com/sagernet/sing-shadowsocks2 v0.2.0
 	github.com/sagernet/sing-shadowtls v0.2.0-alpha.2
 	github.com/sagernet/sing-tun v0.6.0-beta.7
-	github.com/sagernet/sing-vmess v0.1.13
+	github.com/sagernet/sing-vmess v0.2.0-beta.2
 	github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
 	github.com/sagernet/utls v1.6.7
 	github.com/sagernet/wireguard-go v0.0.1-beta.5

+ 2 - 2
go.sum

@@ -135,8 +135,8 @@ github.com/sagernet/sing-shadowtls v0.2.0-alpha.2 h1:RPrpgAdkP5td0vLfS5ldvYosFjS
 github.com/sagernet/sing-shadowtls v0.2.0-alpha.2/go.mod h1:0j5XlzKxaWRIEjc1uiSKmVoWb0k+L9QgZVb876+thZA=
 github.com/sagernet/sing-tun v0.6.0-beta.7 h1:FCSX8oGBqb0H57AAvfGeeH/jMGYWCOg6XWkN/oeES+0=
 github.com/sagernet/sing-tun v0.6.0-beta.7/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
-github.com/sagernet/sing-vmess v0.1.13 h1:/GSfD1Rt6/mVfE80WFHNBykNT7KJNWWmvcMP9DoElEs=
-github.com/sagernet/sing-vmess v0.1.13/go.mod h1:D+g+lhv4iOk1Pn08pd3MtUd72khL5+wgEE3xVH8J+ow=
+github.com/sagernet/sing-vmess v0.2.0-beta.2 h1:obAkAL35X7ql4RnGzDg4dBYIRpGXRKqcN4LyLZpZGSs=
+github.com/sagernet/sing-vmess v0.2.0-beta.2/go.mod h1:HGhf9XUdeE2iOWrX0hQNFgXPbKyGlzpeYFyX0c/pykk=
 github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
 github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo=
 github.com/sagernet/utls v1.6.7 h1:Ep3+aJ8FUGGta+II2IEVNUc3EDhaRCZINWkj/LloIA8=

+ 2 - 1
option/tun.go

@@ -35,7 +35,6 @@ type TunInboundOptions struct {
 	IncludeAndroidUser     badoption.Listable[int]          `json:"include_android_user,omitempty"`
 	IncludePackage         badoption.Listable[string]       `json:"include_package,omitempty"`
 	ExcludePackage         badoption.Listable[string]       `json:"exclude_package,omitempty"`
-	EndpointIndependentNat bool                             `json:"endpoint_independent_nat,omitempty"`
 	UDPTimeout             UDPTimeoutCompat                 `json:"udp_timeout,omitempty"`
 	Stack                  string                           `json:"stack,omitempty"`
 	Platform               *TunPlatformOptions              `json:"platform,omitempty"`
@@ -53,6 +52,8 @@ type TunInboundOptions struct {
 	Inet4RouteExcludeAddress badoption.Listable[netip.Prefix] `json:"inet4_route_exclude_address,omitempty"`
 	// Deprecated: merged to RouteExcludeAddress
 	Inet6RouteExcludeAddress badoption.Listable[netip.Prefix] `json:"inet6_route_exclude_address,omitempty"`
+	// Deprecated: removed
+	EndpointIndependentNat bool `json:"endpoint_independent_nat,omitempty"`
 }
 
 type FwMark uint32

+ 8 - 5
protocol/direct/inbound.go

@@ -95,6 +95,9 @@ func (i *Inbound) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) {
 }
 
 func (i *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
+	metadata.Inbound = i.Tag()
+	metadata.InboundType = i.Type()
+	metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr())
 	switch i.overrideOption {
 	case 1:
 		metadata.Destination = i.overrideDestination
@@ -105,11 +108,9 @@ func (i *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata a
 	case 3:
 		metadata.Destination.Port = i.overrideDestination.Port
 	}
-	i.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
-	metadata.Inbound = i.Tag()
-	metadata.InboundType = i.Type()
-	metadata.InboundDetour = i.listener.ListenOptions().Detour
-	metadata.InboundOptions = i.listener.ListenOptions().InboundOptions
+	if i.overrideOption != 0 {
+		i.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
+	}
 	i.router.RouteConnectionEx(ctx, conn, metadata, onClose)
 }
 
@@ -119,7 +120,9 @@ func (i *Inbound) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn,
 	var metadata adapter.InboundContext
 	metadata.Inbound = i.Tag()
 	metadata.InboundType = i.Type()
+	//nolint:staticcheck
 	metadata.InboundDetour = i.listener.ListenOptions().Detour
+	//nolint:staticcheck
 	metadata.InboundOptions = i.listener.ListenOptions().InboundOptions
 	metadata.Source = source
 	metadata.Destination = destination

+ 4 - 0
protocol/direct/outbound.go

@@ -63,9 +63,11 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
 		dialer:               outboundDialer,
 		// loopBack:       newLoopBackDetector(router),
 	}
+	//nolint:staticcheck
 	if options.ProxyProtocol != 0 {
 		return nil, E.New("Proxy Protocol is deprecated and removed in sing-box 1.6.0")
 	}
+	//nolint:staticcheck
 	if options.OverrideAddress != "" && options.OverridePort != 0 {
 		outbound.overrideOption = 1
 		outbound.overrideDestination = M.ParseSocksaddrHostPort(options.OverrideAddress, options.OverridePort)
@@ -161,6 +163,7 @@ func (h *Outbound) DialParallel(ctx context.Context, network string, destination
 	if h.domainStrategy != dns.DomainStrategyAsIS {
 		domainStrategy = h.domainStrategy
 	} else {
+		//nolint:staticcheck
 		domainStrategy = dns.DomainStrategy(metadata.InboundOptions.DomainStrategy)
 	}
 	switch domainStrategy {
@@ -200,6 +203,7 @@ func (h *Outbound) DialParallelNetwork(ctx context.Context, network string, dest
 	if h.domainStrategy != dns.DomainStrategyAsIS {
 		domainStrategy = h.domainStrategy
 	} else {
+		//nolint:staticcheck
 		domainStrategy = dns.DomainStrategy(metadata.InboundOptions.DomainStrategy)
 	}
 	switch domainStrategy {

+ 10 - 20
protocol/http/inbound.go

@@ -82,33 +82,25 @@ func (h *Inbound) Close() error {
 }
 
 func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
-	err := h.newConnection(ctx, conn, metadata, onClose)
-	N.CloseOnHandshakeFailure(conn, onClose, err)
-	if err != nil {
-		if E.IsClosedOrCanceled(err) {
-			h.logger.DebugContext(ctx, "connection closed: ", err)
-		} else {
-			h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
-		}
-	}
-}
-
-func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) error {
-	var err error
 	if h.tlsConfig != nil {
-		conn, err = tls.ServerHandshake(ctx, conn, h.tlsConfig)
+		tlsConn, err := tls.ServerHandshake(ctx, conn, h.tlsConfig)
 		if err != nil {
-			return err
+			N.CloseOnHandshakeFailure(conn, onClose, err)
+			h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source, ": TLS handshake"))
+			return
 		}
+		conn = tlsConn
+	}
+	err := http.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), h.authenticator, nil, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, onClose)
+	if err != nil {
+		N.CloseOnHandshakeFailure(conn, onClose, err)
+		h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
 	}
-	return http.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), h.authenticator, nil, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, onClose)
 }
 
 func (h *Inbound) newUserConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
 	metadata.Inbound = h.Tag()
 	metadata.InboundType = h.Type()
-	metadata.InboundDetour = h.listener.ListenOptions().Detour
-	metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
 	user, loaded := auth.UserFromContext[string](ctx)
 	if !loaded {
 		h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
@@ -123,8 +115,6 @@ func (h *Inbound) newUserConnection(ctx context.Context, conn net.Conn, metadata
 func (h *Inbound) streamUserPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
 	metadata.Inbound = h.Tag()
 	metadata.InboundType = h.Type()
-	metadata.InboundDetour = h.listener.ListenOptions().Detour
-	metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
 	user, loaded := auth.UserFromContext[string](ctx)
 	if !loaded {
 		h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)

+ 4 - 0
protocol/hysteria/inbound.go

@@ -123,7 +123,9 @@ func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, source M.S
 	var metadata adapter.InboundContext
 	metadata.Inbound = h.Tag()
 	metadata.InboundType = h.Type()
+	//nolint:staticcheck
 	metadata.InboundDetour = h.listener.ListenOptions().Detour
+	//nolint:staticcheck
 	metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
 	metadata.OriginDestination = h.listener.UDPAddr()
 	metadata.Source = source
@@ -144,7 +146,9 @@ func (h *Inbound) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn,
 	var metadata adapter.InboundContext
 	metadata.Inbound = h.Tag()
 	metadata.InboundType = h.Type()
+	//nolint:staticcheck
 	metadata.InboundDetour = h.listener.ListenOptions().Detour
+	//nolint:staticcheck
 	metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
 	metadata.OriginDestination = h.listener.UDPAddr()
 	metadata.Source = source

+ 4 - 0
protocol/hysteria2/inbound.go

@@ -134,7 +134,9 @@ func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, source M.S
 	var metadata adapter.InboundContext
 	metadata.Inbound = h.Tag()
 	metadata.InboundType = h.Type()
+	//nolint:staticcheck
 	metadata.InboundDetour = h.listener.ListenOptions().Detour
+	//nolint:staticcheck
 	metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
 	metadata.OriginDestination = h.listener.UDPAddr()
 	metadata.Source = source
@@ -155,7 +157,9 @@ func (h *Inbound) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn,
 	var metadata adapter.InboundContext
 	metadata.Inbound = h.Tag()
 	metadata.InboundType = h.Type()
+	//nolint:staticcheck
 	metadata.InboundDetour = h.listener.ListenOptions().Detour
+	//nolint:staticcheck
 	metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
 	metadata.OriginDestination = h.listener.UDPAddr()
 	metadata.Source = source

+ 0 - 4
protocol/mixed/inbound.go

@@ -94,8 +94,6 @@ func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata ada
 func (h *Inbound) newUserConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
 	metadata.Inbound = h.Tag()
 	metadata.InboundType = h.Type()
-	metadata.InboundDetour = h.listener.ListenOptions().Detour
-	metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
 	user, loaded := auth.UserFromContext[string](ctx)
 	if !loaded {
 		h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
@@ -110,8 +108,6 @@ func (h *Inbound) newUserConnection(ctx context.Context, conn net.Conn, metadata
 func (h *Inbound) streamUserPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
 	metadata.Inbound = h.Tag()
 	metadata.InboundType = h.Type()
-	metadata.InboundDetour = h.listener.ListenOptions().Detour
-	metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
 	user, loaded := auth.UserFromContext[string](ctx)
 	if !loaded {
 		h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)

+ 2 - 0
protocol/naive/inbound.go

@@ -195,7 +195,9 @@ func (n *Inbound) newConnection(ctx context.Context, waitForClose bool, conn net
 	var metadata adapter.InboundContext
 	metadata.Inbound = n.Tag()
 	metadata.InboundType = n.Type()
+	//nolint:staticcheck
 	metadata.InboundDetour = n.listener.ListenOptions().Detour
+	//nolint:staticcheck
 	metadata.InboundOptions = n.listener.ListenOptions().InboundOptions
 	metadata.Source = source
 	metadata.Destination = destination

+ 0 - 2
protocol/redirect/redirect.go

@@ -62,8 +62,6 @@ func (h *Redirect) NewConnectionEx(ctx context.Context, conn net.Conn, metadata
 	}
 	metadata.Inbound = h.Tag()
 	metadata.InboundType = h.Type()
-	metadata.InboundDetour = h.listener.ListenOptions().Detour
-	metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
 	metadata.Destination = M.SocksaddrFromNetIP(destination)
 	h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
 	h.router.RouteConnectionEx(ctx, conn, metadata, onClose)

+ 0 - 2
protocol/redirect/tproxy.go

@@ -106,8 +106,6 @@ func (t *TProxy) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, s
 	var metadata adapter.InboundContext
 	metadata.Inbound = t.Tag()
 	metadata.InboundType = t.Type()
-	metadata.InboundDetour = t.listener.ListenOptions().Detour
-	metadata.InboundOptions = t.listener.ListenOptions().InboundOptions
 	metadata.Source = source
 	metadata.Destination = destination
 	metadata.OriginDestination = t.listener.UDPAddr()

+ 2 - 4
protocol/shadowsocks/inbound.go

@@ -104,6 +104,7 @@ func (h *Inbound) Close() error {
 	return h.listener.Close()
 }
 
+//nolint:staticcheck
 func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
 	err := h.service.NewConnection(ctx, conn, adapter.UpstreamMetadata(metadata))
 	N.CloseOnHandshakeFailure(conn, onClose, err)
@@ -116,6 +117,7 @@ func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata a
 	}
 }
 
+//nolint:staticcheck
 func (h *Inbound) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) {
 	err := h.service.NewPacket(h.ctx, &stubPacketConn{h.listener.PacketWriter()}, buffer, M.Metadata{Source: source})
 	if err != nil {
@@ -127,8 +129,6 @@ func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata ada
 	h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
 	metadata.Inbound = h.Tag()
 	metadata.InboundType = h.Type()
-	metadata.InboundDetour = h.listener.ListenOptions().Detour
-	metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
 	return h.router.RouteConnection(ctx, conn, metadata)
 }
 
@@ -138,8 +138,6 @@ func (h *Inbound) newPacketConnection(ctx context.Context, conn N.PacketConn, me
 	h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
 	metadata.Inbound = h.Tag()
 	metadata.InboundType = h.Type()
-	metadata.InboundDetour = h.listener.ListenOptions().Detour
-	metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
 	return h.router.RoutePacketConnection(ctx, conn, metadata)
 }
 

+ 7 - 0
protocol/shadowsocks/inbound_multi.go

@@ -112,6 +112,7 @@ func (h *MultiInbound) Close() error {
 	return h.listener.Close()
 }
 
+//nolint:staticcheck
 func (h *MultiInbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
 	err := h.service.NewConnection(ctx, conn, adapter.UpstreamMetadata(metadata))
 	N.CloseOnHandshakeFailure(conn, onClose, err)
@@ -124,6 +125,7 @@ func (h *MultiInbound) NewConnectionEx(ctx context.Context, conn net.Conn, metad
 	}
 }
 
+//nolint:staticcheck
 func (h *MultiInbound) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) {
 	err := h.service.NewPacket(h.ctx, &stubPacketConn{h.listener.PacketWriter()}, buffer, M.Metadata{Source: source})
 	if err != nil {
@@ -145,7 +147,9 @@ func (h *MultiInbound) newConnection(ctx context.Context, conn net.Conn, metadat
 	h.logger.InfoContext(ctx, "[", user, "] inbound connection to ", metadata.Destination)
 	metadata.Inbound = h.Tag()
 	metadata.InboundType = h.Type()
+	//nolint:staticcheck
 	metadata.InboundDetour = h.listener.ListenOptions().Detour
+	//nolint:staticcheck
 	metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
 	return h.router.RouteConnection(ctx, conn, metadata)
 }
@@ -166,11 +170,14 @@ func (h *MultiInbound) newPacketConnection(ctx context.Context, conn N.PacketCon
 	h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
 	metadata.Inbound = h.Tag()
 	metadata.InboundType = h.Type()
+	//nolint:staticcheck
 	metadata.InboundDetour = h.listener.ListenOptions().Detour
+	//nolint:staticcheck
 	metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
 	return h.router.RoutePacketConnection(ctx, conn, metadata)
 }
 
+//nolint:staticcheck
 func (h *MultiInbound) NewError(ctx context.Context, err error) {
 	NewError(h.logger, ctx, err)
 }

+ 7 - 0
protocol/shadowsocks/inbound_relay.go

@@ -97,6 +97,7 @@ func (h *RelayInbound) Close() error {
 	return h.listener.Close()
 }
 
+//nolint:staticcheck
 func (h *RelayInbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
 	err := h.service.NewConnection(ctx, conn, adapter.UpstreamMetadata(metadata))
 	N.CloseOnHandshakeFailure(conn, onClose, err)
@@ -109,6 +110,7 @@ func (h *RelayInbound) NewConnectionEx(ctx context.Context, conn net.Conn, metad
 	}
 }
 
+//nolint:staticcheck
 func (h *RelayInbound) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) {
 	err := h.service.NewPacket(h.ctx, &stubPacketConn{h.listener.PacketWriter()}, buffer, M.Metadata{Source: source})
 	if err != nil {
@@ -130,7 +132,9 @@ func (h *RelayInbound) newConnection(ctx context.Context, conn net.Conn, metadat
 	h.logger.InfoContext(ctx, "[", destination, "] inbound connection to ", metadata.Destination)
 	metadata.Inbound = h.Tag()
 	metadata.InboundType = h.Type()
+	//nolint:staticcheck
 	metadata.InboundDetour = h.listener.ListenOptions().Detour
+	//nolint:staticcheck
 	metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
 	return h.router.RouteConnection(ctx, conn, metadata)
 }
@@ -151,11 +155,14 @@ func (h *RelayInbound) newPacketConnection(ctx context.Context, conn N.PacketCon
 	h.logger.InfoContext(ctx, "[", destination, "] inbound packet connection to ", metadata.Destination)
 	metadata.Inbound = h.Tag()
 	metadata.InboundType = h.Type()
+	//nolint:staticcheck
 	metadata.InboundDetour = h.listener.ListenOptions().Detour
+	//nolint:staticcheck
 	metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
 	return h.router.RoutePacketConnection(ctx, conn, metadata)
 }
 
+//nolint:staticcheck
 func (h *RelayInbound) NewError(ctx context.Context, err error) {
 	NewError(h.logger, ctx, err)
 }

+ 2 - 0
protocol/shadowtls/inbound.go

@@ -119,7 +119,9 @@ func (h *inboundHandler) NewConnectionEx(ctx context.Context, conn net.Conn, sou
 	var metadata adapter.InboundContext
 	metadata.Inbound = h.Tag()
 	metadata.InboundType = h.Type()
+	//nolint:staticcheck
 	metadata.InboundDetour = h.listener.ListenOptions().Detour
+	//nolint:staticcheck
 	metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
 	metadata.Source = source
 	metadata.Destination = destination

+ 0 - 4
protocol/socks/inbound.go

@@ -76,8 +76,6 @@ func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata a
 func (h *Inbound) newUserConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
 	metadata.Inbound = h.Tag()
 	metadata.InboundType = h.Type()
-	metadata.InboundDetour = h.listener.ListenOptions().Detour
-	metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
 	user, loaded := auth.UserFromContext[string](ctx)
 	if !loaded {
 		h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
@@ -92,8 +90,6 @@ func (h *Inbound) newUserConnection(ctx context.Context, conn net.Conn, metadata
 func (h *Inbound) streamUserPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
 	metadata.Inbound = h.Tag()
 	metadata.InboundType = h.Type()
-	metadata.InboundDetour = h.listener.ListenOptions().Detour
-	metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
 	user, loaded := auth.UserFromContext[string](ctx)
 	if !loaded {
 		h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)

+ 13 - 11
protocol/tor/proxy.go

@@ -1,13 +1,13 @@
 package tor
 
 import (
+	std_bufio "bufio"
 	"context"
 	"crypto/rand"
 	"encoding/hex"
 	"net"
 
 	"github.com/sagernet/sing-box/adapter"
-	"github.com/sagernet/sing-box/adapter/outbound"
 	"github.com/sagernet/sing-box/log"
 	"github.com/sagernet/sing/common"
 	"github.com/sagernet/sing/common/auth"
@@ -15,12 +15,14 @@ import (
 	M "github.com/sagernet/sing/common/metadata"
 	N "github.com/sagernet/sing/common/network"
 	"github.com/sagernet/sing/protocol/socks"
+	"github.com/sagernet/sing/service"
 )
 
 type ProxyListener struct {
 	ctx           context.Context
 	logger        log.ContextLogger
 	dialer        N.Dialer
+	connection    adapter.ConnectionManager
 	tcpListener   *net.TCPListener
 	username      string
 	password      string
@@ -38,6 +40,7 @@ func NewProxyListener(ctx context.Context, logger log.ContextLogger, dialer N.Di
 		ctx:           ctx,
 		logger:        logger,
 		dialer:        dialer,
+		connection:    service.FromContext[adapter.ConnectionManager](ctx),
 		authenticator: auth.NewAuthenticator([]auth.User{{Username: username, Password: password}}),
 		username:      username,
 		password:      password,
@@ -95,25 +98,24 @@ func (l *ProxyListener) acceptLoop() {
 	}
 }
 
-// TODO: migrate to new api
-//
-//nolint:staticcheck
 func (l *ProxyListener) accept(ctx context.Context, conn *net.TCPConn) error {
-	return socks.HandleConnection(ctx, conn, l.authenticator, l, M.Metadata{})
+	return socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), l.authenticator, nil, l, M.SocksaddrFromNet(conn.RemoteAddr()), M.Socksaddr{}, nil)
 }
 
-func (l *ProxyListener) NewConnection(ctx context.Context, conn net.Conn, upstreamMetadata M.Metadata) error {
+func (l *ProxyListener) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
 	var metadata adapter.InboundContext
+	metadata.Source = source
+	metadata.Destination = destination
 	metadata.Network = N.NetworkTCP
-	metadata.Destination = upstreamMetadata.Destination
 	l.logger.InfoContext(ctx, "proxy connection to ", metadata.Destination)
-	return outbound.NewConnection(ctx, l.dialer, conn, metadata)
+	l.connection.NewConnection(ctx, l.dialer, conn, metadata, onClose)
 }
 
-func (l *ProxyListener) NewPacketConnection(ctx context.Context, conn N.PacketConn, upstreamMetadata M.Metadata) error {
+func (l *ProxyListener) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
 	var metadata adapter.InboundContext
+	metadata.Source = source
+	metadata.Destination = destination
 	metadata.Network = N.NetworkUDP
-	metadata.Destination = upstreamMetadata.Destination
 	l.logger.InfoContext(ctx, "proxy packet connection to ", metadata.Destination)
-	return outbound.NewPacketConnection(ctx, l.dialer, conn, metadata)
+	l.connection.NewPacketConnection(ctx, l.dialer, conn, metadata, onClose)
 }

+ 47 - 49
protocol/trojan/inbound.go

@@ -56,7 +56,7 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
 		}
 		inbound.tlsConfig = tlsConfig
 	}
-	var fallbackHandler N.TCPConnectionHandler
+	var fallbackHandler N.TCPConnectionHandlerEx
 	if options.Fallback != nil && options.Fallback.Server != "" || len(options.FallbackForALPN) > 0 {
 		if options.Fallback != nil && options.Fallback.Server != "" {
 			inbound.fallbackAddr = options.Fallback.Build()
@@ -78,9 +78,9 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
 			}
 			inbound.fallbackAddrTLSNextProto = fallbackAddrNextProto
 		}
-		fallbackHandler = adapter.NewUpstreamContextHandler(inbound.fallbackConnection, nil, nil)
+		fallbackHandler = adapter.NewUpstreamContextHandlerEx(inbound.fallbackConnection, nil)
 	}
-	service := trojan.NewService[int](adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, nil), fallbackHandler, logger)
+	service := trojan.NewService[int](adapter.NewUpstreamContextHandlerEx(inbound.newConnection, inbound.newPacketConnection), fallbackHandler, logger)
 	err := service.UpdateUsers(common.MapIndexed(options.Users, func(index int, it option.TrojanUser) int {
 		return index
 	}), common.Map(options.Users, func(it option.TrojanUser) string {
@@ -158,37 +158,30 @@ func (h *Inbound) Close() error {
 	)
 }
 
-func (h *Inbound) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
-	var err error
+func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
 	if h.tlsConfig != nil && h.transport == nil {
-		conn, err = tls.ServerHandshake(ctx, conn, h.tlsConfig)
+		tlsConn, err := tls.ServerHandshake(ctx, conn, h.tlsConfig)
 		if err != nil {
-			return err
+			N.CloseOnHandshakeFailure(conn, onClose, err)
+			h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source, ": TLS handshake"))
+			return
 		}
+		conn = tlsConn
 	}
-	return h.service.NewConnection(adapter.WithContext(ctx, &metadata), conn, adapter.UpstreamMetadata(metadata))
-}
-
-func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
-	err := h.NewConnection(ctx, conn, metadata)
-	N.CloseOnHandshakeFailure(conn, onClose, err)
+	err := h.service.NewConnection(adapter.WithContext(ctx, &metadata), conn, metadata.Source, onClose)
 	if err != nil {
-		if E.IsClosedOrCanceled(err) {
-			h.logger.DebugContext(ctx, "connection closed: ", err)
-		} else {
-			h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
-		}
+		N.CloseOnHandshakeFailure(conn, onClose, err)
+		h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
 	}
 }
 
-func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
+func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
 	metadata.Inbound = h.Tag()
 	metadata.InboundType = h.Type()
-	metadata.InboundDetour = h.listener.ListenOptions().Detour
-	metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
 	userIndex, loaded := auth.UserFromContext[int](ctx)
 	if !loaded {
-		return os.ErrInvalid
+		N.CloseOnHandshakeFailure(conn, onClose, os.ErrInvalid)
+		return
 	}
 	user := h.users[userIndex].Name
 	if user == "" {
@@ -197,53 +190,54 @@ func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata ada
 		metadata.User = user
 	}
 	h.logger.InfoContext(ctx, "[", user, "] inbound connection to ", metadata.Destination)
-	return h.router.RouteConnection(ctx, conn, metadata)
+	h.router.RouteConnectionEx(ctx, conn, metadata, onClose)
+}
+
+func (h *Inbound) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
+	metadata.Inbound = h.Tag()
+	metadata.InboundType = h.Type()
+	userIndex, loaded := auth.UserFromContext[int](ctx)
+	if !loaded {
+		N.CloseOnHandshakeFailure(conn, onClose, os.ErrInvalid)
+		return
+	}
+	user := h.users[userIndex].Name
+	if user == "" {
+		user = F.ToString(userIndex)
+	} else {
+		metadata.User = user
+	}
+	h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
+	h.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
 }
 
-func (h *Inbound) fallbackConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
+func (h *Inbound) fallbackConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
 	var fallbackAddr M.Socksaddr
 	if len(h.fallbackAddrTLSNextProto) > 0 {
 		if tlsConn, loaded := common.Cast[tls.Conn](conn); loaded {
 			connectionState := tlsConn.ConnectionState()
 			if connectionState.NegotiatedProtocol != "" {
 				if fallbackAddr, loaded = h.fallbackAddrTLSNextProto[connectionState.NegotiatedProtocol]; !loaded {
-					return E.New("fallback disabled for ALPN: ", connectionState.NegotiatedProtocol)
+					h.logger.DebugContext(ctx, "process connection from ", metadata.Source, ": fallback disabled for ALPN: ", connectionState.NegotiatedProtocol)
+					N.CloseOnHandshakeFailure(conn, onClose, os.ErrInvalid)
+					return
 				}
 			}
 		}
 	}
 	if !fallbackAddr.IsValid() {
 		if !h.fallbackAddr.IsValid() {
-			return E.New("fallback disabled by default")
+			h.logger.DebugContext(ctx, "process connection from ", metadata.Source, ": fallback disabled by default")
+			N.CloseOnHandshakeFailure(conn, onClose, os.ErrInvalid)
+			return
 		}
 		fallbackAddr = h.fallbackAddr
 	}
 	metadata.Inbound = h.Tag()
 	metadata.InboundType = h.Type()
-	metadata.InboundDetour = h.listener.ListenOptions().Detour
-	metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
-	h.logger.InfoContext(ctx, "fallback connection to ", fallbackAddr)
 	metadata.Destination = fallbackAddr
-	return h.router.RouteConnection(ctx, conn, metadata)
-}
-
-func (h *Inbound) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
-	metadata.Inbound = h.Tag()
-	metadata.InboundType = h.Type()
-	metadata.InboundDetour = h.listener.ListenOptions().Detour
-	metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
-	userIndex, loaded := auth.UserFromContext[int](ctx)
-	if !loaded {
-		return os.ErrInvalid
-	}
-	user := h.users[userIndex].Name
-	if user == "" {
-		user = F.ToString(userIndex)
-	} else {
-		metadata.User = user
-	}
-	h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
-	return h.router.RoutePacketConnection(ctx, conn, metadata)
+	h.logger.InfoContext(ctx, "fallback connection to ", fallbackAddr)
+	h.router.RouteConnectionEx(ctx, conn, metadata, onClose)
 }
 
 var _ adapter.V2RayServerTransportHandler = (*inboundTransportHandler)(nil)
@@ -254,6 +248,10 @@ func (h *inboundTransportHandler) NewConnectionEx(ctx context.Context, conn net.
 	var metadata adapter.InboundContext
 	metadata.Source = source
 	metadata.Destination = destination
+	//nolint:staticcheck
+	metadata.InboundDetour = h.listener.ListenOptions().Detour
+	//nolint:staticcheck
+	metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
 	h.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
 	(*Inbound)(h).NewConnectionEx(ctx, conn, metadata, onClose)
 }

+ 4 - 0
protocol/tuic/inbound.go

@@ -105,7 +105,9 @@ func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, source M.S
 	var metadata adapter.InboundContext
 	metadata.Inbound = h.Tag()
 	metadata.InboundType = h.Type()
+	//nolint:staticcheck
 	metadata.InboundDetour = h.listener.ListenOptions().Detour
+	//nolint:staticcheck
 	metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
 	metadata.OriginDestination = h.listener.UDPAddr()
 	metadata.Source = source
@@ -126,7 +128,9 @@ func (h *Inbound) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn,
 	var metadata adapter.InboundContext
 	metadata.Inbound = h.Tag()
 	metadata.InboundType = h.Type()
+	//nolint:staticcheck
 	metadata.InboundDetour = h.listener.ListenOptions().Detour
+	//nolint:staticcheck
 	metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
 	metadata.OriginDestination = h.listener.UDPAddr()
 	metadata.Source = source

+ 10 - 10
protocol/tun/inbound.go

@@ -41,11 +41,9 @@ type Inbound struct {
 	router         adapter.Router
 	networkManager adapter.NetworkManager
 	logger         log.ContextLogger
-	// Deprecated
-	inboundOptions option.InboundOptions
-	tunOptions     tun.Options
-	// Deprecated
-	endpointIndependentNat      bool
+	//nolint:staticcheck
+	inboundOptions              option.InboundOptions
+	tunOptions                  tun.Options
 	udpTimeout                  time.Duration
 	stack                       string
 	tunIf                       tun.Tun
@@ -202,11 +200,10 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
 			ExcludePackage:           options.ExcludePackage,
 			InterfaceMonitor:         networkManager.InterfaceMonitor(),
 		},
-		endpointIndependentNat: options.EndpointIndependentNat,
-		udpTimeout:             udpTimeout,
-		stack:                  options.Stack,
-		platformInterface:      service.FromContext[platform.Interface](ctx),
-		platformOptions:        common.PtrValueOrDefault(options.Platform),
+		udpTimeout:        udpTimeout,
+		stack:             options.Stack,
+		platformInterface: service.FromContext[platform.Interface](ctx),
+		platformOptions:   common.PtrValueOrDefault(options.Platform),
 	}
 	if options.AutoRedirect {
 		if !options.AutoRoute {
@@ -436,6 +433,7 @@ func (t *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, source M.S
 	metadata.InboundType = C.TypeTun
 	metadata.Source = source
 	metadata.Destination = destination
+	//nolint:staticcheck
 	metadata.InboundOptions = t.inboundOptions
 	t.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
 	t.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
@@ -449,6 +447,7 @@ func (t *Inbound) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn,
 	metadata.InboundType = C.TypeTun
 	metadata.Source = source
 	metadata.Destination = destination
+	//nolint:staticcheck
 	metadata.InboundOptions = t.inboundOptions
 	t.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source)
 	t.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
@@ -464,6 +463,7 @@ func (t *autoRedirectHandler) NewConnectionEx(ctx context.Context, conn net.Conn
 	metadata.InboundType = C.TypeTun
 	metadata.Source = source
 	metadata.Destination = destination
+	//nolint:staticcheck
 	metadata.InboundOptions = t.inboundOptions
 	t.logger.InfoContext(ctx, "inbound redirect connection from ", metadata.Source)
 	t.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)

+ 18 - 40
protocol/vless/inbound.go

@@ -58,7 +58,7 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
 	if err != nil {
 		return nil, err
 	}
-	service := vless.NewService[int](logger, adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound))
+	service := vless.NewService[int](logger, adapter.NewUpstreamContextHandlerEx(inbound.newConnectionEx, inbound.newPacketConnectionEx))
 	service.UpdateUsers(common.MapIndexed(inbound.users, func(index int, _ option.VLESSUser) int {
 		return index
 	}), common.Map(inbound.users, func(it option.VLESSUser) string {
@@ -138,37 +138,30 @@ func (h *Inbound) Close() error {
 	)
 }
 
-func (h *Inbound) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
-	var err error
+func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
 	if h.tlsConfig != nil && h.transport == nil {
-		conn, err = tls.ServerHandshake(ctx, conn, h.tlsConfig)
+		tlsConn, err := tls.ServerHandshake(ctx, conn, h.tlsConfig)
 		if err != nil {
-			return err
+			N.CloseOnHandshakeFailure(conn, onClose, err)
+			h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source, ": TLS handshake"))
+			return
 		}
+		conn = tlsConn
 	}
-	return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
-}
-
-func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
-	err := h.NewConnection(ctx, conn, metadata)
-	N.CloseOnHandshakeFailure(conn, onClose, err)
+	err := h.service.NewConnection(adapter.WithContext(ctx, &metadata), conn, metadata.Source, onClose)
 	if err != nil {
-		if E.IsClosedOrCanceled(err) {
-			h.logger.DebugContext(ctx, "connection closed: ", err)
-		} else {
-			h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
-		}
+		N.CloseOnHandshakeFailure(conn, onClose, err)
+		h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
 	}
 }
 
-func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
+func (h *Inbound) newConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
 	metadata.Inbound = h.Tag()
 	metadata.InboundType = h.Type()
-	metadata.InboundDetour = h.listener.ListenOptions().Detour
-	metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
 	userIndex, loaded := auth.UserFromContext[int](ctx)
 	if !loaded {
-		return os.ErrInvalid
+		N.CloseOnHandshakeFailure(conn, onClose, os.ErrInvalid)
+		return
 	}
 	user := h.users[userIndex].Name
 	if user == "" {
@@ -177,17 +170,16 @@ func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata ada
 		metadata.User = user
 	}
 	h.logger.InfoContext(ctx, "[", user, "] inbound connection to ", metadata.Destination)
-	return h.router.RouteConnection(ctx, conn, metadata)
+	h.router.RouteConnectionEx(ctx, conn, metadata, onClose)
 }
 
-func (h *Inbound) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
+func (h *Inbound) newPacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
 	metadata.Inbound = h.Tag()
 	metadata.InboundType = h.Type()
-	metadata.InboundDetour = h.listener.ListenOptions().Detour
-	metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
 	userIndex, loaded := auth.UserFromContext[int](ctx)
 	if !loaded {
-		return os.ErrInvalid
+		N.CloseOnHandshakeFailure(conn, onClose, os.ErrInvalid)
+		return
 	}
 	user := h.users[userIndex].Name
 	if user == "" {
@@ -202,7 +194,7 @@ func (h *Inbound) newPacketConnection(ctx context.Context, conn N.PacketConn, me
 	} else {
 		h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
 	}
-	return h.router.RoutePacketConnection(ctx, conn, metadata)
+	h.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
 }
 
 var _ adapter.V2RayServerTransportHandler = (*inboundTransportHandler)(nil)
@@ -216,17 +208,3 @@ func (h *inboundTransportHandler) NewConnectionEx(ctx context.Context, conn net.
 	h.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
 	(*Inbound)(h).NewConnectionEx(ctx, conn, metadata, onClose)
 }
-
-func (h *Inbound) NewError(ctx context.Context, err error) {
-	NewError(h.logger, ctx, err)
-}
-
-// Deprecated: remove
-func NewError(logger logger.ContextLogger, ctx context.Context, err error) {
-	common.Close(err)
-	if E.IsClosedOrCanceled(err) {
-		logger.DebugContext(ctx, "connection closed: ", err)
-		return
-	}
-	logger.ErrorContext(ctx, err)
-}

+ 18 - 40
protocol/vmess/inbound.go

@@ -65,7 +65,7 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
 	if options.Transport != nil && options.Transport.Type != "" {
 		serviceOptions = append(serviceOptions, vmess.ServiceWithDisableHeaderProtection())
 	}
-	service := vmess.NewService[int](adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound), serviceOptions...)
+	service := vmess.NewService[int](adapter.NewUpstreamContextHandlerEx(inbound.newConnectionEx, inbound.newPacketConnectionEx), serviceOptions...)
 	inbound.service = service
 	err = service.UpdateUsers(common.MapIndexed(options.Users, func(index int, it option.VMessUser) int {
 		return index
@@ -152,37 +152,30 @@ func (h *Inbound) Close() error {
 	)
 }
 
-func (h *Inbound) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
-	var err error
+func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
 	if h.tlsConfig != nil && h.transport == nil {
-		conn, err = tls.ServerHandshake(ctx, conn, h.tlsConfig)
+		tlsConn, err := tls.ServerHandshake(ctx, conn, h.tlsConfig)
 		if err != nil {
-			return err
+			N.CloseOnHandshakeFailure(conn, onClose, err)
+			h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source, ": TLS handshake"))
+			return
 		}
+		conn = tlsConn
 	}
-	return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata))
-}
-
-func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
-	err := h.NewConnection(ctx, conn, metadata)
-	N.CloseOnHandshakeFailure(conn, onClose, err)
+	err := h.service.NewConnection(adapter.WithContext(ctx, &metadata), conn, metadata.Source, onClose)
 	if err != nil {
-		if E.IsClosedOrCanceled(err) {
-			h.logger.DebugContext(ctx, "connection closed: ", err)
-		} else {
-			h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
-		}
+		N.CloseOnHandshakeFailure(conn, onClose, err)
+		h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))
 	}
 }
 
-func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
+func (h *Inbound) newConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
 	metadata.Inbound = h.Tag()
 	metadata.InboundType = h.Type()
-	metadata.InboundDetour = h.listener.ListenOptions().Detour
-	metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
 	userIndex, loaded := auth.UserFromContext[int](ctx)
 	if !loaded {
-		return os.ErrInvalid
+		N.CloseOnHandshakeFailure(conn, onClose, os.ErrInvalid)
+		return
 	}
 	user := h.users[userIndex].Name
 	if user == "" {
@@ -191,17 +184,16 @@ func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata ada
 		metadata.User = user
 	}
 	h.logger.InfoContext(ctx, "[", user, "] inbound connection to ", metadata.Destination)
-	return h.router.RouteConnection(ctx, conn, metadata)
+	h.router.RouteConnectionEx(ctx, conn, metadata, onClose)
 }
 
-func (h *Inbound) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
+func (h *Inbound) newPacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
 	metadata.Inbound = h.Tag()
 	metadata.InboundType = h.Type()
-	metadata.InboundDetour = h.listener.ListenOptions().Detour
-	metadata.InboundOptions = h.listener.ListenOptions().InboundOptions
 	userIndex, loaded := auth.UserFromContext[int](ctx)
 	if !loaded {
-		return os.ErrInvalid
+		N.CloseOnHandshakeFailure(conn, onClose, os.ErrInvalid)
+		return
 	}
 	user := h.users[userIndex].Name
 	if user == "" {
@@ -216,7 +208,7 @@ func (h *Inbound) newPacketConnection(ctx context.Context, conn N.PacketConn, me
 	} else {
 		h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination)
 	}
-	return h.router.RoutePacketConnection(ctx, conn, metadata)
+	h.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
 }
 
 var _ adapter.V2RayServerTransportHandler = (*inboundTransportHandler)(nil)
@@ -230,17 +222,3 @@ func (h *inboundTransportHandler) NewConnectionEx(ctx context.Context, conn net.
 	h.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
 	(*Inbound)(h).NewConnectionEx(ctx, conn, metadata, onClose)
 }
-
-func (h *Inbound) NewError(ctx context.Context, err error) {
-	NewError(h.logger, ctx, err)
-}
-
-// Deprecated: remove
-func NewError(logger logger.ContextLogger, ctx context.Context, err error) {
-	common.Close(err)
-	if E.IsClosedOrCanceled(err) {
-		logger.DebugContext(ctx, "connection closed: ", err)
-		return
-	}
-	logger.ErrorContext(ctx, err)
-}

+ 1 - 1
route/network.go

@@ -202,7 +202,7 @@ func (r *NetworkManager) Close() error {
 		})
 		monitor.Finish()
 	}
-	return nil
+	return err
 }
 
 func (r *NetworkManager) InterfaceFinder() control.InterfaceFinder {

+ 3 - 1
route/route.go

@@ -53,6 +53,7 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
 		return E.New("reject connection to ", metadata.Destination, " while device paused")
 	}
 
+	//nolint:staticcheck
 	if metadata.InboundDetour != "" {
 		if metadata.LastInbound == metadata.InboundDetour {
 			return E.New("routing loop on detour: ", metadata.InboundDetour)
@@ -182,6 +183,7 @@ func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, m
 	if r.pauseManager.IsDevicePaused() {
 		return E.New("reject packet connection to ", metadata.Destination, " while device paused")
 	}
+	//nolint:staticcheck
 	if metadata.InboundDetour != "" {
 		if metadata.LastInbound == metadata.InboundDetour {
 			return E.New("routing loop on detour: ", metadata.InboundDetour)
@@ -281,7 +283,7 @@ func (r *Router) PreMatch(metadata adapter.InboundContext) error {
 	if !isReject {
 		return nil
 	}
-	return rejectAction.Error(nil)
+	return rejectAction.Error(context.Background())
 }
 
 func (r *Router) matchRule(

+ 14 - 8
transport/trojan/mux.go

@@ -4,17 +4,19 @@ import (
 	std_bufio "bufio"
 	"context"
 	"net"
+	"os"
 
 	"github.com/sagernet/sing/common/buf"
 	"github.com/sagernet/sing/common/bufio"
 	E "github.com/sagernet/sing/common/exceptions"
 	"github.com/sagernet/sing/common/logger"
 	M "github.com/sagernet/sing/common/metadata"
+	N "github.com/sagernet/sing/common/network"
 	"github.com/sagernet/sing/common/task"
 	"github.com/sagernet/smux"
 )
 
-func HandleMuxConnection(ctx context.Context, conn net.Conn, metadata M.Metadata, handler Handler, logger logger.ContextLogger) error {
+func HandleMuxConnection(ctx context.Context, conn net.Conn, source M.Socksaddr, handler Handler, logger logger.ContextLogger, onClose N.CloseHandlerFunc) error {
 	session, err := smux.Server(conn, smuxConfig())
 	if err != nil {
 		return err
@@ -27,29 +29,32 @@ func HandleMuxConnection(ctx context.Context, conn net.Conn, metadata M.Metadata
 			if err != nil {
 				return err
 			}
-			go newMuxConnection(ctx, stream, metadata, handler, logger)
+			go newMuxConnection(ctx, stream, source, handler, logger)
 		}
 	})
 	group.Cleanup(func() {
 		session.Close()
+		if onClose != nil {
+			onClose(os.ErrClosed)
+		}
 	})
 	return group.Run(ctx)
 }
 
-func newMuxConnection(ctx context.Context, conn net.Conn, metadata M.Metadata, handler Handler, logger logger.ContextLogger) {
-	err := newMuxConnection0(ctx, conn, metadata, handler)
+func newMuxConnection(ctx context.Context, conn net.Conn, source M.Socksaddr, handler Handler, logger logger.ContextLogger) {
+	err := newMuxConnection0(ctx, conn, source, handler)
 	if err != nil {
 		logger.ErrorContext(ctx, E.Cause(err, "process trojan-go multiplex connection"))
 	}
 }
 
-func newMuxConnection0(ctx context.Context, conn net.Conn, metadata M.Metadata, handler Handler) error {
+func newMuxConnection0(ctx context.Context, conn net.Conn, source M.Socksaddr, handler Handler) error {
 	reader := std_bufio.NewReader(conn)
 	command, err := reader.ReadByte()
 	if err != nil {
 		return E.Cause(err, "read command")
 	}
-	metadata.Destination, err = M.SocksaddrSerializer.ReadAddrPort(reader)
+	destination, err := M.SocksaddrSerializer.ReadAddrPort(reader)
 	if err != nil {
 		return E.Cause(err, "read destination")
 	}
@@ -63,12 +68,13 @@ func newMuxConnection0(ctx context.Context, conn net.Conn, metadata M.Metadata,
 	}
 	switch command {
 	case CommandTCP:
-		return handler.NewConnection(ctx, conn, metadata)
+		handler.NewConnectionEx(ctx, conn, source, destination, nil)
 	case CommandUDP:
-		return handler.NewPacketConnection(ctx, &PacketConn{Conn: conn}, metadata)
+		handler.NewPacketConnectionEx(ctx, &PacketConn{Conn: conn}, source, destination, nil)
 	default:
 		return E.New("unknown command ", command)
 	}
+	return nil
 }
 
 func smuxConfig() *smux.Config {

+ 14 - 15
transport/trojan/service.go

@@ -16,19 +16,19 @@ import (
 )
 
 type Handler interface {
-	N.TCPConnectionHandler
-	N.UDPConnectionHandler
+	N.TCPConnectionHandlerEx
+	N.UDPConnectionHandlerEx
 }
 
 type Service[K comparable] struct {
 	users           map[K][56]byte
 	keys            map[[56]byte]K
 	handler         Handler
-	fallbackHandler N.TCPConnectionHandler
+	fallbackHandler N.TCPConnectionHandlerEx
 	logger          logger.ContextLogger
 }
 
-func NewService[K comparable](handler Handler, fallbackHandler N.TCPConnectionHandler, logger logger.ContextLogger) *Service[K] {
+func NewService[K comparable](handler Handler, fallbackHandler N.TCPConnectionHandlerEx, logger logger.ContextLogger) *Service[K] {
 	return &Service[K]{
 		users:           make(map[K][56]byte),
 		keys:            make(map[[56]byte]K),
@@ -59,19 +59,19 @@ func (s *Service[K]) UpdateUsers(userList []K, passwordList []string) error {
 	return nil
 }
 
-func (s *Service[K]) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
+func (s *Service[K]) NewConnection(ctx context.Context, conn net.Conn, source M.Socksaddr, onClose N.CloseHandlerFunc) error {
 	var key [KeyLength]byte
 	n, err := conn.Read(key[:])
 	if err != nil {
 		return err
 	} else if n != KeyLength {
-		return s.fallback(ctx, conn, metadata, key[:n], E.New("bad request size"))
+		return s.fallback(ctx, conn, source, key[:n], E.New("bad request size"), onClose)
 	}
 
 	if user, loaded := s.keys[key]; loaded {
 		ctx = auth.ContextWithUser(ctx, user)
 	} else {
-		return s.fallback(ctx, conn, metadata, key[:], E.New("bad request"))
+		return s.fallback(ctx, conn, source, key[:], E.New("bad request"), onClose)
 	}
 
 	err = rw.SkipN(conn, 2)
@@ -102,26 +102,25 @@ func (s *Service[K]) NewConnection(ctx context.Context, conn net.Conn, metadata
 		return E.Cause(err, "skip crlf")
 	}
 
-	metadata.Protocol = "trojan"
-	metadata.Destination = destination
-
 	switch command {
 	case CommandTCP:
-		return s.handler.NewConnection(ctx, conn, metadata)
+		s.handler.NewConnectionEx(ctx, conn, source, destination, onClose)
 	case CommandUDP:
-		return s.handler.NewPacketConnection(ctx, &PacketConn{Conn: conn}, metadata)
+		s.handler.NewPacketConnectionEx(ctx, &PacketConn{Conn: conn}, source, destination, onClose)
 	// case CommandMux:
 	default:
-		return HandleMuxConnection(ctx, conn, metadata, s.handler, s.logger)
+		return HandleMuxConnection(ctx, conn, source, s.handler, s.logger, onClose)
 	}
+	return nil
 }
 
-func (s *Service[K]) fallback(ctx context.Context, conn net.Conn, metadata M.Metadata, header []byte, err error) error {
+func (s *Service[K]) fallback(ctx context.Context, conn net.Conn, source M.Socksaddr, header []byte, err error, onClose N.CloseHandlerFunc) error {
 	if s.fallbackHandler == nil {
 		return E.Extend(err, "fallback disabled")
 	}
 	conn = bufio.NewCachedConn(conn, buf.As(header).ToOwned())
-	return s.fallbackHandler.NewConnection(ctx, conn, metadata)
+	s.fallbackHandler.NewConnectionEx(ctx, conn, source, M.Socksaddr{}, onClose)
+	return nil
 }
 
 type PacketConn struct {