From bcb903f94d1d685f8a6084de8c0cc1fdafeaf066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Mon, 21 Oct 2024 23:38:34 +0800 Subject: [PATCH] Crazy sekai overturns the small pond --- adapter/conn_router.go | 104 ---- adapter/handler.go | 26 + adapter/inbound.go | 35 +- adapter/outbound.go | 5 - adapter/router.go | 35 +- adapter/rule.go | 38 ++ adapter/upstream.go | 179 ++++-- adapter/upstream_legacy.go | 216 +++++++ adapter/v2ray.go | 4 +- cmd/sing-box/cmd_rule_set_match.go | 4 +- .../convertor/adguard/convertor_test.go | 8 +- common/mux/router.go | 17 +- common/mux/v2ray_legacy.go | 32 - common/sniff/sniff.go | 2 +- common/uot/router.go | 39 +- constant/rule.go | 15 + experimental/clashapi/rules.go | 3 +- .../clashapi/trafficontrol/tracker.go | 35 +- go.mod | 12 +- go.sum | 24 +- inbound/default.go | 51 +- inbound/default_tcp.go | 18 +- inbound/default_udp.go | 87 +-- inbound/direct.go | 49 +- inbound/http.go | 39 +- inbound/mixed.go | 34 +- inbound/naive.go | 26 +- inbound/redirect.go | 9 +- inbound/shadowsocks.go | 39 +- inbound/shadowsocks_multi.go | 33 +- inbound/shadowsocks_relay.go | 31 +- inbound/shadowtls.go | 9 + inbound/socks.go | 19 +- inbound/tproxy.go | 43 +- inbound/trojan.go | 26 +- inbound/tun.go | 98 +-- inbound/vless.go | 26 +- inbound/vmess.go | 26 +- include/quic_stub.go | 3 +- option/inbound.go | 1 + option/rule.go | 50 +- option/rule_action.go | 166 +++++ option/rule_dns.go | 59 +- option/types.go | 26 +- outbound/block.go | 2 + outbound/default.go | 10 +- outbound/direct.go | 14 +- outbound/dns.go | 2 + outbound/http.go | 8 - outbound/hysteria.go | 8 - outbound/hysteria2.go | 8 - outbound/proxy.go | 3 + outbound/selector.go | 16 +- outbound/shadowsocks.go | 8 - outbound/shadowtls.go | 8 - outbound/socks.go | 4 + outbound/ssh.go | 8 - outbound/tor.go | 8 - outbound/trojan.go | 8 - outbound/tuic.go | 8 - outbound/urltest.go | 4 + outbound/vless.go | 8 - outbound/vmess.go | 8 - outbound/wireguard.go | 4 + ...uter_geo_resources.go => geo_resources.go} | 3 +- route/route.go | 583 ++++++++++++++++++ route/{router_dns.go => route_dns.go} | 105 ++-- route/router.go | 394 +----------- route/{ => rule}/rule_abstract.go | 20 +- route/rule/rule_action.go | 228 +++++++ route/{ => rule}/rule_default.go | 48 +- route/{ => rule}/rule_dns.go | 63 +- route/{ => rule}/rule_headless.go | 2 +- route/{ => rule}/rule_item_adguard.go | 2 +- route/{ => rule}/rule_item_auth_user.go | 2 +- route/{ => rule}/rule_item_cidr.go | 2 +- route/{ => rule}/rule_item_clash_mode.go | 2 +- route/{ => rule}/rule_item_client.go | 2 +- route/{ => rule}/rule_item_domain.go | 2 +- route/{ => rule}/rule_item_domain_keyword.go | 2 +- route/{ => rule}/rule_item_domain_regex.go | 2 +- route/{ => rule}/rule_item_geoip.go | 2 +- route/{ => rule}/rule_item_geosite.go | 2 +- route/{ => rule}/rule_item_inbound.go | 2 +- route/{ => rule}/rule_item_ip_is_private.go | 2 +- route/{ => rule}/rule_item_ipversion.go | 2 +- route/{ => rule}/rule_item_network.go | 2 +- route/{ => rule}/rule_item_outbound.go | 2 +- route/{ => rule}/rule_item_package_name.go | 2 +- route/{ => rule}/rule_item_port.go | 2 +- route/{ => rule}/rule_item_port_range.go | 2 +- route/{ => rule}/rule_item_process_name.go | 2 +- route/{ => rule}/rule_item_process_path.go | 2 +- .../rule_item_process_path_regex.go | 2 +- route/{ => rule}/rule_item_protocol.go | 2 +- route/{ => rule}/rule_item_query_type.go | 2 +- route/{ => rule}/rule_item_rule_set.go | 2 +- route/{ => rule}/rule_item_user.go | 2 +- route/{ => rule}/rule_item_user_id.go | 2 +- route/{ => rule}/rule_item_wifi_bssid.go | 2 +- route/{ => rule}/rule_item_wifi_ssid.go | 2 +- route/{ => rule}/rule_set.go | 30 +- route/{ => rule}/rule_set_local.go | 2 +- route/{ => rule}/rule_set_remote.go | 2 +- route/{router_rule.go => rule_conds.go} | 24 - test/brutal_test.go | 52 +- test/clash_test.go | 12 +- test/direct_test.go | 13 +- test/docker_test.go | 9 +- test/domain_inbound_test.go | 13 +- test/ech_test.go | 38 +- test/go.mod | 85 ++- test/go.sum | 189 +++--- test/http_test.go | 13 +- test/hysteria2_test.go | 12 +- test/hysteria_test.go | 13 +- test/inbound_detour_test.go | 13 +- test/mux_cool_test.go | 13 +- test/mux_test.go | 26 +- test/shadowsocks_test.go | 39 +- test/shadowtls_test.go | 63 +- test/tfo_test.go | 13 +- test/tls_test.go | 13 +- test/trojan_test.go | 26 +- test/tuic_test.go | 13 +- test/v2ray_transport_test.go | 52 +- test/vmess_test.go | 12 +- transport/v2ray/grpc.go | 7 +- transport/v2ray/grpc_lite.go | 5 +- transport/v2ray/quic.go | 5 +- transport/v2ray/transport.go | 15 +- transport/v2raygrpc/client.go | 2 +- transport/v2raygrpc/conn.go | 10 +- transport/v2raygrpc/server.go | 25 +- transport/v2raygrpclite/server.go | 32 +- transport/v2rayhttp/server.go | 21 +- transport/v2rayhttpupgrade/server.go | 14 +- transport/v2rayquic/server.go | 12 +- transport/v2raywebsocket/server.go | 18 +- 139 files changed, 2852 insertions(+), 1554 deletions(-) delete mode 100644 adapter/conn_router.go create mode 100644 adapter/rule.go create mode 100644 adapter/upstream_legacy.go delete mode 100644 common/mux/v2ray_legacy.go create mode 100644 option/rule_action.go rename route/{router_geo_resources.go => geo_resources.go} (98%) create mode 100644 route/route.go rename route/{router_dns.go => route_dns.go} (75%) rename route/{ => rule}/rule_abstract.go (94%) create mode 100644 route/rule/rule_action.go rename route/{ => rule}/rule_default.go (88%) rename route/{ => rule}/rule_dns.go (89%) rename route/{ => rule}/rule_headless.go (99%) rename route/{ => rule}/rule_item_adguard.go (98%) rename route/{ => rule}/rule_item_auth_user.go (98%) rename route/{ => rule}/rule_item_cidr.go (99%) rename route/{ => rule}/rule_item_clash_mode.go (97%) rename route/{ => rule}/rule_item_client.go (98%) rename route/{ => rule}/rule_item_domain.go (99%) rename route/{ => rule}/rule_item_domain_keyword.go (98%) rename route/{ => rule}/rule_item_domain_regex.go (99%) rename route/{ => rule}/rule_item_geoip.go (99%) rename route/{ => rule}/rule_item_geosite.go (98%) rename route/{ => rule}/rule_item_inbound.go (98%) rename route/{ => rule}/rule_item_ip_is_private.go (98%) rename route/{ => rule}/rule_item_ipversion.go (97%) rename route/{ => rule}/rule_item_network.go (98%) rename route/{ => rule}/rule_item_outbound.go (98%) rename route/{ => rule}/rule_item_package_name.go (98%) rename route/{ => rule}/rule_item_port.go (98%) rename route/{ => rule}/rule_item_port_range.go (99%) rename route/{ => rule}/rule_item_process_name.go (98%) rename route/{ => rule}/rule_item_process_path.go (98%) rename route/{ => rule}/rule_item_process_path_regex.go (98%) rename route/{ => rule}/rule_item_protocol.go (98%) rename route/{ => rule}/rule_item_query_type.go (98%) rename route/{ => rule}/rule_item_rule_set.go (99%) rename route/{ => rule}/rule_item_user.go (98%) rename route/{ => rule}/rule_item_user_id.go (98%) rename route/{ => rule}/rule_item_wifi_bssid.go (98%) rename route/{ => rule}/rule_item_wifi_ssid.go (98%) rename route/{ => rule}/rule_set.go (60%) rename route/{ => rule}/rule_set_local.go (99%) rename route/{ => rule}/rule_set_remote.go (99%) rename route/{router_rule.go => rule_conds.go} (78%) diff --git a/adapter/conn_router.go b/adapter/conn_router.go deleted file mode 100644 index a87c45e8..00000000 --- a/adapter/conn_router.go +++ /dev/null @@ -1,104 +0,0 @@ -package adapter - -import ( - "context" - "net" - - "github.com/sagernet/sing/common/logger" - M "github.com/sagernet/sing/common/metadata" - N "github.com/sagernet/sing/common/network" -) - -type ConnectionRouter interface { - RouteConnection(ctx context.Context, conn net.Conn, metadata InboundContext) error - RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext) error -} - -func NewRouteHandler( - metadata InboundContext, - router ConnectionRouter, - logger logger.ContextLogger, -) UpstreamHandlerAdapter { - return &routeHandlerWrapper{ - metadata: metadata, - router: router, - logger: logger, - } -} - -func NewRouteContextHandler( - router ConnectionRouter, - logger logger.ContextLogger, -) UpstreamHandlerAdapter { - return &routeContextHandlerWrapper{ - router: router, - logger: logger, - } -} - -var _ UpstreamHandlerAdapter = (*routeHandlerWrapper)(nil) - -type routeHandlerWrapper struct { - metadata InboundContext - router ConnectionRouter - logger logger.ContextLogger -} - -func (w *routeHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error { - myMetadata := w.metadata - if metadata.Source.IsValid() { - myMetadata.Source = metadata.Source - } - if metadata.Destination.IsValid() { - myMetadata.Destination = metadata.Destination - } - return w.router.RouteConnection(ctx, conn, myMetadata) -} - -func (w *routeHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error { - myMetadata := w.metadata - if metadata.Source.IsValid() { - myMetadata.Source = metadata.Source - } - if metadata.Destination.IsValid() { - myMetadata.Destination = metadata.Destination - } - return w.router.RoutePacketConnection(ctx, conn, myMetadata) -} - -func (w *routeHandlerWrapper) NewError(ctx context.Context, err error) { - w.logger.ErrorContext(ctx, err) -} - -var _ UpstreamHandlerAdapter = (*routeContextHandlerWrapper)(nil) - -type routeContextHandlerWrapper struct { - router ConnectionRouter - logger logger.ContextLogger -} - -func (w *routeContextHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error { - myMetadata := ContextFrom(ctx) - if metadata.Source.IsValid() { - myMetadata.Source = metadata.Source - } - if metadata.Destination.IsValid() { - myMetadata.Destination = metadata.Destination - } - return w.router.RouteConnection(ctx, conn, *myMetadata) -} - -func (w *routeContextHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error { - myMetadata := ContextFrom(ctx) - if metadata.Source.IsValid() { - myMetadata.Source = metadata.Source - } - if metadata.Destination.IsValid() { - myMetadata.Destination = metadata.Destination - } - return w.router.RoutePacketConnection(ctx, conn, *myMetadata) -} - -func (w *routeContextHandlerWrapper) NewError(ctx context.Context, err error) { - w.logger.ErrorContext(ctx, err) -} diff --git a/adapter/handler.go b/adapter/handler.go index bc5bcfbb..6da158ab 100644 --- a/adapter/handler.go +++ b/adapter/handler.go @@ -6,27 +6,53 @@ import ( "github.com/sagernet/sing/common/buf" E "github.com/sagernet/sing/common/exceptions" + M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" ) +// Deprecated type ConnectionHandler interface { NewConnection(ctx context.Context, conn net.Conn, metadata InboundContext) error } +type ConnectionHandlerEx interface { + NewConnectionEx(ctx context.Context, conn net.Conn, metadata InboundContext, onClose N.CloseHandlerFunc) +} + +// Deprecated: use PacketHandlerEx instead type PacketHandler interface { NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata InboundContext) error } +type PacketHandlerEx interface { + NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) +} + +// Deprecated: use OOBPacketHandlerEx instead type OOBPacketHandler interface { NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, oob []byte, metadata InboundContext) error } +type OOBPacketHandlerEx interface { + NewPacketEx(buffer *buf.Buffer, oob []byte, source M.Socksaddr) +} + +// Deprecated type PacketConnectionHandler interface { NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext) error } +type PacketConnectionHandlerEx interface { + NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata InboundContext, onClose N.CloseHandlerFunc) +} + type UpstreamHandlerAdapter interface { N.TCPConnectionHandler N.UDPConnectionHandler E.Handler } + +type UpstreamHandlerAdapterEx interface { + N.TCPConnectionHandlerEx + N.UDPConnectionHandlerEx +} diff --git a/adapter/inbound.go b/adapter/inbound.go index 82909d01..f4d5802f 100644 --- a/adapter/inbound.go +++ b/adapter/inbound.go @@ -2,13 +2,11 @@ package adapter import ( "context" - "net" "net/netip" "github.com/sagernet/sing-box/common/process" "github.com/sagernet/sing-box/option" M "github.com/sagernet/sing/common/metadata" - N "github.com/sagernet/sing/common/network" ) type Inbound interface { @@ -17,11 +15,14 @@ type Inbound interface { Tag() string } -type InjectableInbound interface { +type TCPInjectableInbound interface { Inbound - Network() []string - NewConnection(ctx context.Context, conn net.Conn, metadata InboundContext) error - NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext) error + ConnectionHandlerEx +} + +type UDPInjectableInbound interface { + Inbound + PacketConnectionHandlerEx } type InboundContext struct { @@ -43,16 +44,18 @@ type InboundContext struct { // cache - InboundDetour string - LastInbound string - OriginDestination M.Socksaddr - InboundOptions option.InboundOptions - DestinationAddresses []netip.Addr - SourceGeoIPCode string - GeoIPCode string - ProcessInfo *process.Info - QueryType uint16 - FakeIP bool + InboundDetour string + LastInbound string + OriginDestination M.Socksaddr + // Deprecated + InboundOptions option.InboundOptions + UDPDisableDomainUnmapping bool + DestinationAddresses []netip.Addr + SourceGeoIPCode string + GeoIPCode string + ProcessInfo *process.Info + QueryType uint16 + FakeIP bool // rule cache diff --git a/adapter/outbound.go b/adapter/outbound.go index b6980fb9..312cdf3e 100644 --- a/adapter/outbound.go +++ b/adapter/outbound.go @@ -1,9 +1,6 @@ package adapter import ( - "context" - "net" - N "github.com/sagernet/sing/common/network" ) @@ -15,6 +12,4 @@ type Outbound interface { Network() []string Dependencies() []string N.Dialer - NewConnection(ctx context.Context, conn net.Conn, metadata InboundContext) error - NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext) error } diff --git a/adapter/router.go b/adapter/router.go index 5dc4de53..134c9442 100644 --- a/adapter/router.go +++ b/adapter/router.go @@ -34,6 +34,7 @@ type Router interface { FakeIPStore() FakeIPStore ConnectionRouter + ConnectionRouterEx GeoIPReader() *geoip.Reader LoadGeosite(code string) (Rule, error) @@ -70,6 +71,18 @@ type Router interface { ResetNetwork() error } +// Deprecated: Use ConnectionRouterEx instead. +type ConnectionRouter interface { + RouteConnection(ctx context.Context, conn net.Conn, metadata InboundContext) error + RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext) error +} + +type ConnectionRouterEx interface { + ConnectionRouter + RouteConnectionEx(ctx context.Context, conn net.Conn, metadata InboundContext, onClose N.CloseHandlerFunc) + RoutePacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata InboundContext, onClose N.CloseHandlerFunc) +} + func ContextWithRouter(ctx context.Context, router Router) context.Context { return service.ContextWith(ctx, router) } @@ -78,28 +91,6 @@ func RouterFromContext(ctx context.Context) Router { return service.FromContext[Router](ctx) } -type HeadlessRule interface { - Match(metadata *InboundContext) bool - String() string -} - -type Rule interface { - HeadlessRule - Service - Type() string - UpdateGeosite() error - Outbound() string -} - -type DNSRule interface { - Rule - DisableCache() bool - RewriteTTL() *uint32 - ClientSubnet() *netip.Prefix - WithAddressLimit() bool - MatchAddressLimit(metadata *InboundContext) bool -} - type RuleSet interface { Name() string StartContext(ctx context.Context, startContext *HTTPStartContext) error diff --git a/adapter/rule.go b/adapter/rule.go new file mode 100644 index 00000000..f3737a25 --- /dev/null +++ b/adapter/rule.go @@ -0,0 +1,38 @@ +package adapter + +import ( + C "github.com/sagernet/sing-box/constant" +) + +type HeadlessRule interface { + Match(metadata *InboundContext) bool + String() string +} + +type Rule interface { + HeadlessRule + Service + Type() string + UpdateGeosite() error + Action() RuleAction +} + +type DNSRule interface { + Rule + WithAddressLimit() bool + MatchAddressLimit(metadata *InboundContext) bool +} + +type RuleAction interface { + Type() string + String() string +} + +func IsFinalAction(action RuleAction) bool { + switch action.Type() { + case C.RuleActionTypeSniff, C.RuleActionTypeResolve: + return false + default: + return true + } +} diff --git a/adapter/upstream.go b/adapter/upstream.go index 95dd98ea..8b0d7c03 100644 --- a/adapter/upstream.go +++ b/adapter/upstream.go @@ -4,112 +4,165 @@ import ( "context" "net" - E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" ) type ( - ConnectionHandlerFunc = func(ctx context.Context, conn net.Conn, metadata InboundContext) error - PacketConnectionHandlerFunc = func(ctx context.Context, conn N.PacketConn, metadata InboundContext) error + ConnectionHandlerFuncEx = func(ctx context.Context, conn net.Conn, metadata InboundContext, onClose N.CloseHandlerFunc) + PacketConnectionHandlerFuncEx = func(ctx context.Context, conn N.PacketConn, metadata InboundContext, onClose N.CloseHandlerFunc) ) -func NewUpstreamHandler( +func NewUpstreamHandlerEx( metadata InboundContext, - connectionHandler ConnectionHandlerFunc, - packetHandler PacketConnectionHandlerFunc, - errorHandler E.Handler, -) UpstreamHandlerAdapter { - return &myUpstreamHandlerWrapper{ + connectionHandler ConnectionHandlerFuncEx, + packetHandler PacketConnectionHandlerFuncEx, +) UpstreamHandlerAdapterEx { + return &myUpstreamHandlerWrapperEx{ metadata: metadata, connectionHandler: connectionHandler, packetHandler: packetHandler, - errorHandler: errorHandler, } } -var _ UpstreamHandlerAdapter = (*myUpstreamHandlerWrapper)(nil) +var _ UpstreamHandlerAdapterEx = (*myUpstreamHandlerWrapperEx)(nil) -type myUpstreamHandlerWrapper struct { +type myUpstreamHandlerWrapperEx struct { metadata InboundContext - connectionHandler ConnectionHandlerFunc - packetHandler PacketConnectionHandlerFunc - errorHandler E.Handler + connectionHandler ConnectionHandlerFuncEx + packetHandler PacketConnectionHandlerFuncEx } -func (w *myUpstreamHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error { +func (w *myUpstreamHandlerWrapperEx) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) { myMetadata := w.metadata - if metadata.Source.IsValid() { - myMetadata.Source = metadata.Source + if source.IsValid() { + myMetadata.Source = source } - if metadata.Destination.IsValid() { - myMetadata.Destination = metadata.Destination + if destination.IsValid() { + myMetadata.Destination = destination } - return w.connectionHandler(ctx, conn, myMetadata) + w.connectionHandler(ctx, conn, myMetadata, onClose) } -func (w *myUpstreamHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error { +func (w *myUpstreamHandlerWrapperEx) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) { myMetadata := w.metadata - if metadata.Source.IsValid() { - myMetadata.Source = metadata.Source + if source.IsValid() { + myMetadata.Source = source } - if metadata.Destination.IsValid() { - myMetadata.Destination = metadata.Destination + if destination.IsValid() { + myMetadata.Destination = destination } - return w.packetHandler(ctx, conn, myMetadata) + w.packetHandler(ctx, conn, myMetadata, onClose) } -func (w *myUpstreamHandlerWrapper) NewError(ctx context.Context, err error) { - w.errorHandler.NewError(ctx, err) +var _ UpstreamHandlerAdapterEx = (*myUpstreamContextHandlerWrapperEx)(nil) + +type myUpstreamContextHandlerWrapperEx struct { + connectionHandler ConnectionHandlerFuncEx + packetHandler PacketConnectionHandlerFuncEx } -func UpstreamMetadata(metadata InboundContext) M.Metadata { - return M.Metadata{ - Source: metadata.Source, - Destination: metadata.Destination, - } -} - -type myUpstreamContextHandlerWrapper struct { - connectionHandler ConnectionHandlerFunc - packetHandler PacketConnectionHandlerFunc - errorHandler E.Handler -} - -func NewUpstreamContextHandler( - connectionHandler ConnectionHandlerFunc, - packetHandler PacketConnectionHandlerFunc, - errorHandler E.Handler, -) UpstreamHandlerAdapter { - return &myUpstreamContextHandlerWrapper{ +func NewUpstreamContextHandlerEx( + connectionHandler ConnectionHandlerFuncEx, + packetHandler PacketConnectionHandlerFuncEx, +) UpstreamHandlerAdapterEx { + return &myUpstreamContextHandlerWrapperEx{ connectionHandler: connectionHandler, packetHandler: packetHandler, - errorHandler: errorHandler, } } -func (w *myUpstreamContextHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error { +func (w *myUpstreamContextHandlerWrapperEx) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) { myMetadata := ContextFrom(ctx) - if metadata.Source.IsValid() { - myMetadata.Source = metadata.Source + if source.IsValid() { + myMetadata.Source = source } - if metadata.Destination.IsValid() { - myMetadata.Destination = metadata.Destination + if destination.IsValid() { + myMetadata.Destination = destination } - return w.connectionHandler(ctx, conn, *myMetadata) + w.connectionHandler(ctx, conn, *myMetadata, onClose) } -func (w *myUpstreamContextHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error { +func (w *myUpstreamContextHandlerWrapperEx) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) { myMetadata := ContextFrom(ctx) - if metadata.Source.IsValid() { - myMetadata.Source = metadata.Source + if source.IsValid() { + myMetadata.Source = source } - if metadata.Destination.IsValid() { - myMetadata.Destination = metadata.Destination + if destination.IsValid() { + myMetadata.Destination = destination } - return w.packetHandler(ctx, conn, *myMetadata) + w.packetHandler(ctx, conn, *myMetadata, onClose) } -func (w *myUpstreamContextHandlerWrapper) NewError(ctx context.Context, err error) { - w.errorHandler.NewError(ctx, err) +func NewRouteHandlerEx( + metadata InboundContext, + router ConnectionRouterEx, +) UpstreamHandlerAdapterEx { + return &routeHandlerWrapperEx{ + metadata: metadata, + router: router, + } +} + +var _ UpstreamHandlerAdapterEx = (*routeHandlerWrapperEx)(nil) + +type routeHandlerWrapperEx struct { + metadata InboundContext + router ConnectionRouterEx +} + +func (r *routeHandlerWrapperEx) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) { + if source.IsValid() { + r.metadata.Source = source + } + if destination.IsValid() { + r.metadata.Destination = destination + } + r.router.RouteConnectionEx(ctx, conn, r.metadata, onClose) +} + +func (r *routeHandlerWrapperEx) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) { + if source.IsValid() { + r.metadata.Source = source + } + if destination.IsValid() { + r.metadata.Destination = destination + } + r.router.RoutePacketConnectionEx(ctx, conn, r.metadata, onClose) +} + +func NewRouteContextHandlerEx( + router ConnectionRouterEx, +) UpstreamHandlerAdapterEx { + return &routeContextHandlerWrapperEx{ + router: router, + } +} + +var _ UpstreamHandlerAdapterEx = (*routeContextHandlerWrapperEx)(nil) + +type routeContextHandlerWrapperEx struct { + router ConnectionRouterEx +} + +func (r *routeContextHandlerWrapperEx) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) { + metadata := ContextFrom(ctx) + if source.IsValid() { + metadata.Source = source + } + if destination.IsValid() { + metadata.Destination = destination + } + r.router.RouteConnectionEx(ctx, conn, *metadata, onClose) +} + +func (r *routeContextHandlerWrapperEx) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) { + metadata := ContextFrom(ctx) + if source.IsValid() { + metadata.Source = source + } + if destination.IsValid() { + metadata.Destination = destination + } + r.router.RoutePacketConnectionEx(ctx, conn, *metadata, onClose) } diff --git a/adapter/upstream_legacy.go b/adapter/upstream_legacy.go new file mode 100644 index 00000000..1c6c15c0 --- /dev/null +++ b/adapter/upstream_legacy.go @@ -0,0 +1,216 @@ +package adapter + +import ( + "context" + "net" + + 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" +) + +type ( + // Deprecated + ConnectionHandlerFunc = func(ctx context.Context, conn net.Conn, metadata InboundContext) error + // Deprecated + PacketConnectionHandlerFunc = func(ctx context.Context, conn N.PacketConn, metadata InboundContext) error +) + +// Deprecated +func NewUpstreamHandler( + metadata InboundContext, + connectionHandler ConnectionHandlerFunc, + packetHandler PacketConnectionHandlerFunc, + errorHandler E.Handler, +) UpstreamHandlerAdapter { + return &myUpstreamHandlerWrapper{ + metadata: metadata, + connectionHandler: connectionHandler, + packetHandler: packetHandler, + errorHandler: errorHandler, + } +} + +var _ UpstreamHandlerAdapter = (*myUpstreamHandlerWrapper)(nil) + +// Deprecated +type myUpstreamHandlerWrapper struct { + metadata InboundContext + connectionHandler ConnectionHandlerFunc + packetHandler PacketConnectionHandlerFunc + errorHandler E.Handler +} + +func (w *myUpstreamHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error { + myMetadata := w.metadata + if metadata.Source.IsValid() { + myMetadata.Source = metadata.Source + } + if metadata.Destination.IsValid() { + myMetadata.Destination = metadata.Destination + } + return w.connectionHandler(ctx, conn, myMetadata) +} + +func (w *myUpstreamHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error { + myMetadata := w.metadata + if metadata.Source.IsValid() { + myMetadata.Source = metadata.Source + } + if metadata.Destination.IsValid() { + myMetadata.Destination = metadata.Destination + } + return w.packetHandler(ctx, conn, myMetadata) +} + +func (w *myUpstreamHandlerWrapper) NewError(ctx context.Context, err error) { + w.errorHandler.NewError(ctx, err) +} + +// Deprecated +func UpstreamMetadata(metadata InboundContext) M.Metadata { + return M.Metadata{ + Source: metadata.Source, + Destination: metadata.Destination, + } +} + +// Deprecated +type myUpstreamContextHandlerWrapper struct { + connectionHandler ConnectionHandlerFunc + packetHandler PacketConnectionHandlerFunc + errorHandler E.Handler +} + +// Deprecated +func NewUpstreamContextHandler( + connectionHandler ConnectionHandlerFunc, + packetHandler PacketConnectionHandlerFunc, + errorHandler E.Handler, +) UpstreamHandlerAdapter { + return &myUpstreamContextHandlerWrapper{ + connectionHandler: connectionHandler, + packetHandler: packetHandler, + errorHandler: errorHandler, + } +} + +func (w *myUpstreamContextHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error { + myMetadata := ContextFrom(ctx) + if metadata.Source.IsValid() { + myMetadata.Source = metadata.Source + } + if metadata.Destination.IsValid() { + myMetadata.Destination = metadata.Destination + } + return w.connectionHandler(ctx, conn, *myMetadata) +} + +func (w *myUpstreamContextHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error { + myMetadata := ContextFrom(ctx) + if metadata.Source.IsValid() { + myMetadata.Source = metadata.Source + } + if metadata.Destination.IsValid() { + myMetadata.Destination = metadata.Destination + } + return w.packetHandler(ctx, conn, *myMetadata) +} + +func (w *myUpstreamContextHandlerWrapper) NewError(ctx context.Context, err error) { + w.errorHandler.NewError(ctx, err) +} + +// Deprecated: Use ConnectionRouterEx instead. +func NewRouteHandler( + metadata InboundContext, + router ConnectionRouter, + logger logger.ContextLogger, +) UpstreamHandlerAdapter { + return &routeHandlerWrapper{ + metadata: metadata, + router: router, + logger: logger, + } +} + +// Deprecated: Use ConnectionRouterEx instead. +func NewRouteContextHandler( + router ConnectionRouter, + logger logger.ContextLogger, +) UpstreamHandlerAdapter { + return &routeContextHandlerWrapper{ + router: router, + logger: logger, + } +} + +var _ UpstreamHandlerAdapter = (*routeHandlerWrapper)(nil) + +// Deprecated: Use ConnectionRouterEx instead. +type routeHandlerWrapper struct { + metadata InboundContext + router ConnectionRouter + logger logger.ContextLogger +} + +func (w *routeHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error { + myMetadata := w.metadata + if metadata.Source.IsValid() { + myMetadata.Source = metadata.Source + } + if metadata.Destination.IsValid() { + myMetadata.Destination = metadata.Destination + } + return w.router.RouteConnection(ctx, conn, myMetadata) +} + +func (w *routeHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error { + myMetadata := w.metadata + if metadata.Source.IsValid() { + myMetadata.Source = metadata.Source + } + if metadata.Destination.IsValid() { + myMetadata.Destination = metadata.Destination + } + return w.router.RoutePacketConnection(ctx, conn, myMetadata) +} + +func (w *routeHandlerWrapper) NewError(ctx context.Context, err error) { + w.logger.ErrorContext(ctx, err) +} + +var _ UpstreamHandlerAdapter = (*routeContextHandlerWrapper)(nil) + +// Deprecated: Use ConnectionRouterEx instead. +type routeContextHandlerWrapper struct { + router ConnectionRouter + logger logger.ContextLogger +} + +func (w *routeContextHandlerWrapper) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error { + myMetadata := ContextFrom(ctx) + if metadata.Source.IsValid() { + myMetadata.Source = metadata.Source + } + if metadata.Destination.IsValid() { + myMetadata.Destination = metadata.Destination + } + return w.router.RouteConnection(ctx, conn, *myMetadata) +} + +func (w *routeContextHandlerWrapper) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error { + myMetadata := ContextFrom(ctx) + if metadata.Source.IsValid() { + myMetadata.Source = metadata.Source + } + if metadata.Destination.IsValid() { + myMetadata.Destination = metadata.Destination + } + return w.router.RoutePacketConnection(ctx, conn, *myMetadata) +} + +func (w *routeContextHandlerWrapper) NewError(ctx context.Context, err error) { + w.logger.ErrorContext(ctx, err) +} diff --git a/adapter/v2ray.go b/adapter/v2ray.go index 5a98d2e5..d9370807 100644 --- a/adapter/v2ray.go +++ b/adapter/v2ray.go @@ -4,7 +4,6 @@ import ( "context" "net" - E "github.com/sagernet/sing/common/exceptions" N "github.com/sagernet/sing/common/network" ) @@ -16,8 +15,7 @@ type V2RayServerTransport interface { } type V2RayServerTransportHandler interface { - N.TCPConnectionHandler - E.Handler + N.TCPConnectionHandlerEx } type V2RayClientTransport interface { diff --git a/cmd/sing-box/cmd_rule_set_match.go b/cmd/sing-box/cmd_rule_set_match.go index 7cded86e..b96ff22c 100644 --- a/cmd/sing-box/cmd_rule_set_match.go +++ b/cmd/sing-box/cmd_rule_set_match.go @@ -10,7 +10,7 @@ import ( C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" - "github.com/sagernet/sing-box/route" + "github.com/sagernet/sing-box/route/rule" E "github.com/sagernet/sing/common/exceptions" F "github.com/sagernet/sing/common/format" "github.com/sagernet/sing/common/json" @@ -83,7 +83,7 @@ func ruleSetMatch(sourcePath string, domain string) error { } for i, ruleOptions := range plainRuleSet.Rules { var currentRule adapter.HeadlessRule - currentRule, err = route.NewHeadlessRule(nil, ruleOptions) + currentRule, err = rule.NewHeadlessRule(nil, ruleOptions) if err != nil { return E.Cause(err, "parse rule_set.rules.[", i, "]") } diff --git a/cmd/sing-box/internal/convertor/adguard/convertor_test.go b/cmd/sing-box/internal/convertor/adguard/convertor_test.go index 7da8a228..6098485c 100644 --- a/cmd/sing-box/internal/convertor/adguard/convertor_test.go +++ b/cmd/sing-box/internal/convertor/adguard/convertor_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/sagernet/sing-box/adapter" - "github.com/sagernet/sing-box/route" + "github.com/sagernet/sing-box/route/rule" "github.com/stretchr/testify/require" ) @@ -26,7 +26,7 @@ example.arpa `)) require.NoError(t, err) require.Len(t, rules, 1) - rule, err := route.NewHeadlessRule(nil, rules[0]) + rule, err := rule.NewHeadlessRule(nil, rules[0]) require.NoError(t, err) matchDomain := []string{ "example.org", @@ -85,7 +85,7 @@ func TestHosts(t *testing.T) { `)) require.NoError(t, err) require.Len(t, rules, 1) - rule, err := route.NewHeadlessRule(nil, rules[0]) + rule, err := rule.NewHeadlessRule(nil, rules[0]) require.NoError(t, err) matchDomain := []string{ "google.com", @@ -115,7 +115,7 @@ www.example.org `)) require.NoError(t, err) require.Len(t, rules, 1) - rule, err := route.NewHeadlessRule(nil, rules[0]) + rule, err := rule.NewHeadlessRule(nil, rules[0]) require.NoError(t, err) matchDomain := []string{ "example.com", diff --git a/common/mux/router.go b/common/mux/router.go index 8a229685..2d4e7705 100644 --- a/common/mux/router.go +++ b/common/mux/router.go @@ -15,11 +15,11 @@ import ( ) type Router struct { - router adapter.ConnectionRouter + router adapter.ConnectionRouterEx service *mux.Service } -func NewRouterWithOptions(router adapter.ConnectionRouter, logger logger.ContextLogger, options option.InboundMultiplexOptions) (adapter.ConnectionRouter, error) { +func NewRouterWithOptions(router adapter.ConnectionRouterEx, logger logger.ContextLogger, options option.InboundMultiplexOptions) (adapter.ConnectionRouterEx, error) { if !options.Enabled { return router, nil } @@ -54,6 +54,7 @@ func NewRouterWithOptions(router adapter.ConnectionRouter, logger logger.Context 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 return r.service.NewConnection(adapter.WithContext(ctx, &metadata), conn, adapter.UpstreamMetadata(metadata)) } else { return r.router.RouteConnection(ctx, conn, metadata) @@ -63,3 +64,15 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { return r.router.RoutePacketConnection(ctx, conn, metadata) } + +func (r *Router) RouteConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) { + if metadata.Destination == mux.Destination { + r.service.NewConnectionEx(adapter.WithContext(ctx, &metadata), conn, metadata.Source, metadata.Destination, onClose) + return + } + r.router.RouteConnectionEx(ctx, conn, metadata, onClose) +} + +func (r *Router) RoutePacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) { + r.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose) +} diff --git a/common/mux/v2ray_legacy.go b/common/mux/v2ray_legacy.go deleted file mode 100644 index f53aff2d..00000000 --- a/common/mux/v2ray_legacy.go +++ /dev/null @@ -1,32 +0,0 @@ -package mux - -import ( - "context" - "net" - - "github.com/sagernet/sing-box/adapter" - vmess "github.com/sagernet/sing-vmess" - "github.com/sagernet/sing/common/logger" - N "github.com/sagernet/sing/common/network" -) - -type V2RayLegacyRouter struct { - router adapter.ConnectionRouter - logger logger.ContextLogger -} - -func NewV2RayLegacyRouter(router adapter.ConnectionRouter, logger logger.ContextLogger) adapter.ConnectionRouter { - return &V2RayLegacyRouter{router, logger} -} - -func (r *V2RayLegacyRouter) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { - if metadata.Destination.Fqdn == vmess.MuxDestination.Fqdn { - r.logger.InfoContext(ctx, "inbound legacy multiplex connection") - return vmess.HandleMuxConnection(ctx, conn, adapter.NewRouteHandler(metadata, r.router, r.logger)) - } - return r.router.RouteConnection(ctx, conn, metadata) -} - -func (r *V2RayLegacyRouter) RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { - return r.router.RoutePacketConnection(ctx, conn, metadata) -} diff --git a/common/sniff/sniff.go b/common/sniff/sniff.go index 80bb2984..81fc0a27 100644 --- a/common/sniff/sniff.go +++ b/common/sniff/sniff.go @@ -18,7 +18,7 @@ type ( PacketSniffer = func(ctx context.Context, metadata *adapter.InboundContext, packet []byte) error ) -func Skip(metadata adapter.InboundContext) bool { +func Skip(metadata *adapter.InboundContext) bool { // skip server first protocols switch metadata.Destination.Port { case 25, 465, 587: diff --git a/common/uot/router.go b/common/uot/router.go index fb2d23d5..98c6d608 100644 --- a/common/uot/router.go +++ b/common/uot/router.go @@ -13,14 +13,14 @@ import ( "github.com/sagernet/sing/common/uot" ) -var _ adapter.ConnectionRouter = (*Router)(nil) +var _ adapter.ConnectionRouterEx = (*Router)(nil) type Router struct { - router adapter.ConnectionRouter + router adapter.ConnectionRouterEx logger logger.ContextLogger } -func NewRouter(router adapter.ConnectionRouter, logger logger.ContextLogger) *Router { +func NewRouter(router adapter.ConnectionRouterEx, logger logger.ContextLogger) *Router { return &Router{router, logger} } @@ -51,3 +51,36 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { return r.router.RoutePacketConnection(ctx, conn, metadata) } + +func (r *Router) RouteConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) { + switch metadata.Destination.Fqdn { + case uot.MagicAddress: + request, err := uot.ReadRequest(conn) + if err != nil { + err = E.Cause(err, "UoT read request") + r.logger.ErrorContext(ctx, "process connection from ", metadata.Source, ": ", err) + N.CloseOnHandshakeFailure(conn, onClose, err) + return + } + if request.IsConnect { + r.logger.InfoContext(ctx, "inbound UoT connect connection to ", request.Destination) + } else { + r.logger.InfoContext(ctx, "inbound UoT connection to ", request.Destination) + } + metadata.Domain = metadata.Destination.Fqdn + metadata.Destination = request.Destination + r.router.RoutePacketConnectionEx(ctx, uot.NewConn(conn, *request), metadata, onClose) + return + case uot.LegacyMagicAddress: + r.logger.InfoContext(ctx, "inbound legacy UoT connection") + metadata.Domain = metadata.Destination.Fqdn + metadata.Destination = M.Socksaddr{Addr: netip.IPv4Unspecified()} + r.RoutePacketConnectionEx(ctx, uot.NewConn(conn, uot.Request{}), metadata, onClose) + return + } + r.router.RouteConnectionEx(ctx, conn, metadata, onClose) +} + +func (r *Router) RoutePacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) { + r.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose) +} diff --git a/constant/rule.go b/constant/rule.go index 086b95a0..ba74ec63 100644 --- a/constant/rule.go +++ b/constant/rule.go @@ -23,3 +23,18 @@ const ( RuleSetVersion2 RuleSetVersionCurrent = RuleSetVersion2 ) + +const ( + RuleActionTypeRoute = "route" + RuleActionTypeReturn = "return" + RuleActionTypeReject = "reject" + RuleActionTypeHijackDNS = "hijack-dns" + RuleActionTypeSniff = "sniff" + RuleActionTypeResolve = "resolve" +) + +const ( + RuleActionRejectMethodDefault = "default" + RuleActionRejectMethodPortUnreachable = "port-unreachable" + RuleActionRejectMethodDrop = "drop" +) diff --git a/experimental/clashapi/rules.go b/experimental/clashapi/rules.go index 6ab5dda1..bc8fbb2b 100644 --- a/experimental/clashapi/rules.go +++ b/experimental/clashapi/rules.go @@ -30,10 +30,9 @@ func getRules(router adapter.Router) func(w http.ResponseWriter, r *http.Request rules = append(rules, Rule{ Type: rule.Type(), Payload: rule.String(), - Proxy: rule.Outbound(), + Proxy: rule.Action().String(), }) } - render.JSON(w, r, render.M{ "rules": rules, }) diff --git a/experimental/clashapi/trafficontrol/tracker.go b/experimental/clashapi/trafficontrol/tracker.go index 73c28e69..9c18abeb 100644 --- a/experimental/clashapi/trafficontrol/tracker.go +++ b/experimental/clashapi/trafficontrol/tracker.go @@ -5,6 +5,7 @@ import ( "time" "github.com/sagernet/sing-box/adapter" + R "github.com/sagernet/sing-box/route/rule" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/atomic" "github.com/sagernet/sing/common/bufio" @@ -60,7 +61,7 @@ func (t TrackerMetadata) MarshalJSON() ([]byte, error) { } var rule string if t.Rule != nil { - rule = F.ToString(t.Rule, " => ", t.Rule.Outbound()) + rule = F.ToString(t.Rule, " => ", t.Rule.Action()) } else { rule = "final" } @@ -131,19 +132,21 @@ func NewTCPTracker(conn net.Conn, manager *Manager, metadata adapter.InboundCont outbound string outboundType string ) - if rule == nil { - if defaultOutbound, err := router.DefaultOutbound(N.NetworkTCP); err == nil { - next = defaultOutbound.Tag() - } - } else { - next = rule.Outbound() + var action adapter.RuleAction + if rule != nil { + action = rule.Action() + } + if routeAction, isRouteAction := action.(*R.RuleActionRoute); isRouteAction { + next = routeAction.Outbound + } else if defaultOutbound, err := router.DefaultOutbound(N.NetworkTCP); err == nil { + next = defaultOutbound.Tag() } for { - chain = append(chain, next) detour, loaded := router.Outbound(next) if !loaded { break } + chain = append(chain, next) outbound = detour.Tag() outboundType = detour.Type() group, isGroup := detour.(adapter.OutboundGroup) @@ -218,19 +221,21 @@ func NewUDPTracker(conn N.PacketConn, manager *Manager, metadata adapter.Inbound outbound string outboundType string ) - if rule == nil { - if defaultOutbound, err := router.DefaultOutbound(N.NetworkUDP); err == nil { - next = defaultOutbound.Tag() - } - } else { - next = rule.Outbound() + var action adapter.RuleAction + if rule != nil { + action = rule.Action() + } + if routeAction, isRouteAction := action.(*R.RuleActionRoute); isRouteAction { + next = routeAction.Outbound + } else if defaultOutbound, err := router.DefaultOutbound(N.NetworkUDP); err == nil { + next = defaultOutbound.Tag() } for { - chain = append(chain, next) detour, loaded := router.Outbound(next) if !loaded { break } + chain = append(chain, next) outbound = detour.Tag() outboundType = detour.Type() group, isGroup := detour.(adapter.OutboundGroup) diff --git a/go.mod b/go.mod index 29d27331..7d4d8637 100644 --- a/go.mod +++ b/go.mod @@ -27,14 +27,14 @@ require ( github.com/sagernet/gvisor v0.0.0-20241021032506-a4324256e4a3 github.com/sagernet/quic-go v0.48.1-beta.1 github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 - github.com/sagernet/sing v0.5.1 - github.com/sagernet/sing-dns v0.3.0 - github.com/sagernet/sing-mux v0.2.1 - github.com/sagernet/sing-quic v0.3.1 + github.com/sagernet/sing v0.6.0-alpha.18 + github.com/sagernet/sing-dns v0.4.0-alpha.3 + github.com/sagernet/sing-mux v0.3.0-alpha.1 + github.com/sagernet/sing-quic v0.4.0-alpha.4 github.com/sagernet/sing-shadowsocks v0.2.7 github.com/sagernet/sing-shadowsocks2 v0.2.0 - github.com/sagernet/sing-shadowtls v0.1.5 - github.com/sagernet/sing-tun v0.4.1 + github.com/sagernet/sing-shadowtls v0.2.0-alpha.2 + github.com/sagernet/sing-tun v0.6.0-alpha.9 github.com/sagernet/sing-vmess v0.1.12 github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 github.com/sagernet/utls v1.6.7 diff --git a/go.sum b/go.sum index b2d1b8c9..b52cd461 100644 --- a/go.sum +++ b/go.sum @@ -115,22 +115,22 @@ github.com/sagernet/quic-go v0.48.1-beta.1/go.mod h1:1WgdDIVD1Gybp40JTWketeSfKA/ 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.5.1 h1:mhL/MZVq0TjuvHcpYcFtmSD1BFOxZ/+8ofbNZcg1k1Y= -github.com/sagernet/sing v0.5.1/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= -github.com/sagernet/sing-dns v0.3.0 h1:uHCIlbCwBxALJwXcEK1d75d7t3vzCSVEQsPfZR1cxQE= -github.com/sagernet/sing-dns v0.3.0/go.mod h1:TqLIelI+FAbVEdiTRolhGLOwvhVjY7oT+wezlOJUQ7M= -github.com/sagernet/sing-mux v0.2.1 h1:N/3MHymfnFZRd29tE3TaXwPUVVgKvxhtOkiCMLp9HVo= -github.com/sagernet/sing-mux v0.2.1/go.mod h1:dm3BWL6NvES9pbib7llpylrq7Gq+LjlzG+0RacdxcyE= -github.com/sagernet/sing-quic v0.3.1 h1:kLg2n4JPnuzUPg7myJGbfGVJGeXiccXfV+PhXIlkSEc= -github.com/sagernet/sing-quic v0.3.1/go.mod h1:g8b5Fj88KRM0H9lpKAxJj0EpkL/Yk06qXJAG7FuZd2I= +github.com/sagernet/sing v0.6.0-alpha.18 h1:ih4CurU8KvbhfagYjSqVrE2LR0oBSXSZTNH2sAGPGiM= +github.com/sagernet/sing v0.6.0-alpha.18/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= +github.com/sagernet/sing-dns v0.4.0-alpha.3 h1:TcAQdz68Gs28VD9o9zDIW7IS8A9LZDruTPI9g9JbGHA= +github.com/sagernet/sing-dns v0.4.0-alpha.3/go.mod h1:9LHcYKg2bGQpbtXrfNbopz8ok/zBK9ljiI2kmFG9JKg= +github.com/sagernet/sing-mux v0.3.0-alpha.1 h1:IgNX5bJBpL41gGbp05pdDOvh/b5eUQ6cv9240+Ngipg= +github.com/sagernet/sing-mux v0.3.0-alpha.1/go.mod h1:FTcImmdfW38Lz7b+HQ+mxxOth1lz4ao8uEnz+MwIJQE= +github.com/sagernet/sing-quic v0.4.0-alpha.4 h1:P9xAx3nIfcqb9M8jfgs0uLm+VxCcaY++FCqaBfHY3dQ= +github.com/sagernet/sing-quic v0.4.0-alpha.4/go.mod h1:h5RkKTmUhudJKzK7c87FPXD5w1bJjVyxMN9+opZcctA= github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8= github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE= github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg= github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ= -github.com/sagernet/sing-shadowtls v0.1.5 h1:uXxmq/HXh8DIiBGLzpMjCbWnzIAFs+lIxiTOjdgG5qo= -github.com/sagernet/sing-shadowtls v0.1.5/go.mod h1:tvrDPTGLrSM46Wnf7mSr+L8NHvgvF8M4YnJF790rZX4= -github.com/sagernet/sing-tun v0.4.1 h1:VKjKX93fUlEbYiabX3OhnqEylYzrcYcQthnaO0u2cI0= -github.com/sagernet/sing-tun v0.4.1/go.mod h1:hHNVxjL7X0vNjkfN0GTkMk2CrGdgk0zZMEinkmz8XeM= +github.com/sagernet/sing-shadowtls v0.2.0-alpha.2 h1:RPrpgAdkP5td0vLfS5ldvYosFjSsZtRPxiyLV6jyKg0= +github.com/sagernet/sing-shadowtls v0.2.0-alpha.2/go.mod h1:0j5XlzKxaWRIEjc1uiSKmVoWb0k+L9QgZVb876+thZA= +github.com/sagernet/sing-tun v0.6.0-alpha.9 h1:Qf667035KnlydZ+ftj3U4HH+oddi3RdyKzBiCcnSgaI= +github.com/sagernet/sing-tun v0.6.0-alpha.9/go.mod h1:TgvxE2YD7O9c/unHju0nWAGBGsVppWIuju13vlmdllM= github.com/sagernet/sing-vmess v0.1.12 h1:2gFD8JJb+eTFMoa8FIVMnknEi+vCSfaiTXTfEYAYAPg= github.com/sagernet/sing-vmess v0.1.12/go.mod h1:luTSsfyBGAc9VhtCqwjR+dt1QgqBhuYBCONB/POhF8I= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ= diff --git a/inbound/default.go b/inbound/default.go index 44c580de..880dd26f 100644 --- a/inbound/default.go +++ b/inbound/default.go @@ -22,13 +22,13 @@ type myInboundAdapter struct { protocol string network []string ctx context.Context - router adapter.ConnectionRouter + router adapter.ConnectionRouterEx logger log.ContextLogger tag string listenOptions option.ListenOptions - connHandler adapter.ConnectionHandler - packetHandler adapter.PacketHandler - oobPacketHandler adapter.OOBPacketHandler + connHandler adapter.ConnectionHandlerEx + packetHandler adapter.PacketHandlerEx + oobPacketHandler adapter.OOBPacketHandlerEx packetUpstream any // http mixed @@ -55,10 +55,6 @@ func (a *myInboundAdapter) Tag() string { return a.tag } -func (a *myInboundAdapter) Network() []string { - return a.network -} - func (a *myInboundAdapter) Start() error { var err error if common.Contains(a.network, N.NetworkTCP) { @@ -150,6 +146,31 @@ func (a *myInboundAdapter) newPacketConnection(ctx context.Context, conn N.Packe return a.router.RoutePacketConnection(ctx, conn, metadata) } +func (a *myInboundAdapter) upstreamHandlerEx(metadata adapter.InboundContext) adapter.UpstreamHandlerAdapterEx { + return adapter.NewUpstreamHandlerEx(metadata, a.newConnectionEx, a.streamPacketConnectionEx) +} + +func (a *myInboundAdapter) upstreamContextHandlerEx() adapter.UpstreamHandlerAdapterEx { + return adapter.NewUpstreamContextHandlerEx(a.newConnectionEx, a.newPacketConnectionEx) +} + +func (a *myInboundAdapter) newConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) { + a.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination) + a.router.RouteConnectionEx(ctx, conn, metadata, onClose) +} + +func (a *myInboundAdapter) newPacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) { + ctx = log.ContextWithNewID(ctx) + a.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source) + a.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination) + a.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose) +} + +func (a *myInboundAdapter) streamPacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) { + a.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination) + a.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose) +} + func (a *myInboundAdapter) createMetadata(conn net.Conn, metadata adapter.InboundContext) adapter.InboundContext { metadata.Inbound = a.tag metadata.InboundType = a.protocol @@ -167,25 +188,17 @@ func (a *myInboundAdapter) createMetadata(conn net.Conn, metadata adapter.Inboun return metadata } -func (a *myInboundAdapter) createPacketMetadata(conn N.PacketConn, metadata adapter.InboundContext) adapter.InboundContext { - metadata.Inbound = a.tag - metadata.InboundType = a.protocol - metadata.InboundDetour = a.listenOptions.Detour - metadata.InboundOptions = a.listenOptions.InboundOptions - if !metadata.Destination.IsValid() { - metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap() - } - return metadata -} - +// Deprecated: don't use func (a *myInboundAdapter) newError(err error) { a.logger.Error(err) } +// Deprecated: don't use func (a *myInboundAdapter) NewError(ctx context.Context, err error) { NewError(a.logger, ctx, err) } +// Deprecated: don't use func NewError(logger log.ContextLogger, ctx context.Context, err error) { common.Close(err) if E.IsClosedOrCanceled(err) { diff --git a/inbound/default_tcp.go b/inbound/default_tcp.go index d680c695..d38f96fe 100644 --- a/inbound/default_tcp.go +++ b/inbound/default_tcp.go @@ -71,18 +71,14 @@ func (a *myInboundAdapter) injectTCP(conn net.Conn, metadata adapter.InboundCont ctx := log.ContextWithNewID(a.ctx) metadata = a.createMetadata(conn, metadata) a.logger.InfoContext(ctx, "inbound connection from ", metadata.Source) - hErr := a.connHandler.NewConnection(ctx, conn, metadata) - if hErr != nil { - conn.Close() - a.NewError(ctx, E.Cause(hErr, "process connection from ", metadata.Source)) - } + a.connHandler.NewConnectionEx(ctx, conn, metadata, nil) } -func (a *myInboundAdapter) routeTCP(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) { +func (a *myInboundAdapter) routeTCP(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) { + metadata := a.createMetadata(conn, adapter.InboundContext{ + Source: source, + Destination: destination, + }) a.logger.InfoContext(ctx, "inbound connection from ", metadata.Source) - hErr := a.newConnection(ctx, conn, metadata) - if hErr != nil { - conn.Close() - a.NewError(ctx, E.Cause(hErr, "process connection from ", metadata.Source)) - } + a.connHandler.NewConnectionEx(ctx, conn, metadata, onClose) } diff --git a/inbound/default_udp.go b/inbound/default_udp.go index 8e39471a..6bcde79d 100644 --- a/inbound/default_udp.go +++ b/inbound/default_udp.go @@ -42,7 +42,6 @@ func (a *myInboundAdapter) loopUDPIn() { defer buffer.Release() buffer.IncRef() defer buffer.DecRef() - packetService := (*myInboundPacketAdapter)(a) for { buffer.Reset() n, addr, err := a.udpConn.ReadFromUDPAddrPort(buffer.FreeBytes()) @@ -50,16 +49,7 @@ func (a *myInboundAdapter) loopUDPIn() { return } buffer.Truncate(n) - var metadata adapter.InboundContext - metadata.Inbound = a.tag - metadata.InboundType = a.protocol - metadata.InboundOptions = a.listenOptions.InboundOptions - metadata.Source = M.SocksaddrFromNetIP(addr).Unwrap() - metadata.OriginDestination = a.udpAddr - err = a.packetHandler.NewPacket(a.ctx, packetService, buffer, metadata) - if err != nil { - a.newError(E.Cause(err, "process packet from ", metadata.Source)) - } + a.packetHandler.NewPacketEx(buffer, M.SocksaddrFromNetIP(addr).Unwrap()) } } @@ -69,7 +59,6 @@ func (a *myInboundAdapter) loopUDPOOBIn() { defer buffer.Release() buffer.IncRef() defer buffer.DecRef() - packetService := (*myInboundPacketAdapter)(a) oob := make([]byte, 1024) for { buffer.Reset() @@ -78,22 +67,12 @@ func (a *myInboundAdapter) loopUDPOOBIn() { return } buffer.Truncate(n) - var metadata adapter.InboundContext - metadata.Inbound = a.tag - metadata.InboundType = a.protocol - metadata.InboundOptions = a.listenOptions.InboundOptions - metadata.Source = M.SocksaddrFromNetIP(addr).Unwrap() - metadata.OriginDestination = a.udpAddr - err = a.oobPacketHandler.NewPacket(a.ctx, packetService, buffer, oob[:oobN], metadata) - if err != nil { - a.newError(E.Cause(err, "process packet from ", metadata.Source)) - } + a.oobPacketHandler.NewPacketEx(buffer, oob[:oobN], M.SocksaddrFromNetIP(addr).Unwrap()) } } func (a *myInboundAdapter) loopUDPInThreadSafe() { defer close(a.packetOutboundClosed) - packetService := (*myInboundPacketAdapter)(a) for { buffer := buf.NewPacket() n, addr, err := a.udpConn.ReadFromUDPAddrPort(buffer.FreeBytes()) @@ -102,23 +81,12 @@ func (a *myInboundAdapter) loopUDPInThreadSafe() { return } buffer.Truncate(n) - var metadata adapter.InboundContext - metadata.Inbound = a.tag - metadata.InboundType = a.protocol - metadata.InboundOptions = a.listenOptions.InboundOptions - metadata.Source = M.SocksaddrFromNetIP(addr).Unwrap() - metadata.OriginDestination = a.udpAddr - err = a.packetHandler.NewPacket(a.ctx, packetService, buffer, metadata) - if err != nil { - buffer.Release() - a.newError(E.Cause(err, "process packet from ", metadata.Source)) - } + a.packetHandler.NewPacketEx(buffer, M.SocksaddrFromNetIP(addr).Unwrap()) } } func (a *myInboundAdapter) loopUDPOOBInThreadSafe() { defer close(a.packetOutboundClosed) - packetService := (*myInboundPacketAdapter)(a) oob := make([]byte, 1024) for { buffer := buf.NewPacket() @@ -128,17 +96,7 @@ func (a *myInboundAdapter) loopUDPOOBInThreadSafe() { return } buffer.Truncate(n) - var metadata adapter.InboundContext - metadata.Inbound = a.tag - metadata.InboundType = a.protocol - metadata.InboundOptions = a.listenOptions.InboundOptions - metadata.Source = M.SocksaddrFromNetIP(addr).Unwrap() - metadata.OriginDestination = a.udpAddr - err = a.oobPacketHandler.NewPacket(a.ctx, packetService, buffer, oob[:oobN], metadata) - if err != nil { - buffer.Release() - a.newError(E.Cause(err, "process packet from ", metadata.Source)) - } + a.oobPacketHandler.NewPacketEx(buffer, oob[:oobN], M.SocksaddrFromNetIP(addr).Unwrap()) } } @@ -148,7 +106,7 @@ func (a *myInboundAdapter) loopUDPOut() { case packet := <-a.packetOutbound: err := a.writePacket(packet.buffer, packet.destination) if err != nil && !E.IsClosed(err) { - a.newError(E.New("write back udp: ", err)) + a.logger.Error(E.New("write back udp: ", err)) } continue case <-a.packetOutboundClosed: @@ -164,15 +122,36 @@ func (a *myInboundAdapter) loopUDPOut() { } } +func (a *myInboundAdapter) packetConn() N.PacketConn { + return (*myInboundPacketAdapter)(a) +} + +func (a *myInboundAdapter) createPacketMetadata(conn N.PacketConn, metadata adapter.InboundContext) adapter.InboundContext { + metadata.Inbound = a.tag + metadata.InboundType = a.protocol + metadata.InboundDetour = a.listenOptions.Detour + metadata.InboundOptions = a.listenOptions.InboundOptions + if !metadata.Destination.IsValid() { + metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap() + } + metadata.OriginDestination = a.udpAddr + return metadata +} + +func (a *myInboundAdapter) createPacketMetadataEx(source M.Socksaddr, destination M.Socksaddr) adapter.InboundContext { + var metadata adapter.InboundContext + metadata.Inbound = a.tag + metadata.InboundType = a.protocol + metadata.InboundDetour = a.listenOptions.Detour + metadata.InboundOptions = a.listenOptions.InboundOptions + metadata.Source = source + metadata.Destination = destination + metadata.OriginDestination = a.udpAddr + return metadata +} + func (a *myInboundAdapter) writePacket(buffer *buf.Buffer, destination M.Socksaddr) error { defer buffer.Release() - if destination.IsFqdn() { - udpAddr, err := net.ResolveUDPAddr(N.NetworkUDP, destination.String()) - if err != nil { - return err - } - return common.Error(a.udpConn.WriteTo(buffer.Bytes(), udpAddr)) - } return common.Error(a.udpConn.WriteToUDPAddrPort(buffer.Bytes(), destination.AddrPort())) } diff --git a/inbound/direct.go b/inbound/direct.go index 7079a9f2..b9a99f12 100644 --- a/inbound/direct.go +++ b/inbound/direct.go @@ -3,7 +3,6 @@ package inbound import ( "context" "net" - "net/netip" "time" "github.com/sagernet/sing-box/adapter" @@ -13,14 +12,14 @@ import ( "github.com/sagernet/sing/common/buf" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" - "github.com/sagernet/sing/common/udpnat" + "github.com/sagernet/sing/common/udpnat2" ) var _ adapter.Inbound = (*Direct)(nil) type Direct struct { myInboundAdapter - udpNat *udpnat.Service[netip.AddrPort] + udpNat *udpnat.Service overrideOption int overrideDestination M.Socksaddr } @@ -54,10 +53,9 @@ func NewDirect(ctx context.Context, router adapter.Router, logger log.ContextLog } else { udpTimeout = C.UDPTimeout } - inbound.udpNat = udpnat.New[netip.AddrPort](int64(udpTimeout.Seconds()), adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound)) + inbound.udpNat = udpnat.New(inbound, inbound.preparePacketConnection, udpTimeout, false) inbound.connHandler = inbound inbound.packetHandler = inbound - inbound.packetUpstream = inbound.udpNat return inbound } @@ -76,29 +74,38 @@ func (d *Direct) NewConnection(ctx context.Context, conn net.Conn, metadata adap return d.router.RouteConnection(ctx, conn, metadata) } -func (d *Direct) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata adapter.InboundContext) error { +func (d *Direct) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) { + var destination M.Socksaddr switch d.overrideOption { case 1: - metadata.Destination = d.overrideDestination + destination = d.overrideDestination case 2: - destination := d.overrideDestination - destination.Port = metadata.Destination.Port - metadata.Destination = destination + destination = d.overrideDestination + destination.Port = source.Port case 3: - metadata.Destination.Port = d.overrideDestination.Port + destination = source + destination.Port = d.overrideDestination.Port } - d.udpNat.NewContextPacket(ctx, metadata.Source.AddrPort(), buffer, adapter.UpstreamMetadata(metadata), func(natConn N.PacketConn) (context.Context, N.PacketWriter) { - return adapter.WithContext(log.ContextWithNewID(ctx), &metadata), &udpnat.DirectBackWriter{Source: conn, Nat: natConn} - }) - return nil + d.udpNat.NewPacket([][]byte{buffer.Bytes()}, source, destination, nil) } -func (d *Direct) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { - return d.router.RouteConnection(ctx, conn, metadata) +func (d *Direct) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) { + d.newConnectionEx(ctx, conn, metadata, onClose) } -func (d *Direct) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { - ctx = log.ContextWithNewID(ctx) - d.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source) - return d.router.RoutePacketConnection(ctx, conn, metadata) +func (d *Direct) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) { + d.newPacketConnectionEx(ctx, conn, d.createPacketMetadataEx(source, destination), onClose) +} + +func (d *Direct) preparePacketConnection(source M.Socksaddr, destination M.Socksaddr, userData any) (bool, context.Context, N.PacketWriter, N.CloseHandlerFunc) { + return true, d.ctx, &directPacketWriter{d.packetConn(), source}, nil +} + +type directPacketWriter struct { + writer N.PacketWriter + source M.Socksaddr +} + +func (w *directPacketWriter) WritePacket(buffer *buf.Buffer, addr M.Socksaddr) error { + return w.writer.WritePacket(buffer, w.source) } diff --git a/inbound/http.go b/inbound/http.go index b4663319..20c8f690 100644 --- a/inbound/http.go +++ b/inbound/http.go @@ -4,7 +4,6 @@ import ( std_bufio "bufio" "context" "net" - "os" "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/tls" @@ -20,8 +19,8 @@ import ( ) var ( - _ adapter.Inbound = (*HTTP)(nil) - _ adapter.InjectableInbound = (*HTTP)(nil) + _ adapter.Inbound = (*HTTP)(nil) + _ adapter.TCPInjectableInbound = (*HTTP)(nil) ) type HTTP struct { @@ -72,7 +71,15 @@ func (h *HTTP) Close() error { ) } -func (h *HTTP) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { +func (h *HTTP) 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 { + h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source)) + } +} + +func (h *HTTP) 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) @@ -80,35 +87,33 @@ func (h *HTTP) NewConnection(ctx context.Context, conn net.Conn, metadata adapte return err } } - return http.HandleConnection(ctx, conn, std_bufio.NewReader(conn), h.authenticator, h.upstreamUserHandler(metadata), adapter.UpstreamMetadata(metadata)) + return http.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), h.authenticator, nil, h.upstreamUserHandlerEx(metadata), metadata.Source, onClose) } -func (h *HTTP) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { - return os.ErrInvalid +func (a *myInboundAdapter) upstreamUserHandlerEx(metadata adapter.InboundContext) adapter.UpstreamHandlerAdapterEx { + return adapter.NewUpstreamHandlerEx(metadata, a.newUserConnection, a.streamUserPacketConnection) } -func (a *myInboundAdapter) upstreamUserHandler(metadata adapter.InboundContext) adapter.UpstreamHandlerAdapter { - return adapter.NewUpstreamHandler(metadata, a.newUserConnection, a.streamUserPacketConnection, a) -} - -func (a *myInboundAdapter) newUserConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { +func (a *myInboundAdapter) newUserConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) { user, loaded := auth.UserFromContext[string](ctx) if !loaded { a.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination) - return a.router.RouteConnection(ctx, conn, metadata) + a.router.RouteConnectionEx(ctx, conn, metadata, onClose) + return } metadata.User = user a.logger.InfoContext(ctx, "[", user, "] inbound connection to ", metadata.Destination) - return a.router.RouteConnection(ctx, conn, metadata) + a.router.RouteConnectionEx(ctx, conn, metadata, onClose) } -func (a *myInboundAdapter) streamUserPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { +func (a *myInboundAdapter) streamUserPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) { user, loaded := auth.UserFromContext[string](ctx) if !loaded { a.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination) - return a.router.RoutePacketConnection(ctx, conn, metadata) + a.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose) + return } metadata.User = user a.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination) - return a.router.RoutePacketConnection(ctx, conn, metadata) + a.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose) } diff --git a/inbound/mixed.go b/inbound/mixed.go index 3933f7af..81f6a43a 100644 --- a/inbound/mixed.go +++ b/inbound/mixed.go @@ -4,7 +4,6 @@ import ( std_bufio "bufio" "context" "net" - "os" "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/uot" @@ -12,6 +11,7 @@ import ( "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing/common/auth" + E "github.com/sagernet/sing/common/exceptions" N "github.com/sagernet/sing/common/network" "github.com/sagernet/sing/protocol/http" "github.com/sagernet/sing/protocol/socks" @@ -20,8 +20,8 @@ import ( ) var ( - _ adapter.Inbound = (*Mixed)(nil) - _ adapter.InjectableInbound = (*Mixed)(nil) + _ adapter.Inbound = (*Mixed)(nil) + _ adapter.TCPInjectableInbound = (*Mixed)(nil) ) type Mixed struct { @@ -47,20 +47,24 @@ func NewMixed(ctx context.Context, router adapter.Router, logger log.ContextLogg return inbound } -func (h *Mixed) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { - reader := std_bufio.NewReader(conn) - headerBytes, err := reader.Peek(1) +func (h *Mixed) 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 { - return err - } - switch headerBytes[0] { - case socks4.Version, socks5.Version: - return socks.HandleConnection0(ctx, conn, reader, h.authenticator, h.upstreamUserHandler(metadata), adapter.UpstreamMetadata(metadata)) - default: - return http.HandleConnection(ctx, conn, reader, h.authenticator, h.upstreamUserHandler(metadata), adapter.UpstreamMetadata(metadata)) + h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source)) } } -func (h *Mixed) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { - return os.ErrInvalid +func (h *Mixed) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) error { + reader := std_bufio.NewReader(conn) + headerBytes, err := reader.Peek(1) + if err != nil { + return E.Cause(err, "peek first byte") + } + switch headerBytes[0] { + case socks4.Version, socks5.Version: + return socks.HandleConnectionEx(ctx, conn, reader, h.authenticator, nil, h.upstreamUserHandlerEx(metadata), metadata.Source, metadata.Destination, onClose) + default: + return http.HandleConnectionEx(ctx, conn, reader, h.authenticator, nil, h.upstreamUserHandlerEx(metadata), metadata.Source, onClose) + } } diff --git a/inbound/naive.go b/inbound/naive.go index 07328c09..498e823c 100644 --- a/inbound/naive.go +++ b/inbound/naive.go @@ -17,6 +17,7 @@ import ( C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing-box/transport/v2rayhttp" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/auth" "github.com/sagernet/sing/common/buf" @@ -164,13 +165,13 @@ func (n *Naive) ServeHTTP(writer http.ResponseWriter, request *http.Request) { n.badRequest(ctx, request, E.New("hijack failed")) return } - n.newConnection(ctx, &naiveH1Conn{Conn: conn}, userName, source, destination) + n.newConnection(ctx, false, &naiveH1Conn{Conn: conn}, userName, source, destination) } else { - n.newConnection(ctx, &naiveH2Conn{reader: request.Body, writer: writer, flusher: writer.(http.Flusher)}, userName, source, destination) + n.newConnection(ctx, true, &naiveH2Conn{reader: request.Body, writer: writer, flusher: writer.(http.Flusher)}, userName, source, destination) } } -func (n *Naive) newConnection(ctx context.Context, conn net.Conn, userName string, source, destination M.Socksaddr) { +func (n *Naive) newConnection(ctx context.Context, waitForClose bool, conn net.Conn, userName string, source M.Socksaddr, destination M.Socksaddr) { if userName != "" { n.logger.InfoContext(ctx, "[", userName, "] inbound connection from ", source) n.logger.InfoContext(ctx, "[", userName, "] inbound connection to ", destination) @@ -178,19 +179,26 @@ func (n *Naive) newConnection(ctx context.Context, conn net.Conn, userName strin n.logger.InfoContext(ctx, "inbound connection from ", source) n.logger.InfoContext(ctx, "inbound connection to ", destination) } - hErr := n.router.RouteConnection(ctx, conn, n.createMetadata(conn, adapter.InboundContext{ + metadata := n.createMetadata(conn, adapter.InboundContext{ Source: source, Destination: destination, User: userName, - })) - if hErr != nil { - conn.Close() - n.NewError(ctx, E.Cause(hErr, "process connection from ", source)) + }) + if !waitForClose { + n.router.RouteConnectionEx(ctx, conn, metadata, nil) + } else { + done := make(chan struct{}) + wrapper := v2rayhttp.NewHTTP2Wrapper(conn) + n.router.RouteConnectionEx(ctx, conn, metadata, N.OnceClose(func(it error) { + close(done) + })) + <-done + wrapper.CloseWrapper() } } func (n *Naive) badRequest(ctx context.Context, request *http.Request, err error) { - n.NewError(ctx, E.Cause(err, "process connection from ", request.RemoteAddr)) + n.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", request.RemoteAddr)) } func rejectHTTP(writer http.ResponseWriter, statusCode int) { diff --git a/inbound/redirect.go b/inbound/redirect.go index 4c7cf1d5..c4c6faf3 100644 --- a/inbound/redirect.go +++ b/inbound/redirect.go @@ -9,7 +9,6 @@ import ( C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" - E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" ) @@ -34,11 +33,13 @@ func NewRedirect(ctx context.Context, router adapter.Router, logger log.ContextL return redirect } -func (r *Redirect) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { +func (r *Redirect) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) { destination, err := redir.GetOriginalDestination(conn) if err != nil { - return E.Cause(err, "get redirect destination") + conn.Close() + r.logger.ErrorContext(ctx, "process connection from ", conn.RemoteAddr(), ": get redirect destination: ", err) + return } metadata.Destination = M.SocksaddrFromNetIP(destination) - return r.newConnection(ctx, conn, metadata) + r.newConnectionEx(ctx, conn, metadata, onClose) } diff --git a/inbound/shadowsocks.go b/inbound/shadowsocks.go index ca15b8d8..3fff231d 100644 --- a/inbound/shadowsocks.go +++ b/inbound/shadowsocks.go @@ -3,7 +3,6 @@ package inbound import ( "context" "net" - "os" "time" "github.com/sagernet/sing-box/adapter" @@ -18,6 +17,7 @@ import ( "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/buf" E "github.com/sagernet/sing/common/exceptions" + M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" "github.com/sagernet/sing/common/ntp" ) @@ -36,8 +36,8 @@ func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte } var ( - _ adapter.Inbound = (*Shadowsocks)(nil) - _ adapter.InjectableInbound = (*Shadowsocks)(nil) + _ adapter.Inbound = (*Shadowsocks)(nil) + _ adapter.TCPInjectableInbound = (*Shadowsocks)(nil) ) type Shadowsocks struct { @@ -74,11 +74,11 @@ func newShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte } switch { case options.Method == shadowsocks.MethodNone: - inbound.service = shadowsocks.NewNoneService(int64(udpTimeout.Seconds()), inbound.upstreamContextHandler()) + inbound.service = shadowsocks.NewNoneService(int64(udpTimeout.Seconds()), adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newConnection, inbound.newPacketConnection, inbound)) case common.Contains(shadowaead.List, options.Method): - inbound.service, err = shadowaead.NewService(options.Method, nil, options.Password, int64(udpTimeout.Seconds()), inbound.upstreamContextHandler()) + inbound.service, err = shadowaead.NewService(options.Method, nil, options.Password, int64(udpTimeout.Seconds()), adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newConnection, inbound.newPacketConnection, inbound)) case common.Contains(shadowaead_2022.List, options.Method): - inbound.service, err = shadowaead_2022.NewServiceWithPassword(options.Method, options.Password, int64(udpTimeout.Seconds()), inbound.upstreamContextHandler(), ntp.TimeFuncFromContext(ctx)) + inbound.service, err = shadowaead_2022.NewServiceWithPassword(options.Method, options.Password, int64(udpTimeout.Seconds()), adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newConnection, inbound.newPacketConnection, inbound), ntp.TimeFuncFromContext(ctx)) default: err = E.New("unsupported method: ", options.Method) } @@ -86,14 +86,29 @@ func newShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte return inbound, err } -func (h *Shadowsocks) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { - return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata)) +func (h *Shadowsocks) 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) + if err != nil { + h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source)) + } } -func (h *Shadowsocks) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata adapter.InboundContext) error { - return h.service.NewPacket(adapter.WithContext(ctx, &metadata), conn, buffer, adapter.UpstreamMetadata(metadata)) +func (h *Shadowsocks) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) { + err := h.service.NewPacket(h.ctx, h.packetConn(), buffer, M.Metadata{Source: source}) + if err != nil { + h.logger.Error(E.Cause(err, "process packet from ", source)) + } } -func (h *Shadowsocks) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { - return os.ErrInvalid +func (h *Shadowsocks) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { + h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination) + return h.router.RouteConnection(ctx, conn, h.createMetadata(conn, metadata)) +} + +func (h *Shadowsocks) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { + ctx = log.ContextWithNewID(ctx) + h.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source) + h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination) + return h.router.RoutePacketConnection(ctx, conn, h.createPacketMetadata(conn, metadata)) } diff --git a/inbound/shadowsocks_multi.go b/inbound/shadowsocks_multi.go index a291af4a..29534194 100644 --- a/inbound/shadowsocks_multi.go +++ b/inbound/shadowsocks_multi.go @@ -20,13 +20,14 @@ import ( "github.com/sagernet/sing/common/buf" E "github.com/sagernet/sing/common/exceptions" F "github.com/sagernet/sing/common/format" + M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" "github.com/sagernet/sing/common/ntp" ) var ( - _ adapter.Inbound = (*ShadowsocksMulti)(nil) - _ adapter.InjectableInbound = (*ShadowsocksMulti)(nil) + _ adapter.Inbound = (*ShadowsocksMulti)(nil) + _ adapter.TCPInjectableInbound = (*ShadowsocksMulti)(nil) ) type ShadowsocksMulti struct { @@ -66,14 +67,15 @@ func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log. options.Method, options.Password, int64(udpTimeout.Seconds()), - adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound), + adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newConnection, inbound.newPacketConnection, inbound), ntp.TimeFuncFromContext(ctx), ) } else if common.Contains(shadowaead.List, options.Method) { service, err = shadowaead.NewMultiService[int]( options.Method, int64(udpTimeout.Seconds()), - adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound)) + adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newConnection, inbound.newPacketConnection, inbound), + ) } else { return nil, E.New("unsupported method: " + options.Method) } @@ -94,16 +96,19 @@ func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log. return inbound, err } -func (h *ShadowsocksMulti) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { - return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata)) +func (h *ShadowsocksMulti) 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) + if err != nil { + h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source)) + } } -func (h *ShadowsocksMulti) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata adapter.InboundContext) error { - return h.service.NewPacket(adapter.WithContext(ctx, &metadata), conn, buffer, adapter.UpstreamMetadata(metadata)) -} - -func (h *ShadowsocksMulti) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { - return os.ErrInvalid +func (h *ShadowsocksMulti) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) { + err := h.service.NewPacket(h.ctx, h.packetConn(), buffer, M.Metadata{Source: source}) + if err != nil { + h.logger.Error(E.Cause(err, "process packet from ", source)) + } } func (h *ShadowsocksMulti) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { @@ -118,7 +123,7 @@ func (h *ShadowsocksMulti) newConnection(ctx context.Context, conn net.Conn, met metadata.User = user } h.logger.InfoContext(ctx, "[", user, "] inbound connection to ", metadata.Destination) - return h.router.RouteConnection(ctx, conn, metadata) + return h.router.RouteConnection(ctx, conn, h.createMetadata(conn, metadata)) } func (h *ShadowsocksMulti) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { @@ -135,5 +140,5 @@ func (h *ShadowsocksMulti) newPacketConnection(ctx context.Context, conn N.Packe ctx = log.ContextWithNewID(ctx) h.logger.InfoContext(ctx, "[", user, "] inbound packet connection from ", metadata.Source) h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination) - return h.router.RoutePacketConnection(ctx, conn, metadata) + return h.router.RoutePacketConnection(ctx, conn, h.createPacketMetadata(conn, metadata)) } diff --git a/inbound/shadowsocks_relay.go b/inbound/shadowsocks_relay.go index fbc2838a..02246a3f 100644 --- a/inbound/shadowsocks_relay.go +++ b/inbound/shadowsocks_relay.go @@ -16,13 +16,15 @@ import ( "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/auth" "github.com/sagernet/sing/common/buf" + E "github.com/sagernet/sing/common/exceptions" F "github.com/sagernet/sing/common/format" + M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" ) var ( - _ adapter.Inbound = (*ShadowsocksRelay)(nil) - _ adapter.InjectableInbound = (*ShadowsocksRelay)(nil) + _ adapter.Inbound = (*ShadowsocksRelay)(nil) + _ adapter.TCPInjectableInbound = (*ShadowsocksRelay)(nil) ) type ShadowsocksRelay struct { @@ -61,7 +63,7 @@ func newShadowsocksRelay(ctx context.Context, router adapter.Router, logger log. options.Method, options.Password, int64(udpTimeout.Seconds()), - adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound), + adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newConnection, inbound.newPacketConnection, inbound), ) if err != nil { return nil, err @@ -79,16 +81,19 @@ func newShadowsocksRelay(ctx context.Context, router adapter.Router, logger log. return inbound, err } -func (h *ShadowsocksRelay) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { - return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata)) +func (h *ShadowsocksRelay) 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) + if err != nil { + h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source)) + } } -func (h *ShadowsocksRelay) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata adapter.InboundContext) error { - return h.service.NewPacket(adapter.WithContext(ctx, &metadata), conn, buffer, adapter.UpstreamMetadata(metadata)) -} - -func (h *ShadowsocksRelay) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { - return os.ErrInvalid +func (h *ShadowsocksRelay) NewPacketEx(buffer *buf.Buffer, source M.Socksaddr) { + err := h.service.NewPacket(h.ctx, h.packetConn(), buffer, M.Metadata{Source: source}) + if err != nil { + h.logger.Error(E.Cause(err, "process packet from ", source)) + } } func (h *ShadowsocksRelay) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { @@ -103,7 +108,7 @@ func (h *ShadowsocksRelay) newConnection(ctx context.Context, conn net.Conn, met metadata.User = destination } h.logger.InfoContext(ctx, "[", destination, "] inbound connection to ", metadata.Destination) - return h.router.RouteConnection(ctx, conn, metadata) + return h.router.RouteConnection(ctx, conn, h.createMetadata(conn, metadata)) } func (h *ShadowsocksRelay) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { @@ -120,5 +125,5 @@ func (h *ShadowsocksRelay) newPacketConnection(ctx context.Context, conn N.Packe ctx = log.ContextWithNewID(ctx) h.logger.InfoContext(ctx, "[", destination, "] inbound packet connection from ", metadata.Source) h.logger.InfoContext(ctx, "[", destination, "] inbound packet connection to ", metadata.Destination) - return h.router.RoutePacketConnection(ctx, conn, metadata) + return h.router.RoutePacketConnection(ctx, conn, h.createPacketMetadata(conn, metadata)) } diff --git a/inbound/shadowtls.go b/inbound/shadowtls.go index 358564f2..ca142286 100644 --- a/inbound/shadowtls.go +++ b/inbound/shadowtls.go @@ -12,6 +12,7 @@ import ( "github.com/sagernet/sing-shadowtls" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/auth" + E "github.com/sagernet/sing/common/exceptions" N "github.com/sagernet/sing/common/network" ) @@ -91,3 +92,11 @@ func (h *ShadowTLS) newConnection(ctx context.Context, conn net.Conn, metadata a } return h.router.RouteConnection(ctx, conn, metadata) } + +func (h *ShadowTLS) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) { + err := h.NewConnection(ctx, conn, metadata) + N.CloseOnHandshakeFailure(conn, onClose, err) + if err != nil { + h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source)) + } +} diff --git a/inbound/socks.go b/inbound/socks.go index 7c5183c9..04b0a77d 100644 --- a/inbound/socks.go +++ b/inbound/socks.go @@ -1,9 +1,9 @@ package inbound import ( + std_bufio "bufio" "context" "net" - "os" "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/uot" @@ -11,13 +11,14 @@ import ( "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing/common/auth" + E "github.com/sagernet/sing/common/exceptions" N "github.com/sagernet/sing/common/network" "github.com/sagernet/sing/protocol/socks" ) var ( - _ adapter.Inbound = (*Socks)(nil) - _ adapter.InjectableInbound = (*Socks)(nil) + _ adapter.Inbound = (*Socks)(nil) + _ adapter.TCPInjectableInbound = (*Socks)(nil) ) type Socks struct { @@ -42,10 +43,10 @@ func NewSocks(ctx context.Context, router adapter.Router, logger log.ContextLogg return inbound } -func (h *Socks) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { - return socks.HandleConnection(ctx, conn, h.authenticator, h.upstreamUserHandler(metadata), adapter.UpstreamMetadata(metadata)) -} - -func (h *Socks) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { - return os.ErrInvalid +func (h *Socks) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) { + err := socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), h.authenticator, nil, h.upstreamUserHandlerEx(metadata), metadata.Source, metadata.Destination, onClose) + N.CloseOnHandshakeFailure(conn, onClose, err) + if err != nil { + h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source)) + } } diff --git a/inbound/tproxy.go b/inbound/tproxy.go index a074eb49..40653c79 100644 --- a/inbound/tproxy.go +++ b/inbound/tproxy.go @@ -18,12 +18,12 @@ import ( E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" - "github.com/sagernet/sing/common/udpnat" + "github.com/sagernet/sing/common/udpnat2" ) type TProxy struct { myInboundAdapter - udpNat *udpnat.Service[netip.AddrPort] + udpNat *udpnat.Service } func NewTProxy(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TProxyInboundOptions) *TProxy { @@ -46,8 +46,7 @@ func NewTProxy(ctx context.Context, router adapter.Router, logger log.ContextLog } tproxy.connHandler = tproxy tproxy.oobPacketHandler = tproxy - tproxy.udpNat = udpnat.New[netip.AddrPort](int64(udpTimeout.Seconds()), tproxy.upstreamContextHandler()) - tproxy.packetUpstream = tproxy.udpNat + tproxy.udpNat = udpnat.New(tproxy, tproxy.preparePacketConnection, udpTimeout, false) return tproxy } @@ -75,35 +74,43 @@ func (t *TProxy) Start() error { return nil } -func (t *TProxy) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { +func (t *TProxy) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) { metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap() - return t.newConnection(ctx, conn, metadata) + t.newConnectionEx(ctx, conn, metadata, onClose) } -func (t *TProxy) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, oob []byte, metadata adapter.InboundContext) error { +func (t *TProxy) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) { + t.newPacketConnectionEx(ctx, conn, t.createPacketMetadataEx(source, destination), onClose) +} + +func (t *TProxy) NewPacketEx(buffer *buf.Buffer, oob []byte, source M.Socksaddr) { destination, err := redir.GetOriginalDestinationFromOOB(oob) if err != nil { - return E.Cause(err, "get tproxy destination") + t.logger.Warn("process packet from ", source, ": get tproxy destination: ", err) + return } - metadata.Destination = M.SocksaddrFromNetIP(destination).Unwrap() - t.udpNat.NewContextPacket(ctx, metadata.Source.AddrPort(), buffer, adapter.UpstreamMetadata(metadata), func(natConn N.PacketConn) (context.Context, N.PacketWriter) { - return adapter.WithContext(log.ContextWithNewID(ctx), &metadata), &tproxyPacketWriter{ctx: ctx, source: natConn, destination: metadata.Destination} - }) - return nil + t.udpNat.NewPacket([][]byte{buffer.Bytes()}, source, M.SocksaddrFromNetIP(destination), nil) } type tproxyPacketWriter struct { ctx context.Context - source N.PacketConn + source netip.AddrPort destination M.Socksaddr conn *net.UDPConn } +func (t *TProxy) preparePacketConnection(source M.Socksaddr, destination M.Socksaddr, userData any) (bool, context.Context, N.PacketWriter, N.CloseHandlerFunc) { + writer := &tproxyPacketWriter{ctx: t.ctx, source: source.AddrPort(), destination: destination} + return true, t.ctx, writer, func(it error) { + common.Close(common.PtrOrNil(writer.conn)) + } +} + func (w *tproxyPacketWriter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error { defer buffer.Release() conn := w.conn if w.destination == destination && conn != nil { - _, err := conn.WriteToUDPAddrPort(buffer.Bytes(), M.AddrPortFromNet(w.source.LocalAddr())) + _, err := conn.WriteToUDPAddrPort(buffer.Bytes(), w.source) if err != nil { w.conn = nil } @@ -122,9 +129,5 @@ func (w *tproxyPacketWriter) WritePacket(buffer *buf.Buffer, destination M.Socks } else { defer udpConn.Close() } - return common.Error(udpConn.WriteToUDPAddrPort(buffer.Bytes(), M.AddrPortFromNet(w.source.LocalAddr()))) -} - -func (w *tproxyPacketWriter) Close() error { - return common.Close(common.PtrOrNil(w.conn)) + return common.Error(udpConn.WriteToUDPAddrPort(buffer.Bytes(), w.source)) } diff --git a/inbound/trojan.go b/inbound/trojan.go index 203b6d39..ce003dda 100644 --- a/inbound/trojan.go +++ b/inbound/trojan.go @@ -22,8 +22,8 @@ import ( ) var ( - _ adapter.Inbound = (*Trojan)(nil) - _ adapter.InjectableInbound = (*Trojan)(nil) + _ adapter.Inbound = (*Trojan)(nil) + _ adapter.TCPInjectableInbound = (*Trojan)(nil) ) type Trojan struct { @@ -90,7 +90,7 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog return nil, err } if options.Transport != nil { - inbound.transport, err = v2ray.NewServerTransport(ctx, common.PtrValueOrDefault(options.Transport), inbound.tlsConfig, (*trojanTransportHandler)(inbound)) + inbound.transport, err = v2ray.NewServerTransport(ctx, logger, common.PtrValueOrDefault(options.Transport), inbound.tlsConfig, (*trojanTransportHandler)(inbound)) if err != nil { return nil, E.Cause(err, "create server transport: ", options.Transport.Type) } @@ -149,11 +149,6 @@ func (h *Trojan) Close() error { ) } -func (h *Trojan) newTransportConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { - h.injectTCP(conn, metadata) - return nil -} - func (h *Trojan) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { var err error if h.tlsConfig != nil && h.transport == nil { @@ -165,8 +160,12 @@ func (h *Trojan) NewConnection(ctx context.Context, conn net.Conn, metadata adap return h.service.NewConnection(adapter.WithContext(ctx, &metadata), conn, adapter.UpstreamMetadata(metadata)) } -func (h *Trojan) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { - return os.ErrInvalid +func (h *Trojan) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) { + err := h.NewConnection(ctx, conn, metadata) + N.CloseOnHandshakeFailure(conn, onClose, err) + if err != nil { + h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source)) + } } func (h *Trojan) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { @@ -226,9 +225,6 @@ var _ adapter.V2RayServerTransportHandler = (*trojanTransportHandler)(nil) type trojanTransportHandler Trojan -func (t *trojanTransportHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error { - return (*Trojan)(t).newTransportConnection(ctx, conn, adapter.InboundContext{ - Source: metadata.Source, - Destination: metadata.Destination, - }) +func (t *trojanTransportHandler) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) { + (*Trojan)(t).routeTCP(ctx, conn, source, destination, onClose) } diff --git a/inbound/tun.go b/inbound/tun.go index 721112d7..0d856419 100644 --- a/inbound/tun.go +++ b/inbound/tun.go @@ -28,17 +28,18 @@ import ( "go4.org/netipx" ) -var _ adapter.Inbound = (*Tun)(nil) +var _ adapter.Inbound = (*TUN)(nil) -type Tun struct { - tag string - ctx context.Context - router adapter.Router - logger log.ContextLogger +type TUN struct { + tag string + ctx context.Context + router adapter.Router + logger log.ContextLogger + // Deprecated inboundOptions option.InboundOptions tunOptions tun.Options endpointIndependentNat bool - udpTimeout int64 + udpTimeout time.Duration stack string tunIf tun.Tun tunStack tun.Stack @@ -53,7 +54,7 @@ type Tun struct { routeExcludeAddressSet []*netipx.IPSet } -func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions, platformInterface platform.Interface) (*Tun, error) { +func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions, platformInterface platform.Interface) (*TUN, error) { address := options.Address var deprecatedAddressUsed bool //nolint:staticcheck @@ -162,7 +163,7 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger outputMark = tun.DefaultAutoRedirectOutputMark } - inbound := &Tun{ + inbound := &TUN{ tag: tag, ctx: ctx, router: router, @@ -194,7 +195,7 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger InterfaceMonitor: router.InterfaceMonitor(), }, endpointIndependentNat: options.EndpointIndependentNat, - udpTimeout: int64(udpTimeout.Seconds()), + udpTimeout: udpTimeout, stack: options.Stack, platformInterface: platformInterface, platformOptions: common.PtrValueOrDefault(options.Platform), @@ -207,7 +208,7 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger inbound.autoRedirect, err = tun.NewAutoRedirect(tun.AutoRedirectOptions{ TunOptions: &inbound.tunOptions, Context: ctx, - Handler: inbound, + Handler: (*autoRedirectHandler)(inbound), Logger: logger, NetworkMonitor: router.NetworkMonitor(), InterfaceFinder: router.InterfaceFinder(), @@ -283,17 +284,17 @@ func parseRange(uidRanges []ranges.Range[uint32], rangeList []string) ([]ranges. return uidRanges, nil } -func (t *Tun) Type() string { +func (t *TUN) Type() string { return C.TypeTun } -func (t *Tun) Tag() string { +func (t *TUN) Tag() string { return t.tag } -func (t *Tun) Start() error { +func (t *TUN) Start() error { if C.IsAndroid && t.platformInterface == nil { - t.tunOptions.BuildAndroidRules(t.router.PackageManager(), t) + t.tunOptions.BuildAndroidRules(t.router.PackageManager()) } if t.tunOptions.Name == "" { t.tunOptions.Name = tun.CalculateInterfaceName("") @@ -327,7 +328,6 @@ func (t *Tun) Start() error { Context: t.ctx, Tun: tunInterface, TunOptions: t.tunOptions, - EndpointIndependentNat: t.endpointIndependentNat, UDPTimeout: t.udpTimeout, Handler: t, Logger: t.logger, @@ -349,7 +349,7 @@ func (t *Tun) Start() error { return nil } -func (t *Tun) PostStart() error { +func (t *TUN) PostStart() error { monitor := taskmonitor.New(t.logger, C.StartTimeout) if t.autoRedirect != nil { t.routeAddressSet = common.FlatMap(t.routeRuleSet, adapter.RuleSet.ExtractIPSet) @@ -388,7 +388,7 @@ func (t *Tun) PostStart() error { return nil } -func (t *Tun) updateRouteAddressSet(it adapter.RuleSet) { +func (t *TUN) updateRouteAddressSet(it adapter.RuleSet) { t.routeAddressSet = common.FlatMap(t.routeRuleSet, adapter.RuleSet.ExtractIPSet) t.routeExcludeAddressSet = common.FlatMap(t.routeExcludeRuleSet, adapter.RuleSet.ExtractIPSet) t.autoRedirect.UpdateRouteAddressSet() @@ -396,7 +396,7 @@ func (t *Tun) updateRouteAddressSet(it adapter.RuleSet) { t.routeExcludeAddressSet = nil } -func (t *Tun) Close() error { +func (t *TUN) Close() error { return common.Close( t.tunStack, t.tunIf, @@ -404,44 +404,48 @@ func (t *Tun) Close() error { ) } -func (t *Tun) NewConnection(ctx context.Context, conn net.Conn, upstreamMetadata M.Metadata) error { - ctx = log.ContextWithNewID(ctx) - var metadata adapter.InboundContext - metadata.Inbound = t.tag - metadata.InboundType = C.TypeTun - metadata.Source = upstreamMetadata.Source - metadata.Destination = upstreamMetadata.Destination - metadata.InboundOptions = t.inboundOptions - if upstreamMetadata.Protocol != "" { - t.logger.InfoContext(ctx, "inbound ", upstreamMetadata.Protocol, " connection from ", metadata.Source) - } else { - t.logger.InfoContext(ctx, "inbound connection from ", metadata.Source) - } - t.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination) - err := t.router.RouteConnection(ctx, conn, metadata) - if err != nil { - t.NewError(ctx, err) - } +func (t *TUN) PrepareConnection(source M.Socksaddr, destination M.Socksaddr) error { + // TODO: implement rejects return nil } -func (t *Tun) NewPacketConnection(ctx context.Context, conn N.PacketConn, upstreamMetadata M.Metadata) error { +func (t *TUN) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) { ctx = log.ContextWithNewID(ctx) var metadata adapter.InboundContext metadata.Inbound = t.tag metadata.InboundType = C.TypeTun - metadata.Source = upstreamMetadata.Source - metadata.Destination = upstreamMetadata.Destination + metadata.Source = source + metadata.Destination = destination + metadata.InboundOptions = t.inboundOptions + t.logger.InfoContext(ctx, "inbound connection from ", metadata.Source) + t.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination) + t.router.RouteConnectionEx(ctx, conn, metadata, onClose) +} + +func (t *TUN) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) { + ctx = log.ContextWithNewID(ctx) + var metadata adapter.InboundContext + metadata.Inbound = t.tag + metadata.InboundType = C.TypeTun + metadata.Source = source + metadata.Destination = destination metadata.InboundOptions = t.inboundOptions t.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source) t.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination) - err := t.router.RoutePacketConnection(ctx, conn, metadata) - if err != nil { - t.NewError(ctx, err) - } - return nil + t.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose) } -func (t *Tun) NewError(ctx context.Context, err error) { - NewError(t.logger, ctx, err) +type autoRedirectHandler TUN + +func (t *autoRedirectHandler) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) { + ctx = log.ContextWithNewID(ctx) + var metadata adapter.InboundContext + metadata.Inbound = t.tag + metadata.InboundType = C.TypeTun + metadata.Source = source + metadata.Destination = destination + metadata.InboundOptions = t.inboundOptions + t.logger.InfoContext(ctx, "inbound redirect connection from ", metadata.Source) + t.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination) + t.router.RouteConnectionEx(ctx, conn, metadata, onClose) } diff --git a/inbound/vless.go b/inbound/vless.go index 56747564..ec26bd88 100644 --- a/inbound/vless.go +++ b/inbound/vless.go @@ -25,8 +25,8 @@ import ( ) var ( - _ adapter.Inbound = (*VLESS)(nil) - _ adapter.InjectableInbound = (*VLESS)(nil) + _ adapter.Inbound = (*VLESS)(nil) + _ adapter.TCPInjectableInbound = (*VLESS)(nil) ) type VLESS struct { @@ -73,7 +73,7 @@ func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogg } } if options.Transport != nil { - inbound.transport, err = v2ray.NewServerTransport(ctx, common.PtrValueOrDefault(options.Transport), inbound.tlsConfig, (*vlessTransportHandler)(inbound)) + inbound.transport, err = v2ray.NewServerTransport(ctx, logger, common.PtrValueOrDefault(options.Transport), inbound.tlsConfig, (*vlessTransportHandler)(inbound)) if err != nil { return nil, E.Cause(err, "create server transport: ", options.Transport.Type) } @@ -128,11 +128,6 @@ func (h *VLESS) Close() error { ) } -func (h *VLESS) newTransportConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { - h.injectTCP(conn, metadata) - return nil -} - func (h *VLESS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { var err error if h.tlsConfig != nil && h.transport == nil { @@ -144,8 +139,12 @@ func (h *VLESS) NewConnection(ctx context.Context, conn net.Conn, metadata adapt return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata)) } -func (h *VLESS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { - return os.ErrInvalid +func (h *VLESS) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) { + err := h.NewConnection(ctx, conn, metadata) + N.CloseOnHandshakeFailure(conn, onClose, err) + if err != nil { + h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source)) + } } func (h *VLESS) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { @@ -188,9 +187,6 @@ var _ adapter.V2RayServerTransportHandler = (*vlessTransportHandler)(nil) type vlessTransportHandler VLESS -func (t *vlessTransportHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error { - return (*VLESS)(t).newTransportConnection(ctx, conn, adapter.InboundContext{ - Source: metadata.Source, - Destination: metadata.Destination, - }) +func (t *vlessTransportHandler) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) { + t.routeTCP(ctx, conn, source, destination, onClose) } diff --git a/inbound/vmess.go b/inbound/vmess.go index 15451275..9099bd62 100644 --- a/inbound/vmess.go +++ b/inbound/vmess.go @@ -25,8 +25,8 @@ import ( ) var ( - _ adapter.Inbound = (*VMess)(nil) - _ adapter.InjectableInbound = (*VMess)(nil) + _ adapter.Inbound = (*VMess)(nil) + _ adapter.TCPInjectableInbound = (*VMess)(nil) ) type VMess struct { @@ -83,7 +83,7 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg } } if options.Transport != nil { - inbound.transport, err = v2ray.NewServerTransport(ctx, common.PtrValueOrDefault(options.Transport), inbound.tlsConfig, (*vmessTransportHandler)(inbound)) + inbound.transport, err = v2ray.NewServerTransport(ctx, logger, common.PtrValueOrDefault(options.Transport), inbound.tlsConfig, (*vmessTransportHandler)(inbound)) if err != nil { return nil, E.Cause(err, "create server transport: ", options.Transport.Type) } @@ -142,11 +142,6 @@ func (h *VMess) Close() error { ) } -func (h *VMess) newTransportConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { - h.injectTCP(conn, metadata) - return nil -} - func (h *VMess) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { var err error if h.tlsConfig != nil && h.transport == nil { @@ -158,8 +153,12 @@ func (h *VMess) NewConnection(ctx context.Context, conn net.Conn, metadata adapt return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata)) } -func (h *VMess) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { - return os.ErrInvalid +func (h *VMess) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) { + err := h.NewConnection(ctx, conn, metadata) + N.CloseOnHandshakeFailure(conn, onClose, err) + if err != nil { + h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source)) + } } func (h *VMess) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { @@ -202,9 +201,6 @@ var _ adapter.V2RayServerTransportHandler = (*vmessTransportHandler)(nil) type vmessTransportHandler VMess -func (t *vmessTransportHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error { - return (*VMess)(t).newTransportConnection(ctx, conn, adapter.InboundContext{ - Source: metadata.Source, - Destination: metadata.Destination, - }) +func (t *vmessTransportHandler) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) { + (*VMess)(t).routeTCP(ctx, conn, source, destination, onClose) } diff --git a/include/quic_stub.go b/include/quic_stub.go index ddf9723f..43aa58d9 100644 --- a/include/quic_stub.go +++ b/include/quic_stub.go @@ -11,6 +11,7 @@ import ( "github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/transport/v2ray" "github.com/sagernet/sing-dns" + "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" ) @@ -20,7 +21,7 @@ func init() { return nil, C.ErrQUICNotIncluded }) v2ray.RegisterQUICConstructor( - func(ctx context.Context, options option.V2RayQUICOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (adapter.V2RayServerTransport, error) { + func(ctx context.Context, logger logger.ContextLogger, options option.V2RayQUICOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (adapter.V2RayServerTransport, error) { return nil, C.ErrQUICNotIncluded }, func(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayQUICOptions, tlsConfig tls.Config) (adapter.V2RayClientTransport, error) { diff --git a/option/inbound.go b/option/inbound.go index 54e8bab8..d3879904 100644 --- a/option/inbound.go +++ b/option/inbound.go @@ -98,6 +98,7 @@ func (h *Inbound) UnmarshalJSON(bytes []byte) error { return nil } +// Deprecated: Use rule action instead type InboundOptions struct { SniffEnabled bool `json:"sniff,omitempty"` SniffOverrideDestination bool `json:"sniff_override_destination,omitempty"` diff --git a/option/rule.go b/option/rule.go index 0c66ab1d..0b11cbdd 100644 --- a/option/rule.go +++ b/option/rule.go @@ -64,7 +64,7 @@ func (r Rule) IsValid() bool { } } -type DefaultRule struct { +type RawDefaultRule struct { Inbound Listable[string] `json:"inbound,omitempty"` IPVersion int `json:"ip_version,omitempty"` Network Listable[string] `json:"network,omitempty"` @@ -98,26 +98,58 @@ type DefaultRule struct { RuleSet Listable[string] `json:"rule_set,omitempty"` RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"` Invert bool `json:"invert,omitempty"` - Outbound string `json:"outbound,omitempty"` // Deprecated: renamed to rule_set_ip_cidr_match_source Deprecated_RulesetIPCIDRMatchSource bool `json:"rule_set_ipcidr_match_source,omitempty"` } +type DefaultRule struct { + RawDefaultRule + RuleAction +} + +func (r *DefaultRule) MarshalJSON() ([]byte, error) { + return MarshallObjects(r.RawDefaultRule, r.RuleAction) +} + +func (r *DefaultRule) UnmarshalJSON(data []byte) error { + err := json.Unmarshal(data, &r.RawDefaultRule) + if err != nil { + return err + } + return UnmarshallExcluded(data, &r.RawDefaultRule, &r.RuleAction) +} + func (r *DefaultRule) IsValid() bool { var defaultValue DefaultRule defaultValue.Invert = r.Invert - defaultValue.Outbound = r.Outbound + defaultValue.Action = r.Action return !reflect.DeepEqual(r, defaultValue) } -type LogicalRule struct { - Mode string `json:"mode"` - Rules []Rule `json:"rules,omitempty"` - Invert bool `json:"invert,omitempty"` - Outbound string `json:"outbound,omitempty"` +type _LogicalRule struct { + Mode string `json:"mode"` + Rules []Rule `json:"rules,omitempty"` + Invert bool `json:"invert,omitempty"` } -func (r LogicalRule) IsValid() bool { +type LogicalRule struct { + _LogicalRule + RuleAction +} + +func (r *LogicalRule) MarshalJSON() ([]byte, error) { + return MarshallObjects(r._LogicalRule, r.RuleAction) +} + +func (r *LogicalRule) UnmarshalJSON(data []byte) error { + err := json.Unmarshal(data, &r._LogicalRule) + if err != nil { + return err + } + return UnmarshallExcluded(data, &r._LogicalRule, &r.RuleAction) +} + +func (r *LogicalRule) IsValid() bool { return len(r.Rules) > 0 && common.All(r.Rules, Rule.IsValid) } diff --git a/option/rule_action.go b/option/rule_action.go new file mode 100644 index 00000000..4f0ec177 --- /dev/null +++ b/option/rule_action.go @@ -0,0 +1,166 @@ +package option + +import ( + C "github.com/sagernet/sing-box/constant" + E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/json" +) + +type _RuleAction struct { + Action string `json:"action,omitempty"` + RouteOptions RouteActionOptions `json:"-"` + RejectOptions RejectActionOptions `json:"-"` + SniffOptions RouteActionSniff `json:"-"` + ResolveOptions RouteActionResolve `json:"-"` +} + +type RuleAction _RuleAction + +func (r RuleAction) MarshalJSON() ([]byte, error) { + var v any + switch r.Action { + case C.RuleActionTypeRoute: + r.Action = "" + v = r.RouteOptions + case C.RuleActionTypeReturn: + v = nil + case C.RuleActionTypeReject: + v = r.RejectOptions + case C.RuleActionTypeHijackDNS: + v = nil + case C.RuleActionTypeSniff: + v = r.SniffOptions + case C.RuleActionTypeResolve: + v = r.ResolveOptions + default: + return nil, E.New("unknown rule action: " + r.Action) + } + if v == nil { + return MarshallObjects((_RuleAction)(r)) + } + return MarshallObjects((_RuleAction)(r), v) +} + +func (r *RuleAction) UnmarshalJSON(data []byte) error { + err := json.Unmarshal(data, (*_RuleAction)(r)) + if err != nil { + return err + } + var v any + switch r.Action { + case "", C.RuleActionTypeRoute: + r.Action = C.RuleActionTypeRoute + v = &r.RouteOptions + case C.RuleActionTypeReturn: + v = nil + case C.RuleActionTypeReject: + v = &r.RejectOptions + case C.RuleActionTypeHijackDNS: + v = nil + case C.RuleActionTypeSniff: + v = &r.SniffOptions + case C.RuleActionTypeResolve: + v = &r.ResolveOptions + default: + return E.New("unknown rule action: " + r.Action) + } + if v == nil { + // check unknown fields + return json.UnmarshalDisallowUnknownFields(data, &_RuleAction{}) + } + return UnmarshallExcluded(data, (*_RuleAction)(r), v) +} + +type _DNSRuleAction struct { + Action string `json:"action,omitempty"` + RouteOptions DNSRouteActionOptions `json:"-"` + RejectOptions RejectActionOptions `json:"-"` + SniffOptions RouteActionSniff `json:"-"` + ResolveOptions RouteActionResolve `json:"-"` +} + +type DNSRuleAction _DNSRuleAction + +func (r DNSRuleAction) MarshalJSON() ([]byte, error) { + var v any + switch r.Action { + case C.RuleActionTypeRoute: + r.Action = "" + v = r.RouteOptions + case C.RuleActionTypeReturn: + v = nil + case C.RuleActionTypeReject: + v = r.RejectOptions + default: + return nil, E.New("unknown DNS rule action: " + r.Action) + } + if v == nil { + return MarshallObjects((_DNSRuleAction)(r)) + } + return MarshallObjects((_DNSRuleAction)(r), v) +} + +func (r *DNSRuleAction) UnmarshalJSON(data []byte) error { + err := json.Unmarshal(data, (*_DNSRuleAction)(r)) + if err != nil { + return err + } + var v any + switch r.Action { + case "", C.RuleActionTypeRoute: + r.Action = C.RuleActionTypeRoute + v = &r.RouteOptions + case C.RuleActionTypeReturn: + v = nil + case C.RuleActionTypeReject: + v = &r.RejectOptions + default: + return E.New("unknown DNS rule action: " + r.Action) + } + if v == nil { + // check unknown fields + return json.UnmarshalDisallowUnknownFields(data, &_DNSRuleAction{}) + } + return UnmarshallExcluded(data, (*_DNSRuleAction)(r), v) +} + +type RouteActionOptions struct { + Outbound string `json:"outbound"` + UDPDisableDomainUnmapping bool `json:"udp_disable_domain_unmapping,omitempty"` +} + +type DNSRouteActionOptions struct { + Server string `json:"server"` + DisableCache bool `json:"disable_cache,omitempty"` + RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"` + ClientSubnet *AddrPrefix `json:"client_subnet,omitempty"` +} + +type RejectActionOptions struct { + Method RejectMethod `json:"method,omitempty"` +} + +type RejectMethod string + +func (m *RejectMethod) UnmarshalJSON(bytes []byte) error { + err := json.Unmarshal(bytes, (*string)(m)) + if err != nil { + return err + } + switch *m { + case C.RuleActionRejectMethodDefault, C.RuleActionRejectMethodPortUnreachable, C.RuleActionRejectMethodDrop: + return nil + default: + return E.New("unknown reject method: " + *m) + } +} + +type RouteActionSniff struct { + Sniffer Listable[string] `json:"sniffer,omitempty"` + Timeout Duration `json:"timeout,omitempty"` +} + +type RouteActionResolve struct { + Strategy DomainStrategy `json:"strategy,omitempty"` + Server string `json:"server,omitempty"` +} diff --git a/option/rule_dns.go b/option/rule_dns.go index 2e52d6c5..b328c45c 100644 --- a/option/rule_dns.go +++ b/option/rule_dns.go @@ -64,7 +64,7 @@ func (r DNSRule) IsValid() bool { } } -type DefaultDNSRule struct { +type RawDefaultDNSRule struct { Inbound Listable[string] `json:"inbound,omitempty"` IPVersion int `json:"ip_version,omitempty"` QueryType Listable[DNSQueryType] `json:"query_type,omitempty"` @@ -100,35 +100,58 @@ type DefaultDNSRule struct { RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"` RuleSetIPCIDRAcceptEmpty bool `json:"rule_set_ip_cidr_accept_empty,omitempty"` Invert bool `json:"invert,omitempty"` - Server string `json:"server,omitempty"` - DisableCache bool `json:"disable_cache,omitempty"` - RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"` - ClientSubnet *AddrPrefix `json:"client_subnet,omitempty"` // Deprecated: renamed to rule_set_ip_cidr_match_source Deprecated_RulesetIPCIDRMatchSource bool `json:"rule_set_ipcidr_match_source,omitempty"` } +type DefaultDNSRule struct { + RawDefaultDNSRule + DNSRuleAction +} + +func (r *DefaultDNSRule) MarshalJSON() ([]byte, error) { + return MarshallObjects(r.RawDefaultDNSRule, r.DNSRuleAction) +} + +func (r *DefaultDNSRule) UnmarshalJSON(data []byte) error { + err := json.Unmarshal(data, &r.RawDefaultDNSRule) + if err != nil { + return err + } + return UnmarshallExcluded(data, &r.RawDefaultDNSRule, &r.DNSRuleAction) +} + func (r *DefaultDNSRule) IsValid() bool { var defaultValue DefaultDNSRule defaultValue.Invert = r.Invert - defaultValue.Server = r.Server - defaultValue.DisableCache = r.DisableCache - defaultValue.RewriteTTL = r.RewriteTTL - defaultValue.ClientSubnet = r.ClientSubnet + defaultValue.DNSRuleAction = r.DNSRuleAction return !reflect.DeepEqual(r, defaultValue) } -type LogicalDNSRule struct { - Mode string `json:"mode"` - Rules []DNSRule `json:"rules,omitempty"` - Invert bool `json:"invert,omitempty"` - Server string `json:"server,omitempty"` - DisableCache bool `json:"disable_cache,omitempty"` - RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"` - ClientSubnet *AddrPrefix `json:"client_subnet,omitempty"` +type _LogicalDNSRule struct { + Mode string `json:"mode"` + Rules []DNSRule `json:"rules,omitempty"` + Invert bool `json:"invert,omitempty"` } -func (r LogicalDNSRule) IsValid() bool { +type LogicalDNSRule struct { + _LogicalDNSRule + DNSRuleAction +} + +func (r *LogicalDNSRule) MarshalJSON() ([]byte, error) { + return MarshallObjects(r._LogicalDNSRule, r.DNSRuleAction) +} + +func (r *LogicalDNSRule) UnmarshalJSON(data []byte) error { + err := json.Unmarshal(data, &r._LogicalDNSRule) + if err != nil { + return err + } + return UnmarshallExcluded(data, &r._LogicalDNSRule, &r.DNSRuleAction) +} + +func (r *LogicalDNSRule) IsValid() bool { return len(r.Rules) > 0 && common.All(r.Rules, DNSRule.IsValid) } diff --git a/option/types.go b/option/types.go index b17f2222..bb648549 100644 --- a/option/types.go +++ b/option/types.go @@ -81,8 +81,11 @@ func (a *AddrPrefix) UnmarshalJSON(content []byte) error { return prefixErr } -func (a AddrPrefix) Build() netip.Prefix { - return netip.Prefix(a) +func (a *AddrPrefix) Build() netip.Prefix { + if a == nil { + return netip.Prefix{} + } + return netip.Prefix(*a) } type NetworkList string @@ -143,12 +146,29 @@ func (l *Listable[T]) UnmarshalJSON(content []byte) error { type DomainStrategy dns.DomainStrategy +func (s DomainStrategy) String() string { + switch dns.DomainStrategy(s) { + case dns.DomainStrategyAsIS: + return "" + case dns.DomainStrategyPreferIPv4: + return "prefer_ipv4" + case dns.DomainStrategyPreferIPv6: + return "prefer_ipv6" + case dns.DomainStrategyUseIPv4: + return "ipv4_only" + case dns.DomainStrategyUseIPv6: + return "ipv6_only" + default: + panic(E.New("unknown domain strategy: ", s)) + } +} + func (s DomainStrategy) MarshalJSON() ([]byte, error) { var value string switch dns.DomainStrategy(s) { case dns.DomainStrategyAsIS: value = "" - // value = "AsIS" + // value = "as_is" case dns.DomainStrategyPreferIPv4: value = "prefer_ipv4" case dns.DomainStrategyPreferIPv6: diff --git a/outbound/block.go b/outbound/block.go index e73c4422..b6ccefe2 100644 --- a/outbound/block.go +++ b/outbound/block.go @@ -39,12 +39,14 @@ func (h *Block) ListenPacket(ctx context.Context, destination M.Socksaddr) (net. return nil, io.EOF } +// Deprecated func (h *Block) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { conn.Close() h.logger.InfoContext(ctx, "blocked connection to ", metadata.Destination) return nil } +// Deprecated func (h *Block) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { conn.Close() h.logger.InfoContext(ctx, "blocked packet connection to ", metadata.Destination) diff --git a/outbound/default.go b/outbound/default.go index 972aca94..a34ac97a 100644 --- a/outbound/default.go +++ b/outbound/default.go @@ -69,7 +69,7 @@ func NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata a if err != nil { return N.ReportHandshakeFailure(conn, err) } - err = N.ReportHandshakeSuccess(conn) + err = N.ReportConnHandshakeSuccess(conn, outConn) if err != nil { outConn.Close() return err @@ -96,7 +96,7 @@ func NewDirectConnection(ctx context.Context, router adapter.Router, this N.Dial if err != nil { return N.ReportHandshakeFailure(conn, err) } - err = N.ReportHandshakeSuccess(conn) + err = N.ReportConnHandshakeSuccess(conn, outConn) if err != nil { outConn.Close() return err @@ -117,14 +117,14 @@ func NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn, if err != nil { return N.ReportHandshakeFailure(conn, err) } - err = N.ReportHandshakeSuccess(conn) + err = N.ReportPacketConnHandshakeSuccess(conn, outConn) if err != nil { outConn.Close() return err } if destinationAddress.IsValid() { if metadata.Destination.IsFqdn() { - if metadata.InboundOptions.UDPDisableDomainUnmapping { + if metadata.UDPDisableDomainUnmapping { outConn = bufio.NewUnidirectionalNATPacketConn(bufio.NewPacketConn(outConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination) } else { outConn = bufio.NewNATPacketConn(bufio.NewPacketConn(outConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), metadata.Destination) @@ -165,7 +165,7 @@ func NewDirectPacketConnection(ctx context.Context, router adapter.Router, this if err != nil { return N.ReportHandshakeFailure(conn, err) } - err = N.ReportHandshakeSuccess(conn) + err = N.ReportPacketConnHandshakeSuccess(conn, outConn) if err != nil { outConn.Close() return err diff --git a/outbound/direct.go b/outbound/direct.go index c873941c..415a72f3 100644 --- a/outbound/direct.go +++ b/outbound/direct.go @@ -30,7 +30,7 @@ type Direct struct { fallbackDelay time.Duration overrideOption int overrideDestination M.Socksaddr - loopBack *loopBackDetector + // loopBack *loopBackDetector } func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, options option.DirectOutboundOptions) (*Direct, error) { @@ -51,7 +51,7 @@ func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, opti domainStrategy: dns.DomainStrategy(options.DomainStrategy), fallbackDelay: time.Duration(options.FallbackDelay), dialer: outboundDialer, - loopBack: newLoopBackDetector(router), + // loopBack: newLoopBackDetector(router), } if options.ProxyProtocol != 0 { return nil, E.New("Proxy Protocol is deprecated and removed in sing-box 1.6.0") @@ -90,11 +90,12 @@ func (h *Direct) DialContext(ctx context.Context, network string, destination M. case N.NetworkUDP: h.logger.InfoContext(ctx, "outbound packet connection to ", destination) } - conn, err := h.dialer.DialContext(ctx, network, destination) + /*conn, err := h.dialer.DialContext(ctx, network, destination) if err != nil { return nil, err } - return h.loopBack.NewConn(conn), nil + return h.loopBack.NewConn(conn), nil*/ + return h.dialer.DialContext(ctx, network, destination) } func (h *Direct) DialParallel(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr) (net.Conn, error) { @@ -148,14 +149,14 @@ func (h *Direct) ListenPacket(ctx context.Context, destination M.Socksaddr) (net if err != nil { return nil, err } - conn = h.loopBack.NewPacketConn(bufio.NewPacketConn(conn), destination) + // conn = h.loopBack.NewPacketConn(bufio.NewPacketConn(conn), destination) if originDestination != destination { conn = bufio.NewNATPacketConn(bufio.NewPacketConn(conn), destination, originDestination) } return conn, nil } -func (h *Direct) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { +/*func (h *Direct) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { if h.loopBack.CheckConn(metadata.Source.AddrPort(), M.AddrPortFromNet(conn.LocalAddr())) { return E.New("reject loopback connection to ", metadata.Destination) } @@ -168,3 +169,4 @@ func (h *Direct) NewPacketConnection(ctx context.Context, conn N.PacketConn, met } return NewPacketConnection(ctx, h, conn, metadata) } +*/ diff --git a/outbound/dns.go b/outbound/dns.go index 6ad4a0f8..08661a99 100644 --- a/outbound/dns.go +++ b/outbound/dns.go @@ -45,6 +45,7 @@ func (d *DNS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.Pa return nil, os.ErrInvalid } +// Deprecated func (d *DNS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { metadata.Destination = M.Socksaddr{} defer conn.Close() @@ -97,6 +98,7 @@ func (d *DNS) handleConnection(ctx context.Context, conn net.Conn, metadata adap return nil } +// Deprecated func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { metadata.Destination = M.Socksaddr{} var reader N.PacketReader = conn diff --git a/outbound/http.go b/outbound/http.go index cbcbce07..6f15afb5 100644 --- a/outbound/http.go +++ b/outbound/http.go @@ -64,11 +64,3 @@ func (h *HTTP) DialContext(ctx context.Context, network string, destination M.So func (h *HTTP) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { return nil, os.ErrInvalid } - -func (h *HTTP) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { - return NewConnection(ctx, h, conn, metadata) -} - -func (h *HTTP) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { - return os.ErrInvalid -} diff --git a/outbound/hysteria.go b/outbound/hysteria.go index cf7f7fed..f513cf64 100644 --- a/outbound/hysteria.go +++ b/outbound/hysteria.go @@ -122,14 +122,6 @@ func (h *Hysteria) ListenPacket(ctx context.Context, destination M.Socksaddr) (n return h.client.ListenPacket(ctx, destination) } -func (h *Hysteria) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { - return NewConnection(ctx, h, conn, metadata) -} - -func (h *Hysteria) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { - return NewPacketConnection(ctx, h, conn, metadata) -} - func (h *Hysteria) InterfaceUpdated() { h.client.CloseWithError(E.New("network changed")) } diff --git a/outbound/hysteria2.go b/outbound/hysteria2.go index 9079e403..5e46f6a8 100644 --- a/outbound/hysteria2.go +++ b/outbound/hysteria2.go @@ -108,14 +108,6 @@ func (h *Hysteria2) ListenPacket(ctx context.Context, destination M.Socksaddr) ( return h.client.ListenPacket(ctx) } -func (h *Hysteria2) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { - return NewConnection(ctx, h, conn, metadata) -} - -func (h *Hysteria2) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { - return NewPacketConnection(ctx, h, conn, metadata) -} - func (h *Hysteria2) InterfaceUpdated() { h.client.CloseWithError(E.New("network changed")) } diff --git a/outbound/proxy.go b/outbound/proxy.go index fbc48481..38c18453 100644 --- a/outbound/proxy.go +++ b/outbound/proxy.go @@ -94,6 +94,9 @@ 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{}) } diff --git a/outbound/selector.go b/outbound/selector.go index 7326f58f..64e6a2f9 100644 --- a/outbound/selector.go +++ b/outbound/selector.go @@ -146,14 +146,26 @@ func (s *Selector) ListenPacket(ctx context.Context, destination M.Socksaddr) (n return s.interruptGroup.NewPacketConn(conn, interrupt.IsExternalConnectionFromContext(ctx)), nil } +// TODO +// Deprecated func (s *Selector) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { ctx = interrupt.ContextWithIsExternalConnection(ctx) - return s.selected.NewConnection(ctx, conn, metadata) + if legacyHandler, ok := s.selected.(adapter.ConnectionHandler); ok { + return legacyHandler.NewConnection(ctx, conn, metadata) + } else { + return NewConnection(ctx, s.selected, conn, metadata) + } } +// TODO +// Deprecated func (s *Selector) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { ctx = interrupt.ContextWithIsExternalConnection(ctx) - return s.selected.NewPacketConnection(ctx, conn, metadata) + if legacyHandler, ok := s.selected.(adapter.PacketConnectionHandler); ok { + return legacyHandler.NewPacketConnection(ctx, conn, metadata) + } else { + return NewPacketConnection(ctx, s.selected, conn, metadata) + } } func RealTag(detour adapter.Outbound) string { diff --git a/outbound/shadowsocks.go b/outbound/shadowsocks.go index 9f8c1cbd..15354274 100644 --- a/outbound/shadowsocks.go +++ b/outbound/shadowsocks.go @@ -125,14 +125,6 @@ func (h *Shadowsocks) ListenPacket(ctx context.Context, destination M.Socksaddr) } } -func (h *Shadowsocks) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { - return NewConnection(ctx, h, conn, metadata) -} - -func (h *Shadowsocks) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { - return NewPacketConnection(ctx, h, conn, metadata) -} - func (h *Shadowsocks) InterfaceUpdated() { if h.multiplexDialer != nil { h.multiplexDialer.Reset() diff --git a/outbound/shadowtls.go b/outbound/shadowtls.go index 38de8c0b..ff1b9d6c 100644 --- a/outbound/shadowtls.go +++ b/outbound/shadowtls.go @@ -106,11 +106,3 @@ func (h *ShadowTLS) DialContext(ctx context.Context, network string, destination func (h *ShadowTLS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { return nil, os.ErrInvalid } - -func (h *ShadowTLS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { - return NewConnection(ctx, h, conn, metadata) -} - -func (h *ShadowTLS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { - return os.ErrInvalid -} diff --git a/outbound/socks.go b/outbound/socks.go index f4757467..575d6eb3 100644 --- a/outbound/socks.go +++ b/outbound/socks.go @@ -113,6 +113,8 @@ func (h *Socks) ListenPacket(ctx context.Context, destination M.Socksaddr) (net. return h.client.ListenPacket(ctx, destination) } +// TODO +// Deprecated func (h *Socks) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { if h.resolve { return NewDirectConnection(ctx, h.router, h, conn, metadata, dns.DomainStrategyUseIPv4) @@ -121,6 +123,8 @@ func (h *Socks) NewConnection(ctx context.Context, conn net.Conn, metadata adapt } } +// TODO +// Deprecated func (h *Socks) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { if h.resolve { return NewDirectPacketConnection(ctx, h.router, h, conn, metadata, dns.DomainStrategyUseIPv4) diff --git a/outbound/ssh.go b/outbound/ssh.go index ce62b004..28abe9a5 100644 --- a/outbound/ssh.go +++ b/outbound/ssh.go @@ -199,11 +199,3 @@ func (s *SSH) DialContext(ctx context.Context, network string, destination M.Soc func (s *SSH) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { return nil, os.ErrInvalid } - -func (s *SSH) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { - return NewConnection(ctx, s, conn, metadata) -} - -func (s *SSH) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { - return os.ErrInvalid -} diff --git a/outbound/tor.go b/outbound/tor.go index 8ae73a66..ccc0c0cf 100644 --- a/outbound/tor.go +++ b/outbound/tor.go @@ -211,11 +211,3 @@ func (t *Tor) DialContext(ctx context.Context, network string, destination M.Soc func (t *Tor) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { return nil, os.ErrInvalid } - -func (t *Tor) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { - return NewConnection(ctx, t, conn, metadata) -} - -func (t *Tor) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { - return os.ErrInvalid -} diff --git a/outbound/trojan.go b/outbound/trojan.go index bde251d0..ee0b2a4b 100644 --- a/outbound/trojan.go +++ b/outbound/trojan.go @@ -99,14 +99,6 @@ func (h *Trojan) ListenPacket(ctx context.Context, destination M.Socksaddr) (net } } -func (h *Trojan) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { - return NewConnection(ctx, h, conn, metadata) -} - -func (h *Trojan) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { - return NewPacketConnection(ctx, h, conn, metadata) -} - func (h *Trojan) InterfaceUpdated() { if h.transport != nil { h.transport.Close() diff --git a/outbound/tuic.go b/outbound/tuic.go index c0983323..aaf998b1 100644 --- a/outbound/tuic.go +++ b/outbound/tuic.go @@ -136,14 +136,6 @@ func (h *TUIC) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.P } } -func (h *TUIC) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { - return NewConnection(ctx, h, conn, metadata) -} - -func (h *TUIC) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { - return NewPacketConnection(ctx, h, conn, metadata) -} - func (h *TUIC) InterfaceUpdated() { _ = h.client.CloseWithError(E.New("network changed")) } diff --git a/outbound/urltest.go b/outbound/urltest.go index c6e38ec5..564a0ddc 100644 --- a/outbound/urltest.go +++ b/outbound/urltest.go @@ -167,11 +167,15 @@ func (s *URLTest) ListenPacket(ctx context.Context, destination M.Socksaddr) (ne return nil, err } +// TODO +// Deprecated func (s *URLTest) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { ctx = interrupt.ContextWithIsExternalConnection(ctx) return NewConnection(ctx, s, conn, metadata) } +// TODO +// Deprecated func (s *URLTest) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { ctx = interrupt.ContextWithIsExternalConnection(ctx) return NewPacketConnection(ctx, s, conn, metadata) diff --git a/outbound/vless.go b/outbound/vless.go index a81678f0..536a1e8f 100644 --- a/outbound/vless.go +++ b/outbound/vless.go @@ -118,14 +118,6 @@ func (h *VLESS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net. } } -func (h *VLESS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { - return NewConnection(ctx, h, conn, metadata) -} - -func (h *VLESS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { - return NewPacketConnection(ctx, h, conn, metadata) -} - func (h *VLESS) InterfaceUpdated() { if h.transport != nil { h.transport.Close() diff --git a/outbound/vmess.go b/outbound/vmess.go index 3149729c..126d2fd0 100644 --- a/outbound/vmess.go +++ b/outbound/vmess.go @@ -146,14 +146,6 @@ func (h *VMess) ListenPacket(ctx context.Context, destination M.Socksaddr) (net. } } -func (h *VMess) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { - return NewConnection(ctx, h, conn, metadata) -} - -func (h *VMess) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { - return NewPacketConnection(ctx, h, conn, metadata) -} - type vmessDialer VMess func (h *vmessDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { diff --git a/outbound/wireguard.go b/outbound/wireguard.go index 3ae3f63b..8eb043f4 100644 --- a/outbound/wireguard.go +++ b/outbound/wireguard.go @@ -234,10 +234,14 @@ func (w *WireGuard) ListenPacket(ctx context.Context, destination M.Socksaddr) ( return w.tunDevice.ListenPacket(ctx, destination) } +// TODO +// Deprecated func (w *WireGuard) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { return NewDirectConnection(ctx, w.router, w, conn, metadata, dns.DomainStrategyAsIS) } +// TODO +// Deprecated func (w *WireGuard) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { return NewDirectPacketConnection(ctx, w.router, w, conn, metadata, dns.DomainStrategyAsIS) } diff --git a/route/router_geo_resources.go b/route/geo_resources.go similarity index 98% rename from route/router_geo_resources.go rename to route/geo_resources.go index d4f65cc8..91c06796 100644 --- a/route/router_geo_resources.go +++ b/route/geo_resources.go @@ -13,6 +13,7 @@ import ( "github.com/sagernet/sing-box/common/geosite" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/experimental/deprecated" + R "github.com/sagernet/sing-box/route/rule" E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" "github.com/sagernet/sing/common/rw" @@ -32,7 +33,7 @@ func (r *Router) LoadGeosite(code string) (adapter.Rule, error) { if err != nil { return nil, err } - rule, err = NewDefaultRule(r.ctx, r, nil, geosite.Compile(items)) + rule, err = R.NewDefaultRule(r.ctx, r, nil, geosite.Compile(items)) if err != nil { return nil, err } diff --git a/route/route.go b/route/route.go new file mode 100644 index 00000000..86d4d95c --- /dev/null +++ b/route/route.go @@ -0,0 +1,583 @@ +package route + +import ( + "context" + "errors" + "net" + "net/netip" + "os" + "os/user" + "strings" + "syscall" + "time" + + "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/common/conntrack" + "github.com/sagernet/sing-box/common/process" + "github.com/sagernet/sing-box/common/sniff" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing-box/outbound" + "github.com/sagernet/sing-box/route/rule" + "github.com/sagernet/sing-dns" + "github.com/sagernet/sing-mux" + "github.com/sagernet/sing-tun" + "github.com/sagernet/sing-vmess" + "github.com/sagernet/sing/common" + "github.com/sagernet/sing/common/buf" + "github.com/sagernet/sing/common/bufio" + "github.com/sagernet/sing/common/bufio/deadline" + E "github.com/sagernet/sing/common/exceptions" + F "github.com/sagernet/sing/common/format" + M "github.com/sagernet/sing/common/metadata" + N "github.com/sagernet/sing/common/network" + "github.com/sagernet/sing/common/uot" +) + +// Deprecated: use RouteConnectionEx instead. +func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { + return r.routeConnection(ctx, conn, metadata, nil) +} + +func (r *Router) RouteConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) { + err := r.routeConnection(ctx, conn, metadata, onClose) + if err != nil { + N.CloseOnHandshakeFailure(conn, onClose, err) + if E.IsClosedOrCanceled(err) { + r.logger.DebugContext(ctx, "connection closed: ", err) + } else { + r.logger.ErrorContext(ctx, err) + } + } +} + +func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) error { + if r.pauseManager.IsDevicePaused() { + return E.New("reject connection to ", metadata.Destination, " while device paused") + } + + if metadata.InboundDetour != "" { + if metadata.LastInbound == metadata.InboundDetour { + return E.New("routing loop on detour: ", metadata.InboundDetour) + } + detour := r.inboundByTag[metadata.InboundDetour] + if detour == nil { + return E.New("inbound detour not found: ", metadata.InboundDetour) + } + injectable, isInjectable := detour.(adapter.TCPInjectableInbound) + if !isInjectable { + return E.New("inbound detour is not TCP injectable: ", metadata.InboundDetour) + } + metadata.LastInbound = metadata.Inbound + metadata.Inbound = metadata.InboundDetour + metadata.InboundDetour = "" + injectable.NewConnectionEx(ctx, conn, metadata, onClose) + return nil + } + conntrack.KillerCheck() + metadata.Network = N.NetworkTCP + switch metadata.Destination.Fqdn { + case mux.Destination.Fqdn: + return E.New("global multiplex is deprecated since sing-box v1.7.0, enable multiplex in inbound options instead.") + case vmess.MuxDestination.Fqdn: + return E.New("global multiplex (v2ray legacy) not supported since sing-box v1.7.0.") + case uot.MagicAddress: + return E.New("global UoT not supported since sing-box v1.7.0.") + case uot.LegacyMagicAddress: + return E.New("global UoT (legacy) not supported since sing-box v1.7.0.") + } + if deadline.NeedAdditionalReadDeadline(conn) { + conn = deadline.NewConn(conn) + } + selectedRule, _, buffers, err := r.matchRule(ctx, &metadata, conn, nil, -1) + if err != nil { + return err + } + var selectedOutbound adapter.Outbound + var selectReturn bool + if selectedRule != nil { + switch action := selectedRule.Action().(type) { + case *rule.RuleActionRoute: + var loaded bool + selectedOutbound, loaded = r.Outbound(action.Outbound) + if !loaded { + buf.ReleaseMulti(buffers) + return E.New("outbound not found: ", action.Outbound) + } + case *rule.RuleActionReturn: + selectReturn = true + case *rule.RuleActionReject: + buf.ReleaseMulti(buffers) + var rejectErr error + switch action.Method { + case C.RuleActionRejectMethodDefault: + rejectErr = os.ErrClosed + case C.RuleActionRejectMethodPortUnreachable: + rejectErr = syscall.ECONNREFUSED + case C.RuleActionRejectMethodDrop: + rejectErr = tun.ErrDrop + } + N.CloseOnHandshakeFailure(conn, onClose, rejectErr) + return nil + } + } + if selectedRule == nil || selectReturn { + if r.defaultOutboundForConnection == nil { + buf.ReleaseMulti(buffers) + return E.New("missing default outbound with TCP support") + } + selectedOutbound = r.defaultOutboundForConnection + } + if !common.Contains(selectedOutbound.Network(), N.NetworkTCP) { + buf.ReleaseMulti(buffers) + return E.New("TCP is not supported by outbound: ", selectedOutbound.Tag()) + } + for _, buffer := range buffers { + conn = bufio.NewCachedConn(conn, buffer) + } + if r.clashServer != nil { + trackerConn, tracker := r.clashServer.RoutedConnection(ctx, conn, metadata, selectedRule) + defer tracker.Leave() + conn = trackerConn + } + if r.v2rayServer != nil { + if statsService := r.v2rayServer.StatsService(); statsService != nil { + conn = statsService.RoutedConnection(metadata.Inbound, selectedOutbound.Tag(), metadata.User, conn) + } + } + legacyOutbound, isLegacy := selectedOutbound.(adapter.ConnectionHandler) + if isLegacy { + err = legacyOutbound.NewConnection(ctx, conn, metadata) + if err != nil { + conn.Close() + if onClose != nil { + onClose(err) + } + return E.Cause(err, "outbound/", selectedOutbound.Type(), "[", selectedOutbound.Tag(), "]") + } else { + if onClose != nil { + onClose(nil) + } + } + return nil + } + // TODO + err = outbound.NewConnection(ctx, selectedOutbound, conn, metadata) + if err != nil { + conn.Close() + if onClose != nil { + onClose(err) + } + return E.Cause(err, "outbound/", selectedOutbound.Type(), "[", selectedOutbound.Tag(), "]") + } else { + if onClose != nil { + onClose(nil) + } + } + return nil +} + +func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { + err := r.routePacketConnection(ctx, conn, metadata, nil) + if err != nil { + conn.Close() + if E.IsClosedOrCanceled(err) { + r.logger.DebugContext(ctx, "connection closed: ", err) + } else { + r.logger.ErrorContext(ctx, err) + } + } + return nil +} + +func (r *Router) RoutePacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) { + err := r.routePacketConnection(ctx, conn, metadata, onClose) + if err != nil { + N.CloseOnHandshakeFailure(conn, onClose, err) + if E.IsClosedOrCanceled(err) { + r.logger.DebugContext(ctx, "connection closed: ", err) + } else { + r.logger.ErrorContext(ctx, err) + } + } else if onClose != nil { + onClose(nil) + } +} + +func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) error { + if r.pauseManager.IsDevicePaused() { + return E.New("reject packet connection to ", metadata.Destination, " while device paused") + } + if metadata.InboundDetour != "" { + if metadata.LastInbound == metadata.InboundDetour { + return E.New("routing loop on detour: ", metadata.InboundDetour) + } + detour := r.inboundByTag[metadata.InboundDetour] + if detour == nil { + return E.New("inbound detour not found: ", metadata.InboundDetour) + } + injectable, isInjectable := detour.(adapter.UDPInjectableInbound) + if !isInjectable { + return E.New("inbound detour is not UDP injectable: ", metadata.InboundDetour) + } + metadata.LastInbound = metadata.Inbound + metadata.Inbound = metadata.InboundDetour + metadata.InboundDetour = "" + injectable.NewPacketConnectionEx(ctx, conn, metadata, onClose) + return nil + } + conntrack.KillerCheck() + + // TODO: move to UoT + metadata.Network = N.NetworkUDP + + // Currently we don't have deadline usages for UDP connections + /*if deadline.NeedAdditionalReadDeadline(conn) { + conn = deadline.NewPacketConn(bufio.NewNetPacketConn(conn)) + }*/ + + selectedRule, _, buffers, err := r.matchRule(ctx, &metadata, nil, conn, -1) + if err != nil { + return err + } + var selectedOutbound adapter.Outbound + var selectReturn bool + if selectedRule != nil { + switch action := selectedRule.Action().(type) { + case *rule.RuleActionRoute: + var loaded bool + selectedOutbound, loaded = r.Outbound(action.Outbound) + if !loaded { + buf.ReleaseMulti(buffers) + return E.New("outbound not found: ", action.Outbound) + } + metadata.UDPDisableDomainUnmapping = action.UDPDisableDomainUnmapping + case *rule.RuleActionReturn: + selectReturn = true + case *rule.RuleActionReject: + buf.ReleaseMulti(buffers) + N.CloseOnHandshakeFailure(conn, onClose, syscall.ECONNREFUSED) + return nil + } + } + if selectedRule == nil || selectReturn { + if r.defaultOutboundForPacketConnection == nil { + buf.ReleaseMulti(buffers) + return E.New("missing default outbound with UDP support") + } + selectedOutbound = r.defaultOutboundForPacketConnection + } + if !common.Contains(selectedOutbound.Network(), N.NetworkUDP) { + buf.ReleaseMulti(buffers) + return E.New("UDP is not supported by outbound: ", selectedOutbound.Tag()) + } + for _, buffer := range buffers { + // TODO: check if metadata.Destination == packet destination + conn = bufio.NewCachedPacketConn(conn, buffer, metadata.Destination) + } + if r.clashServer != nil { + trackerConn, tracker := r.clashServer.RoutedPacketConnection(ctx, conn, metadata, selectedRule) + defer tracker.Leave() + conn = trackerConn + } + if r.v2rayServer != nil { + if statsService := r.v2rayServer.StatsService(); statsService != nil { + conn = statsService.RoutedPacketConnection(metadata.Inbound, selectedOutbound.Tag(), metadata.User, conn) + } + } + if metadata.FakeIP { + conn = bufio.NewNATPacketConn(bufio.NewNetPacketConn(conn), metadata.OriginDestination, metadata.Destination) + } + legacyOutbound, isLegacy := selectedOutbound.(adapter.PacketConnectionHandler) + if isLegacy { + err = legacyOutbound.NewPacketConnection(ctx, conn, metadata) + N.CloseOnHandshakeFailure(conn, onClose, err) + if err != nil { + return E.Cause(err, "outbound/", selectedOutbound.Type(), "[", selectedOutbound.Tag(), "]") + } + return nil + } + // TODO + err = outbound.NewPacketConnection(ctx, selectedOutbound, conn, metadata) + N.CloseOnHandshakeFailure(conn, onClose, err) + if err != nil { + return E.Cause(err, "outbound/", selectedOutbound.Type(), "[", selectedOutbound.Tag(), "]") + } + return nil +} + +func (r *Router) matchRule( + ctx context.Context, metadata *adapter.InboundContext, + inputConn net.Conn, inputPacketConn N.PacketConn, ruleIndex int, +) (selectedRule adapter.Rule, selectedRuleIndex int, buffers []*buf.Buffer, fatalErr error) { + if r.processSearcher != nil && metadata.ProcessInfo == nil { + var originDestination netip.AddrPort + if metadata.OriginDestination.IsValid() { + originDestination = metadata.OriginDestination.AddrPort() + } else if metadata.Destination.IsIP() { + originDestination = metadata.Destination.AddrPort() + } + processInfo, fErr := process.FindProcessInfo(r.processSearcher, ctx, metadata.Network, metadata.Source.AddrPort(), originDestination) + if fErr != nil { + r.logger.InfoContext(ctx, "failed to search process: ", fErr) + } else { + if processInfo.ProcessPath != "" { + r.logger.InfoContext(ctx, "found process path: ", processInfo.ProcessPath) + } else if processInfo.PackageName != "" { + r.logger.InfoContext(ctx, "found package name: ", processInfo.PackageName) + } else if processInfo.UserId != -1 { + if /*needUserName &&*/ true { + osUser, _ := user.LookupId(F.ToString(processInfo.UserId)) + if osUser != nil { + processInfo.User = osUser.Username + } + } + if processInfo.User != "" { + r.logger.InfoContext(ctx, "found user: ", processInfo.User) + } else { + r.logger.InfoContext(ctx, "found user id: ", processInfo.UserId) + } + } + metadata.ProcessInfo = processInfo + } + } + if r.fakeIPStore != nil && r.fakeIPStore.Contains(metadata.Destination.Addr) { + domain, loaded := r.fakeIPStore.Lookup(metadata.Destination.Addr) + if !loaded { + fatalErr = E.New("missing fakeip record, try to configure experimental.cache_file") + return + } + metadata.OriginDestination = metadata.Destination + metadata.Destination = M.Socksaddr{ + Fqdn: domain, + Port: metadata.Destination.Port, + } + metadata.FakeIP = true + r.logger.DebugContext(ctx, "found fakeip domain: ", domain) + } + if r.dnsReverseMapping != nil && metadata.Domain == "" { + domain, loaded := r.dnsReverseMapping.Query(metadata.Destination.Addr) + if loaded { + metadata.Domain = domain + r.logger.DebugContext(ctx, "found reserve mapped domain: ", metadata.Domain) + } + } + if metadata.Destination.IsIPv4() { + metadata.IPVersion = 4 + } else if metadata.Destination.IsIPv6() { + metadata.IPVersion = 6 + } + + //nolint:staticcheck + if metadata.InboundOptions != common.DefaultValue[option.InboundOptions]() { + if metadata.InboundOptions.SniffEnabled { + newBuffers, newErr := r.actionSniff(ctx, metadata, &rule.RuleActionSniff{ + OverrideDestination: metadata.InboundOptions.SniffOverrideDestination, + Timeout: time.Duration(metadata.InboundOptions.SniffTimeout), + }, inputConn, inputPacketConn) + if newErr != nil { + fatalErr = newErr + return + } + buffers = append(buffers, newBuffers...) + } + if dns.DomainStrategy(metadata.InboundOptions.DomainStrategy) != dns.DomainStrategyAsIS { + fatalErr = r.actionResolve(ctx, metadata, &rule.RuleActionResolve{ + Strategy: dns.DomainStrategy(metadata.InboundOptions.DomainStrategy), + }) + if fatalErr != nil { + return + } + } + if metadata.InboundOptions.UDPDisableDomainUnmapping { + metadata.UDPDisableDomainUnmapping = true + } + metadata.InboundOptions = option.InboundOptions{} + } + +match: + for ruleIndex < len(r.rules) { + rules := r.rules + if ruleIndex != -1 { + rules = rules[ruleIndex+1:] + } + var ( + currentRule adapter.Rule + currentRuleIndex int + matched bool + ) + for currentRuleIndex, currentRule = range rules { + if currentRule.Match(metadata) { + matched = true + break + } + } + if !matched { + break + } + r.logger.DebugContext(ctx, "match[", currentRuleIndex, "] ", currentRule, " => ", currentRule.Action()) + switch action := currentRule.Action().(type) { + case *rule.RuleActionSniff: + newBuffers, newErr := r.actionSniff(ctx, metadata, action, inputConn, inputPacketConn) + if newErr != nil { + fatalErr = newErr + return + } + buffers = append(buffers, newBuffers...) + case *rule.RuleActionResolve: + fatalErr = r.actionResolve(ctx, metadata, action) + if fatalErr != nil { + return + } + default: + selectedRule = currentRule + selectedRuleIndex = currentRuleIndex + break match + } + ruleIndex = currentRuleIndex + } + if metadata.Destination.Addr.IsUnspecified() { + newBuffers, newErr := r.actionSniff(ctx, metadata, &rule.RuleActionSniff{}, inputConn, inputPacketConn) + if newErr != nil { + fatalErr = newErr + return + } + buffers = append(buffers, newBuffers...) + } + return +} + +func (r *Router) actionSniff( + ctx context.Context, metadata *adapter.InboundContext, action *rule.RuleActionSniff, + inputConn net.Conn, inputPacketConn N.PacketConn, +) (buffers []*buf.Buffer, fatalErr error) { + if sniff.Skip(metadata) { + return + } else if inputConn != nil && len(action.StreamSniffers) > 0 { + buffer := buf.NewPacket() + err := sniff.PeekStream( + ctx, + metadata, + inputConn, + buffer, + action.Timeout, + action.StreamSniffers..., + ) + if err == nil { + //goland:noinspection GoDeprecation + if action.OverrideDestination && M.IsDomainName(metadata.Domain) { + metadata.Destination = M.Socksaddr{ + Fqdn: metadata.Domain, + Port: metadata.Destination.Port, + } + } + if metadata.Domain != "" && metadata.Client != "" { + r.logger.DebugContext(ctx, "sniffed protocol: ", metadata.Protocol, ", domain: ", metadata.Domain, ", client: ", metadata.Client) + } else if metadata.Domain != "" { + r.logger.DebugContext(ctx, "sniffed protocol: ", metadata.Protocol, ", domain: ", metadata.Domain) + } else { + r.logger.DebugContext(ctx, "sniffed protocol: ", metadata.Protocol) + } + } + if !buffer.IsEmpty() { + buffers = append(buffers, buffer) + } else { + buffer.Release() + } + } else if inputPacketConn != nil && len(action.PacketSniffers) > 0 { + for { + var ( + buffer = buf.NewPacket() + destination M.Socksaddr + done = make(chan struct{}) + err error + ) + go func() { + sniffTimeout := C.ReadPayloadTimeout + if action.Timeout > 0 { + sniffTimeout = action.Timeout + } + inputPacketConn.SetReadDeadline(time.Now().Add(sniffTimeout)) + destination, err = inputPacketConn.ReadPacket(buffer) + inputPacketConn.SetReadDeadline(time.Time{}) + close(done) + }() + select { + case <-done: + case <-ctx.Done(): + inputPacketConn.Close() + fatalErr = ctx.Err() + return + } + if err != nil { + buffer.Release() + if !errors.Is(err, os.ErrDeadlineExceeded) { + fatalErr = err + return + } + } else { + // TODO: maybe always override destination + if metadata.Destination.Addr.IsUnspecified() { + metadata.Destination = destination + } + if len(buffers) > 0 { + err = sniff.PeekPacket( + ctx, + metadata, + buffer.Bytes(), + sniff.QUICClientHello, + ) + } else { + err = sniff.PeekPacket( + ctx, metadata, + buffer.Bytes(), + action.PacketSniffers..., + ) + } + buffers = append(buffers, buffer) + if E.IsMulti(err, sniff.ErrClientHelloFragmented) && len(buffers) == 0 { + r.logger.DebugContext(ctx, "attempt to sniff fragmented QUIC client hello") + continue + } + if metadata.Protocol != "" { + //goland:noinspection GoDeprecation + if action.OverrideDestination && M.IsDomainName(metadata.Domain) { + metadata.Destination = M.Socksaddr{ + Fqdn: metadata.Domain, + Port: metadata.Destination.Port, + } + } + if metadata.Domain != "" && metadata.Client != "" { + r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol, ", domain: ", metadata.Domain, ", client: ", metadata.Client) + } else if metadata.Domain != "" { + r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol, ", domain: ", metadata.Domain) + } else if metadata.Client != "" { + r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol, ", client: ", metadata.Client) + } else { + r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol) + } + } + } + break + } + } + return +} + +func (r *Router) actionResolve(ctx context.Context, metadata *adapter.InboundContext, action *rule.RuleActionResolve) error { + if metadata.Destination.IsFqdn() { + // TODO: check if WithContext is necessary + addresses, err := r.Lookup(adapter.WithContext(ctx, metadata), metadata.Destination.Fqdn, action.Strategy) + if err != nil { + return err + } + metadata.DestinationAddresses = addresses + r.dnsLogger.DebugContext(ctx, "resolved [", strings.Join(F.MapToString(metadata.DestinationAddresses), " "), "]") + if metadata.Destination.IsIPv4() { + metadata.IPVersion = 4 + } else if metadata.Destination.IsIPv6() { + metadata.IPVersion = 6 + } + } + return nil +} diff --git a/route/router_dns.go b/route/route_dns.go similarity index 75% rename from route/router_dns.go rename to route/route_dns.go index ead8c289..43eb61e6 100644 --- a/route/router_dns.go +++ b/route/route_dns.go @@ -8,6 +8,7 @@ import ( "time" "github.com/sagernet/sing-box/adapter" + R "github.com/sagernet/sing-box/route/rule" "github.com/sagernet/sing-dns" "github.com/sagernet/sing/common/cache" E "github.com/sagernet/sing/common/exceptions" @@ -36,15 +37,16 @@ func (m *DNSReverseMapping) Query(address netip.Addr) (string, bool) { return domain, loaded } -func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, index int, isAddressQuery bool) (context.Context, dns.Transport, dns.DomainStrategy, adapter.DNSRule, int) { +func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, ruleIndex int, isAddressQuery bool) (dns.Transport, dns.QueryOptions, adapter.DNSRule, int) { metadata := adapter.ContextFrom(ctx) if metadata == nil { panic("no context") } - if index < len(r.dnsRules) { + var options dns.QueryOptions + if ruleIndex < len(r.dnsRules) { dnsRules := r.dnsRules - if index != -1 { - dnsRules = dnsRules[index+1:] + if ruleIndex != -1 { + dnsRules = dnsRules[ruleIndex+1:] } for currentRuleIndex, rule := range dnsRules { if rule.WithAddressLimit() && !isAddressQuery { @@ -52,43 +54,42 @@ func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, index int, isAd } metadata.ResetRuleCache() if rule.Match(metadata) { - detour := rule.Outbound() - transport, loaded := r.transportMap[detour] - if !loaded { - r.dnsLogger.ErrorContext(ctx, "transport not found: ", detour) - continue + displayRuleIndex := currentRuleIndex + if displayRuleIndex != -1 { + displayRuleIndex += displayRuleIndex + 1 } - _, isFakeIP := transport.(adapter.FakeIPTransport) - if isFakeIP && !allowFakeIP { - continue - } - ruleIndex := currentRuleIndex - if index != -1 { - ruleIndex += index + 1 - } - r.dnsLogger.DebugContext(ctx, "match[", ruleIndex, "] ", rule.String(), " => ", detour) - if isFakeIP || rule.DisableCache() { - ctx = dns.ContextWithDisableCache(ctx, true) - } - if rewriteTTL := rule.RewriteTTL(); rewriteTTL != nil { - ctx = dns.ContextWithRewriteTTL(ctx, *rewriteTTL) - } - if clientSubnet := rule.ClientSubnet(); clientSubnet != nil { - ctx = dns.ContextWithClientSubnet(ctx, *clientSubnet) - } - if domainStrategy, dsLoaded := r.transportDomainStrategy[transport]; dsLoaded { - return ctx, transport, domainStrategy, rule, ruleIndex + if routeAction, isRoute := rule.Action().(*R.RuleActionDNSRoute); isRoute { + transport, loaded := r.transportMap[routeAction.Server] + if !loaded { + r.dnsLogger.ErrorContext(ctx, "transport not found: ", routeAction.Server) + continue + } + _, isFakeIP := transport.(adapter.FakeIPTransport) + if isFakeIP && !allowFakeIP { + continue + } + options.DisableCache = isFakeIP || routeAction.DisableCache + options.RewriteTTL = routeAction.RewriteTTL + options.ClientSubnet = routeAction.ClientSubnet + if domainStrategy, dsLoaded := r.transportDomainStrategy[transport]; dsLoaded { + options.Strategy = domainStrategy + } else { + options.Strategy = r.defaultDomainStrategy + } + r.dnsLogger.DebugContext(ctx, "match[", displayRuleIndex, "] ", rule.String(), " => ", rule.Action()) + return transport, options, rule, currentRuleIndex } else { - return ctx, transport, r.defaultDomainStrategy, rule, ruleIndex + return nil, options, rule, currentRuleIndex } } } } if domainStrategy, dsLoaded := r.transportDomainStrategy[r.defaultTransport]; dsLoaded { - return ctx, r.defaultTransport, domainStrategy, nil, -1 + options.Strategy = domainStrategy } else { - return ctx, r.defaultTransport, r.defaultDomainStrategy, nil, -1 + options.Strategy = r.defaultDomainStrategy } + return r.defaultTransport, options, nil, -1 } func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { @@ -117,21 +118,18 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er metadata.Domain = fqdnToDomain(message.Question[0].Name) } var ( - strategy dns.DomainStrategy + options dns.QueryOptions rule adapter.DNSRule ruleIndex int ) ruleIndex = -1 for { - var ( - dnsCtx context.Context - addressLimit bool - ) - dnsCtx, transport, strategy, rule, ruleIndex = r.matchDNS(ctx, true, ruleIndex, isAddressQuery(message)) - dnsCtx = adapter.OverrideContext(dnsCtx) + dnsCtx := adapter.OverrideContext(ctx) + var addressLimit bool + transport, options, rule, ruleIndex = r.matchDNS(ctx, true, ruleIndex, isAddressQuery(message)) if rule != nil && rule.WithAddressLimit() { addressLimit = true - response, err = r.dnsClient.ExchangeWithResponseCheck(dnsCtx, transport, message, strategy, func(response *mDNS.Msg) bool { + response, err = r.dnsClient.ExchangeWithResponseCheck(dnsCtx, transport, message, options, func(response *mDNS.Msg) bool { addresses, addrErr := dns.MessageToAddresses(response) if addrErr != nil { return false @@ -141,7 +139,7 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er }) } else { addressLimit = false - response, err = r.dnsClient.Exchange(dnsCtx, transport, message, strategy) + response, err = r.dnsClient.Exchange(dnsCtx, transport, message, options) } var rejected bool if err != nil { @@ -199,31 +197,28 @@ func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainS metadata.Destination = M.Socksaddr{} metadata.Domain = domain var ( - transport dns.Transport - transportStrategy dns.DomainStrategy - rule adapter.DNSRule - ruleIndex int + transport dns.Transport + options dns.QueryOptions + rule adapter.DNSRule + ruleIndex int ) ruleIndex = -1 for { - var ( - dnsCtx context.Context - addressLimit bool - ) - dnsCtx, transport, transportStrategy, rule, ruleIndex = r.matchDNS(ctx, false, ruleIndex, true) - dnsCtx = adapter.OverrideContext(dnsCtx) - if strategy == dns.DomainStrategyAsIS { - strategy = transportStrategy + dnsCtx := adapter.OverrideContext(ctx) + var addressLimit bool + transport, options, rule, ruleIndex = r.matchDNS(ctx, false, ruleIndex, true) + if strategy != dns.DomainStrategyAsIS { + options.Strategy = strategy } if rule != nil && rule.WithAddressLimit() { addressLimit = true - responseAddrs, err = r.dnsClient.LookupWithResponseCheck(dnsCtx, transport, domain, strategy, func(responseAddrs []netip.Addr) bool { + responseAddrs, err = r.dnsClient.LookupWithResponseCheck(dnsCtx, transport, domain, options, func(responseAddrs []netip.Addr) bool { metadata.DestinationAddresses = responseAddrs return rule.MatchAddressLimit(metadata) }) } else { addressLimit = false - responseAddrs, err = r.dnsClient.Lookup(dnsCtx, transport, domain, strategy) + responseAddrs, err = r.dnsClient.Lookup(dnsCtx, transport, domain, options) } if err != nil { if errors.Is(err, dns.ErrResponseRejectedCached) { diff --git a/route/router.go b/route/router.go index 1fc9aa77..6308127f 100644 --- a/route/router.go +++ b/route/router.go @@ -3,11 +3,9 @@ package route import ( "context" "errors" - "net" "net/netip" "net/url" "os" - "os/user" "runtime" "strings" "syscall" @@ -19,22 +17,16 @@ import ( "github.com/sagernet/sing-box/common/geoip" "github.com/sagernet/sing-box/common/geosite" "github.com/sagernet/sing-box/common/process" - "github.com/sagernet/sing-box/common/sniff" "github.com/sagernet/sing-box/common/taskmonitor" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/experimental/libbox/platform" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" - "github.com/sagernet/sing-box/outbound" + R "github.com/sagernet/sing-box/route/rule" "github.com/sagernet/sing-box/transport/fakeip" "github.com/sagernet/sing-dns" - "github.com/sagernet/sing-mux" "github.com/sagernet/sing-tun" - "github.com/sagernet/sing-vmess" "github.com/sagernet/sing/common" - "github.com/sagernet/sing/common/buf" - "github.com/sagernet/sing/common/bufio" - "github.com/sagernet/sing/common/bufio/deadline" "github.com/sagernet/sing/common/control" E "github.com/sagernet/sing/common/exceptions" F "github.com/sagernet/sing/common/format" @@ -42,7 +34,6 @@ import ( N "github.com/sagernet/sing/common/network" "github.com/sagernet/sing/common/ntp" "github.com/sagernet/sing/common/task" - "github.com/sagernet/sing/common/uot" "github.com/sagernet/sing/common/winpowrprof" "github.com/sagernet/sing/service" "github.com/sagernet/sing/service/pause" @@ -154,14 +145,14 @@ func NewRouter( Logger: router.dnsLogger, }) for i, ruleOptions := range options.Rules { - routeRule, err := NewRule(ctx, router, router.logger, ruleOptions, true) + routeRule, err := R.NewRule(ctx, router, router.logger, ruleOptions, true) if err != nil { return nil, E.Cause(err, "parse rule[", i, "]") } router.rules = append(router.rules, routeRule) } for i, dnsRuleOptions := range dnsOptions.Rules { - dnsRule, err := NewDNSRule(ctx, router, router.logger, dnsRuleOptions, true) + dnsRule, err := R.NewDNSRule(ctx, router, router.logger, dnsRuleOptions, true) if err != nil { return nil, E.Cause(err, "parse dns rule[", i, "]") } @@ -171,7 +162,7 @@ func NewRouter( if _, exists := router.ruleSetMap[ruleSetOptions.Tag]; exists { return nil, E.New("duplicate rule-set tag: ", ruleSetOptions.Tag) } - ruleSet, err := NewRuleSet(ctx, router, router.logger, ruleSetOptions) + ruleSet, err := R.NewRuleSet(ctx, router, router.logger, ruleSetOptions) if err != nil { return nil, E.Cause(err, "parse rule-set[", i, "]") } @@ -440,8 +431,12 @@ func (r *Router) Initialize(inbounds []adapter.Inbound, outbounds []adapter.Outb r.defaultOutboundForPacketConnection = defaultOutboundForPacketConnection r.outboundByTag = outboundByTag for i, rule := range r.rules { - if _, loaded := outboundByTag[rule.Outbound()]; !loaded { - return E.New("outbound not found for rule[", i, "]: ", rule.Outbound()) + routeAction, isRoute := rule.Action().(*R.RuleActionRoute) + if !isRoute { + continue + } + if _, loaded := outboundByTag[routeAction.Outbound]; !loaded { + return E.New("outbound not found for rule[", i, "]: ", routeAction.Outbound) } } return nil @@ -807,375 +802,6 @@ func (r *Router) NeedWIFIState() bool { return r.needWIFIState } -func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { - if r.pauseManager.IsDevicePaused() { - return E.New("reject connection to ", metadata.Destination, " while device paused") - } - - if metadata.InboundDetour != "" { - if metadata.LastInbound == metadata.InboundDetour { - return E.New("routing loop on detour: ", metadata.InboundDetour) - } - detour := r.inboundByTag[metadata.InboundDetour] - if detour == nil { - return E.New("inbound detour not found: ", metadata.InboundDetour) - } - injectable, isInjectable := detour.(adapter.InjectableInbound) - if !isInjectable { - return E.New("inbound detour is not injectable: ", metadata.InboundDetour) - } - if !common.Contains(injectable.Network(), N.NetworkTCP) { - return E.New("inject: TCP unsupported") - } - metadata.LastInbound = metadata.Inbound - metadata.Inbound = metadata.InboundDetour - metadata.InboundDetour = "" - err := injectable.NewConnection(ctx, conn, metadata) - if err != nil { - return E.Cause(err, "inject ", detour.Tag()) - } - return nil - } - conntrack.KillerCheck() - metadata.Network = N.NetworkTCP - switch metadata.Destination.Fqdn { - case mux.Destination.Fqdn: - return E.New("global multiplex is deprecated since sing-box v1.7.0, enable multiplex in inbound options instead.") - case vmess.MuxDestination.Fqdn: - return E.New("global multiplex (v2ray legacy) not supported since sing-box v1.7.0.") - case uot.MagicAddress: - return E.New("global UoT not supported since sing-box v1.7.0.") - case uot.LegacyMagicAddress: - return E.New("global UoT (legacy) not supported since sing-box v1.7.0.") - } - - if r.fakeIPStore != nil && r.fakeIPStore.Contains(metadata.Destination.Addr) { - domain, loaded := r.fakeIPStore.Lookup(metadata.Destination.Addr) - if !loaded { - return E.New("missing fakeip context") - } - metadata.OriginDestination = metadata.Destination - metadata.Destination = M.Socksaddr{ - Fqdn: domain, - Port: metadata.Destination.Port, - } - metadata.FakeIP = true - r.logger.DebugContext(ctx, "found fakeip domain: ", domain) - } - - if deadline.NeedAdditionalReadDeadline(conn) { - conn = deadline.NewConn(conn) - } - - if metadata.InboundOptions.SniffEnabled && !sniff.Skip(metadata) { - buffer := buf.NewPacket() - err := sniff.PeekStream( - ctx, - &metadata, - conn, - buffer, - time.Duration(metadata.InboundOptions.SniffTimeout), - sniff.TLSClientHello, - sniff.HTTPHost, - sniff.StreamDomainNameQuery, - sniff.SSH, - sniff.BitTorrent, - ) - if err == nil { - if metadata.InboundOptions.SniffOverrideDestination && M.IsDomainName(metadata.Domain) { - metadata.Destination = M.Socksaddr{ - Fqdn: metadata.Domain, - Port: metadata.Destination.Port, - } - } - if metadata.Domain != "" { - r.logger.DebugContext(ctx, "sniffed protocol: ", metadata.Protocol, ", domain: ", metadata.Domain) - } else { - r.logger.DebugContext(ctx, "sniffed protocol: ", metadata.Protocol) - } - } - if !buffer.IsEmpty() { - conn = bufio.NewCachedConn(conn, buffer) - } else { - buffer.Release() - } - } - - if r.dnsReverseMapping != nil && metadata.Domain == "" { - domain, loaded := r.dnsReverseMapping.Query(metadata.Destination.Addr) - if loaded { - metadata.Domain = domain - r.logger.DebugContext(ctx, "found reserve mapped domain: ", metadata.Domain) - } - } - - if metadata.Destination.IsFqdn() && dns.DomainStrategy(metadata.InboundOptions.DomainStrategy) != dns.DomainStrategyAsIS { - addresses, err := r.Lookup(adapter.WithContext(ctx, &metadata), metadata.Destination.Fqdn, dns.DomainStrategy(metadata.InboundOptions.DomainStrategy)) - if err != nil { - return err - } - metadata.DestinationAddresses = addresses - r.dnsLogger.DebugContext(ctx, "resolved [", strings.Join(F.MapToString(metadata.DestinationAddresses), " "), "]") - } - if metadata.Destination.IsIPv4() { - metadata.IPVersion = 4 - } else if metadata.Destination.IsIPv6() { - metadata.IPVersion = 6 - } - ctx, matchedRule, detour, err := r.match(ctx, &metadata, r.defaultOutboundForConnection) - if err != nil { - return err - } - if !common.Contains(detour.Network(), N.NetworkTCP) { - return E.New("missing supported outbound, closing connection") - } - if r.clashServer != nil { - trackerConn, tracker := r.clashServer.RoutedConnection(ctx, conn, metadata, matchedRule) - defer tracker.Leave() - conn = trackerConn - } - if r.v2rayServer != nil { - if statsService := r.v2rayServer.StatsService(); statsService != nil { - conn = statsService.RoutedConnection(metadata.Inbound, detour.Tag(), metadata.User, conn) - } - } - return detour.NewConnection(ctx, conn, metadata) -} - -func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { - if r.pauseManager.IsDevicePaused() { - return E.New("reject packet connection to ", metadata.Destination, " while device paused") - } - if metadata.InboundDetour != "" { - if metadata.LastInbound == metadata.InboundDetour { - return E.New("routing loop on detour: ", metadata.InboundDetour) - } - detour := r.inboundByTag[metadata.InboundDetour] - if detour == nil { - return E.New("inbound detour not found: ", metadata.InboundDetour) - } - injectable, isInjectable := detour.(adapter.InjectableInbound) - if !isInjectable { - return E.New("inbound detour is not injectable: ", metadata.InboundDetour) - } - if !common.Contains(injectable.Network(), N.NetworkUDP) { - return E.New("inject: UDP unsupported") - } - metadata.LastInbound = metadata.Inbound - metadata.Inbound = metadata.InboundDetour - metadata.InboundDetour = "" - err := injectable.NewPacketConnection(ctx, conn, metadata) - if err != nil { - return E.Cause(err, "inject ", detour.Tag()) - } - return nil - } - conntrack.KillerCheck() - metadata.Network = N.NetworkUDP - - if r.fakeIPStore != nil && r.fakeIPStore.Contains(metadata.Destination.Addr) { - domain, loaded := r.fakeIPStore.Lookup(metadata.Destination.Addr) - if !loaded { - return E.New("missing fakeip context") - } - metadata.OriginDestination = metadata.Destination - metadata.Destination = M.Socksaddr{ - Fqdn: domain, - Port: metadata.Destination.Port, - } - metadata.FakeIP = true - r.logger.DebugContext(ctx, "found fakeip domain: ", domain) - } - - // Currently we don't have deadline usages for UDP connections - /*if deadline.NeedAdditionalReadDeadline(conn) { - conn = deadline.NewPacketConn(bufio.NewNetPacketConn(conn)) - }*/ - - if metadata.InboundOptions.SniffEnabled || metadata.Destination.Addr.IsUnspecified() { - var bufferList []*buf.Buffer - for { - var ( - buffer = buf.NewPacket() - destination M.Socksaddr - done = make(chan struct{}) - err error - ) - go func() { - sniffTimeout := C.ReadPayloadTimeout - if metadata.InboundOptions.SniffTimeout > 0 { - sniffTimeout = time.Duration(metadata.InboundOptions.SniffTimeout) - } - conn.SetReadDeadline(time.Now().Add(sniffTimeout)) - destination, err = conn.ReadPacket(buffer) - conn.SetReadDeadline(time.Time{}) - close(done) - }() - select { - case <-done: - case <-ctx.Done(): - conn.Close() - return ctx.Err() - } - if err != nil { - buffer.Release() - if !errors.Is(err, os.ErrDeadlineExceeded) { - return err - } - } else { - if metadata.Destination.Addr.IsUnspecified() { - metadata.Destination = destination - } - if metadata.InboundOptions.SniffEnabled { - if len(bufferList) > 0 { - err = sniff.PeekPacket( - ctx, - &metadata, - buffer.Bytes(), - sniff.QUICClientHello, - ) - } else { - err = sniff.PeekPacket( - ctx, &metadata, - buffer.Bytes(), - sniff.DomainNameQuery, - sniff.QUICClientHello, - sniff.STUNMessage, - sniff.UTP, - sniff.UDPTracker, - sniff.DTLSRecord) - } - if E.IsMulti(err, sniff.ErrClientHelloFragmented) && len(bufferList) == 0 { - bufferList = append(bufferList, buffer) - r.logger.DebugContext(ctx, "attempt to sniff fragmented QUIC client hello") - continue - } - if metadata.Protocol != "" { - if metadata.InboundOptions.SniffOverrideDestination && M.IsDomainName(metadata.Domain) { - metadata.Destination = M.Socksaddr{ - Fqdn: metadata.Domain, - Port: metadata.Destination.Port, - } - } - if metadata.Domain != "" && metadata.Client != "" { - r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol, ", domain: ", metadata.Domain, ", client: ", metadata.Client) - } else if metadata.Domain != "" { - r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol, ", domain: ", metadata.Domain) - } else if metadata.Client != "" { - r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol, ", client: ", metadata.Client) - } else { - r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol) - } - } - } - conn = bufio.NewCachedPacketConn(conn, buffer, destination) - } - for _, cachedBuffer := range common.Reverse(bufferList) { - conn = bufio.NewCachedPacketConn(conn, cachedBuffer, destination) - } - break - } - } - if r.dnsReverseMapping != nil && metadata.Domain == "" { - domain, loaded := r.dnsReverseMapping.Query(metadata.Destination.Addr) - if loaded { - metadata.Domain = domain - r.logger.DebugContext(ctx, "found reserve mapped domain: ", metadata.Domain) - } - } - if metadata.Destination.IsFqdn() && dns.DomainStrategy(metadata.InboundOptions.DomainStrategy) != dns.DomainStrategyAsIS { - addresses, err := r.Lookup(adapter.WithContext(ctx, &metadata), metadata.Destination.Fqdn, dns.DomainStrategy(metadata.InboundOptions.DomainStrategy)) - if err != nil { - return err - } - metadata.DestinationAddresses = addresses - r.dnsLogger.DebugContext(ctx, "resolved [", strings.Join(F.MapToString(metadata.DestinationAddresses), " "), "]") - } - if metadata.Destination.IsIPv4() { - metadata.IPVersion = 4 - } else if metadata.Destination.IsIPv6() { - metadata.IPVersion = 6 - } - ctx, matchedRule, detour, err := r.match(ctx, &metadata, r.defaultOutboundForPacketConnection) - if err != nil { - return err - } - if !common.Contains(detour.Network(), N.NetworkUDP) { - return E.New("missing supported outbound, closing packet connection") - } - if r.clashServer != nil { - trackerConn, tracker := r.clashServer.RoutedPacketConnection(ctx, conn, metadata, matchedRule) - defer tracker.Leave() - conn = trackerConn - } - if r.v2rayServer != nil { - if statsService := r.v2rayServer.StatsService(); statsService != nil { - conn = statsService.RoutedPacketConnection(metadata.Inbound, detour.Tag(), metadata.User, conn) - } - } - if metadata.FakeIP { - conn = bufio.NewNATPacketConn(bufio.NewNetPacketConn(conn), metadata.OriginDestination, metadata.Destination) - } - return detour.NewPacketConnection(ctx, conn, metadata) -} - -func (r *Router) match(ctx context.Context, metadata *adapter.InboundContext, defaultOutbound adapter.Outbound) (context.Context, adapter.Rule, adapter.Outbound, error) { - matchRule, matchOutbound := r.match0(ctx, metadata, defaultOutbound) - if contextOutbound, loaded := outbound.TagFromContext(ctx); loaded { - if contextOutbound == matchOutbound.Tag() { - return nil, nil, nil, E.New("connection loopback in outbound/", matchOutbound.Type(), "[", matchOutbound.Tag(), "]") - } - } - ctx = outbound.ContextWithTag(ctx, matchOutbound.Tag()) - return ctx, matchRule, matchOutbound, nil -} - -func (r *Router) match0(ctx context.Context, metadata *adapter.InboundContext, defaultOutbound adapter.Outbound) (adapter.Rule, adapter.Outbound) { - if r.processSearcher != nil { - var originDestination netip.AddrPort - if metadata.OriginDestination.IsValid() { - originDestination = metadata.OriginDestination.AddrPort() - } else if metadata.Destination.IsIP() { - originDestination = metadata.Destination.AddrPort() - } - processInfo, err := process.FindProcessInfo(r.processSearcher, ctx, metadata.Network, metadata.Source.AddrPort(), originDestination) - if err != nil { - r.logger.InfoContext(ctx, "failed to search process: ", err) - } else { - if processInfo.ProcessPath != "" { - r.logger.InfoContext(ctx, "found process path: ", processInfo.ProcessPath) - } else if processInfo.PackageName != "" { - r.logger.InfoContext(ctx, "found package name: ", processInfo.PackageName) - } else if processInfo.UserId != -1 { - if /*needUserName &&*/ true { - osUser, _ := user.LookupId(F.ToString(processInfo.UserId)) - if osUser != nil { - processInfo.User = osUser.Username - } - } - if processInfo.User != "" { - r.logger.InfoContext(ctx, "found user: ", processInfo.User) - } else { - r.logger.InfoContext(ctx, "found user id: ", processInfo.UserId) - } - } - metadata.ProcessInfo = processInfo - } - } - for i, rule := range r.rules { - metadata.ResetRuleCache() - if rule.Match(metadata) { - detour := rule.Outbound() - r.logger.DebugContext(ctx, "match[", i, "] ", rule.String(), " => ", detour) - if outbound, loaded := r.Outbound(detour); loaded { - return rule, outbound - } - r.logger.ErrorContext(ctx, "outbound not found: ", detour) - } - } - return nil, defaultOutbound -} - func (r *Router) InterfaceFinder() control.InterfaceFinder { return r.interfaceFinder } diff --git a/route/rule_abstract.go b/route/rule/rule_abstract.go similarity index 94% rename from route/rule_abstract.go rename to route/rule/rule_abstract.go index 9ef2e932..6a569341 100644 --- a/route/rule_abstract.go +++ b/route/rule/rule_abstract.go @@ -1,4 +1,4 @@ -package route +package rule import ( "io" @@ -20,7 +20,7 @@ type abstractDefaultRule struct { allItems []RuleItem ruleSetItem RuleItem invert bool - outbound string + action adapter.RuleAction } func (r *abstractDefaultRule) Type() string { @@ -150,8 +150,8 @@ func (r *abstractDefaultRule) Match(metadata *adapter.InboundContext) bool { return !r.invert } -func (r *abstractDefaultRule) Outbound() string { - return r.outbound +func (r *abstractDefaultRule) Action() adapter.RuleAction { + return r.action } func (r *abstractDefaultRule) String() string { @@ -163,10 +163,10 @@ func (r *abstractDefaultRule) String() string { } type abstractLogicalRule struct { - rules []adapter.HeadlessRule - mode string - invert bool - outbound string + rules []adapter.HeadlessRule + mode string + invert bool + action adapter.RuleAction } func (r *abstractLogicalRule) Type() string { @@ -231,8 +231,8 @@ func (r *abstractLogicalRule) Match(metadata *adapter.InboundContext) bool { } } -func (r *abstractLogicalRule) Outbound() string { - return r.outbound +func (r *abstractLogicalRule) Action() adapter.RuleAction { + return r.action } func (r *abstractLogicalRule) String() string { diff --git a/route/rule/rule_action.go b/route/rule/rule_action.go new file mode 100644 index 00000000..e85fc763 --- /dev/null +++ b/route/rule/rule_action.go @@ -0,0 +1,228 @@ +package rule + +import ( + "net/netip" + "strings" + "time" + + "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/common/sniff" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing-dns" + E "github.com/sagernet/sing/common/exceptions" + F "github.com/sagernet/sing/common/format" +) + +func NewRuleAction(action option.RuleAction) (adapter.RuleAction, error) { + switch action.Action { + case C.RuleActionTypeRoute: + return &RuleActionRoute{ + Outbound: action.RouteOptions.Outbound, + UDPDisableDomainUnmapping: action.RouteOptions.UDPDisableDomainUnmapping, + }, nil + case C.RuleActionTypeReturn: + return &RuleActionReject{}, nil + case C.RuleActionTypeReject: + return &RuleActionReject{ + Method: string(action.RejectOptions.Method), + }, nil + case C.RuleActionTypeHijackDNS: + return &RuleActionHijackDNS{}, nil + case C.RuleActionTypeSniff: + sniffAction := &RuleActionSniff{ + snifferNames: action.SniffOptions.Sniffer, + Timeout: time.Duration(action.SniffOptions.Timeout), + } + return sniffAction, sniffAction.build() + case C.RuleActionTypeResolve: + return &RuleActionResolve{ + Strategy: dns.DomainStrategy(action.ResolveOptions.Strategy), + Server: action.ResolveOptions.Server, + }, nil + default: + panic(F.ToString("unknown rule action: ", action.Action)) + } +} + +func NewDNSRuleAction(action option.DNSRuleAction) adapter.RuleAction { + switch action.Action { + case C.RuleActionTypeRoute: + return &RuleActionDNSRoute{ + Server: action.RouteOptions.Server, + DisableCache: action.RouteOptions.DisableCache, + RewriteTTL: action.RouteOptions.RewriteTTL, + ClientSubnet: action.RouteOptions.ClientSubnet.Build(), + } + case C.RuleActionTypeReturn: + return &RuleActionReturn{} + case C.RuleActionTypeReject: + return &RuleActionReject{ + Method: string(action.RejectOptions.Method), + } + default: + panic(F.ToString("unknown rule action: ", action.Action)) + } +} + +type RuleActionRoute struct { + Outbound string + UDPDisableDomainUnmapping bool +} + +func (r *RuleActionRoute) Type() string { + return C.RuleActionTypeRoute +} + +func (r *RuleActionRoute) String() string { + return F.ToString("route(", r.Outbound, ")") +} + +type RuleActionDNSRoute struct { + Server string + DisableCache bool + RewriteTTL *uint32 + ClientSubnet netip.Prefix +} + +func (r *RuleActionDNSRoute) Type() string { + return C.RuleActionTypeRoute +} + +func (r *RuleActionDNSRoute) String() string { + return F.ToString("route(", r.Server, ")") +} + +type RuleActionReturn struct{} + +func (r *RuleActionReturn) Type() string { + return C.RuleActionTypeReturn +} + +func (r *RuleActionReturn) String() string { + return "return" +} + +type RuleActionReject struct { + Method string +} + +func (r *RuleActionReject) Type() string { + return C.RuleActionTypeReject +} + +func (r *RuleActionReject) String() string { + if r.Method == C.RuleActionRejectMethodDefault { + return "reject" + } + return F.ToString("reject(", r.Method, ")") +} + +type RuleActionHijackDNS struct{} + +func (r *RuleActionHijackDNS) Type() string { + return C.RuleActionTypeHijackDNS +} + +func (r *RuleActionHijackDNS) String() string { + return "hijack-dns" +} + +type RuleActionSniff struct { + snifferNames []string + StreamSniffers []sniff.StreamSniffer + PacketSniffers []sniff.PacketSniffer + Timeout time.Duration + // Deprecated + OverrideDestination bool +} + +func (r *RuleActionSniff) Type() string { + return C.RuleActionTypeSniff +} + +func (r *RuleActionSniff) build() error { + if len(r.StreamSniffers) > 0 || len(r.PacketSniffers) > 0 { + return nil + } + if len(r.snifferNames) > 0 { + for _, name := range r.snifferNames { + switch name { + case C.ProtocolTLS: + r.StreamSniffers = append(r.StreamSniffers, sniff.TLSClientHello) + case C.ProtocolHTTP: + r.StreamSniffers = append(r.StreamSniffers, sniff.HTTPHost) + case C.ProtocolQUIC: + r.PacketSniffers = append(r.PacketSniffers, sniff.QUICClientHello) + case C.ProtocolDNS: + r.StreamSniffers = append(r.StreamSniffers, sniff.StreamDomainNameQuery) + r.PacketSniffers = append(r.PacketSniffers, sniff.DomainNameQuery) + case C.ProtocolSTUN: + r.PacketSniffers = append(r.PacketSniffers, sniff.STUNMessage) + case C.ProtocolBitTorrent: + r.StreamSniffers = append(r.StreamSniffers, sniff.BitTorrent) + r.PacketSniffers = append(r.PacketSniffers, sniff.UTP) + r.PacketSniffers = append(r.PacketSniffers, sniff.UDPTracker) + case C.ProtocolDTLS: + r.PacketSniffers = append(r.PacketSniffers, sniff.DTLSRecord) + case C.ProtocolSSH: + r.StreamSniffers = append(r.StreamSniffers, sniff.SSH) + case C.ProtocolRDP: + r.StreamSniffers = append(r.StreamSniffers, sniff.RDP) + default: + return E.New("unknown sniffer: ", name) + } + } + } else { + r.StreamSniffers = []sniff.StreamSniffer{ + sniff.TLSClientHello, + sniff.HTTPHost, + sniff.StreamDomainNameQuery, + sniff.BitTorrent, + sniff.SSH, + sniff.RDP, + } + r.PacketSniffers = []sniff.PacketSniffer{ + sniff.DomainNameQuery, + sniff.QUICClientHello, + sniff.STUNMessage, + sniff.UTP, + sniff.UDPTracker, + sniff.DTLSRecord, + } + } + return nil +} + +func (r *RuleActionSniff) String() string { + if len(r.snifferNames) == 0 && r.Timeout == 0 { + return "sniff" + } else if len(r.snifferNames) > 0 && r.Timeout == 0 { + return F.ToString("sniff(", strings.Join(r.snifferNames, ","), ")") + } else if len(r.snifferNames) == 0 && r.Timeout > 0 { + return F.ToString("sniff(", r.Timeout.String(), ")") + } else { + return F.ToString("sniff(", strings.Join(r.snifferNames, ","), ",", r.Timeout.String(), ")") + } +} + +type RuleActionResolve struct { + Strategy dns.DomainStrategy + Server string +} + +func (r *RuleActionResolve) Type() string { + return C.RuleActionTypeResolve +} + +func (r *RuleActionResolve) String() string { + if r.Strategy == dns.DomainStrategyAsIS && r.Server == "" { + return F.ToString("resolve") + } else if r.Strategy != dns.DomainStrategyAsIS && r.Server == "" { + return F.ToString("resolve(", option.DomainStrategy(r.Strategy).String(), ")") + } else if r.Strategy == dns.DomainStrategyAsIS && r.Server != "" { + return F.ToString("resolve(", r.Server, ")") + } else { + return F.ToString("resolve(", option.DomainStrategy(r.Strategy).String(), ",", r.Server, ")") + } +} diff --git a/route/rule_default.go b/route/rule/rule_default.go similarity index 88% rename from route/rule_default.go rename to route/rule/rule_default.go index fb4f6d82..4f5d1e8a 100644 --- a/route/rule_default.go +++ b/route/rule/rule_default.go @@ -1,4 +1,4 @@ -package route +package rule import ( "context" @@ -17,16 +17,22 @@ func NewRule(ctx context.Context, router adapter.Router, logger log.ContextLogge if !options.DefaultOptions.IsValid() { return nil, E.New("missing conditions") } - if options.DefaultOptions.Outbound == "" && checkOutbound { - return nil, E.New("missing outbound field") + switch options.DefaultOptions.Action { + case "", C.RuleActionTypeRoute: + if options.DefaultOptions.RouteOptions.Outbound == "" && checkOutbound { + return nil, E.New("missing outbound field") + } } return NewDefaultRule(ctx, router, logger, options.DefaultOptions) case C.RuleTypeLogical: if !options.LogicalOptions.IsValid() { return nil, E.New("missing conditions") } - if options.LogicalOptions.Outbound == "" && checkOutbound { - return nil, E.New("missing outbound field") + switch options.LogicalOptions.Action { + case "", C.RuleActionTypeRoute: + if options.LogicalOptions.RouteOptions.Outbound == "" && checkOutbound { + return nil, E.New("missing outbound field") + } } return NewLogicalRule(ctx, router, logger, options.LogicalOptions) default: @@ -46,10 +52,14 @@ type RuleItem interface { } func NewDefaultRule(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.DefaultRule) (*DefaultRule, error) { + action, err := NewRuleAction(options.RuleAction) + if err != nil { + return nil, E.Cause(err, "action") + } rule := &DefaultRule{ abstractDefaultRule{ - invert: options.Invert, - outbound: options.Outbound, + invert: options.Invert, + action: action, }, } if len(options.Inbound) > 0 { @@ -244,27 +254,31 @@ type LogicalRule struct { } func NewLogicalRule(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.LogicalRule) (*LogicalRule, error) { - r := &LogicalRule{ + action, err := NewRuleAction(options.RuleAction) + if err != nil { + return nil, E.Cause(err, "action") + } + rule := &LogicalRule{ abstractLogicalRule{ - rules: make([]adapter.HeadlessRule, len(options.Rules)), - invert: options.Invert, - outbound: options.Outbound, + rules: make([]adapter.HeadlessRule, len(options.Rules)), + invert: options.Invert, + action: action, }, } switch options.Mode { case C.LogicalTypeAnd: - r.mode = C.LogicalTypeAnd + rule.mode = C.LogicalTypeAnd case C.LogicalTypeOr: - r.mode = C.LogicalTypeOr + rule.mode = C.LogicalTypeOr default: return nil, E.New("unknown logical mode: ", options.Mode) } - for i, subRule := range options.Rules { - rule, err := NewRule(ctx, router, logger, subRule, false) + for i, subOptions := range options.Rules { + subRule, err := NewRule(ctx, router, logger, subOptions, false) if err != nil { return nil, E.Cause(err, "sub rule[", i, "]") } - r.rules[i] = rule + rule.rules[i] = subRule } - return r, nil + return rule, nil } diff --git a/route/rule_dns.go b/route/rule/rule_dns.go similarity index 89% rename from route/rule_dns.go rename to route/rule/rule_dns.go index 4740488e..6e57633d 100644 --- a/route/rule_dns.go +++ b/route/rule/rule_dns.go @@ -1,8 +1,7 @@ -package route +package rule import ( "context" - "net/netip" "github.com/sagernet/sing-box/adapter" C "github.com/sagernet/sing-box/constant" @@ -19,16 +18,22 @@ func NewDNSRule(ctx context.Context, router adapter.Router, logger log.ContextLo if !options.DefaultOptions.IsValid() { return nil, E.New("missing conditions") } - if options.DefaultOptions.Server == "" && checkServer { - return nil, E.New("missing server field") + switch options.DefaultOptions.Action { + case "", C.RuleActionTypeRoute: + if options.DefaultOptions.RouteOptions.Server == "" && checkServer { + return nil, E.New("missing server field") + } } return NewDefaultDNSRule(ctx, router, logger, options.DefaultOptions) case C.RuleTypeLogical: if !options.LogicalOptions.IsValid() { return nil, E.New("missing conditions") } - if options.LogicalOptions.Server == "" && checkServer { - return nil, E.New("missing server field") + switch options.LogicalOptions.Action { + case "", C.RuleActionTypeRoute: + if options.LogicalOptions.RouteOptions.Server == "" && checkServer { + return nil, E.New("missing server field") + } } return NewLogicalDNSRule(ctx, router, logger, options.LogicalOptions) default: @@ -40,20 +45,14 @@ var _ adapter.DNSRule = (*DefaultDNSRule)(nil) type DefaultDNSRule struct { abstractDefaultRule - disableCache bool - rewriteTTL *uint32 - clientSubnet *netip.Prefix } func NewDefaultDNSRule(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.DefaultDNSRule) (*DefaultDNSRule, error) { rule := &DefaultDNSRule{ abstractDefaultRule: abstractDefaultRule{ - invert: options.Invert, - outbound: options.Server, + invert: options.Invert, + action: NewDNSRuleAction(options.DNSRuleAction), }, - disableCache: options.DisableCache, - rewriteTTL: options.RewriteTTL, - clientSubnet: (*netip.Prefix)(options.ClientSubnet), } if len(options.Inbound) > 0 { item := NewInboundRule(options.Inbound) @@ -245,16 +244,8 @@ func NewDefaultDNSRule(ctx context.Context, router adapter.Router, logger log.Co return rule, nil } -func (r *DefaultDNSRule) DisableCache() bool { - return r.disableCache -} - -func (r *DefaultDNSRule) RewriteTTL() *uint32 { - return r.rewriteTTL -} - -func (r *DefaultDNSRule) ClientSubnet() *netip.Prefix { - return r.clientSubnet +func (r *DefaultDNSRule) Action() adapter.RuleAction { + return r.action } func (r *DefaultDNSRule) WithAddressLimit() bool { @@ -289,21 +280,15 @@ var _ adapter.DNSRule = (*LogicalDNSRule)(nil) type LogicalDNSRule struct { abstractLogicalRule - disableCache bool - rewriteTTL *uint32 - clientSubnet *netip.Prefix } func NewLogicalDNSRule(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.LogicalDNSRule) (*LogicalDNSRule, error) { r := &LogicalDNSRule{ abstractLogicalRule: abstractLogicalRule{ - rules: make([]adapter.HeadlessRule, len(options.Rules)), - invert: options.Invert, - outbound: options.Server, + rules: make([]adapter.HeadlessRule, len(options.Rules)), + invert: options.Invert, + action: NewDNSRuleAction(options.DNSRuleAction), }, - disableCache: options.DisableCache, - rewriteTTL: options.RewriteTTL, - clientSubnet: (*netip.Prefix)(options.ClientSubnet), } switch options.Mode { case C.LogicalTypeAnd: @@ -323,16 +308,8 @@ func NewLogicalDNSRule(ctx context.Context, router adapter.Router, logger log.Co return r, nil } -func (r *LogicalDNSRule) DisableCache() bool { - return r.disableCache -} - -func (r *LogicalDNSRule) RewriteTTL() *uint32 { - return r.rewriteTTL -} - -func (r *LogicalDNSRule) ClientSubnet() *netip.Prefix { - return r.clientSubnet +func (r *LogicalDNSRule) Action() adapter.RuleAction { + return r.action } func (r *LogicalDNSRule) WithAddressLimit() bool { diff --git a/route/rule_headless.go b/route/rule/rule_headless.go similarity index 99% rename from route/rule_headless.go rename to route/rule/rule_headless.go index 23a98c72..9ea357af 100644 --- a/route/rule_headless.go +++ b/route/rule/rule_headless.go @@ -1,4 +1,4 @@ -package route +package rule import ( "github.com/sagernet/sing-box/adapter" diff --git a/route/rule_item_adguard.go b/route/rule/rule_item_adguard.go similarity index 98% rename from route/rule_item_adguard.go rename to route/rule/rule_item_adguard.go index bdbb3b75..84252e60 100644 --- a/route/rule_item_adguard.go +++ b/route/rule/rule_item_adguard.go @@ -1,4 +1,4 @@ -package route +package rule import ( "strings" diff --git a/route/rule_item_auth_user.go b/route/rule/rule_item_auth_user.go similarity index 98% rename from route/rule_item_auth_user.go rename to route/rule/rule_item_auth_user.go index fbe053e6..5799e3c7 100644 --- a/route/rule_item_auth_user.go +++ b/route/rule/rule_item_auth_user.go @@ -1,4 +1,4 @@ -package route +package rule import ( "strings" diff --git a/route/rule_item_cidr.go b/route/rule/rule_item_cidr.go similarity index 99% rename from route/rule_item_cidr.go rename to route/rule/rule_item_cidr.go index be0bb136..c823dcf3 100644 --- a/route/rule_item_cidr.go +++ b/route/rule/rule_item_cidr.go @@ -1,4 +1,4 @@ -package route +package rule import ( "net/netip" diff --git a/route/rule_item_clash_mode.go b/route/rule/rule_item_clash_mode.go similarity index 97% rename from route/rule_item_clash_mode.go rename to route/rule/rule_item_clash_mode.go index 70141f11..aa5126cb 100644 --- a/route/rule_item_clash_mode.go +++ b/route/rule/rule_item_clash_mode.go @@ -1,4 +1,4 @@ -package route +package rule import ( "strings" diff --git a/route/rule_item_client.go b/route/rule/rule_item_client.go similarity index 98% rename from route/rule_item_client.go rename to route/rule/rule_item_client.go index eeab4402..63ff4103 100644 --- a/route/rule_item_client.go +++ b/route/rule/rule_item_client.go @@ -1,4 +1,4 @@ -package route +package rule import ( "strings" diff --git a/route/rule_item_domain.go b/route/rule/rule_item_domain.go similarity index 99% rename from route/rule_item_domain.go rename to route/rule/rule_item_domain.go index c77890df..b7655a79 100644 --- a/route/rule_item_domain.go +++ b/route/rule/rule_item_domain.go @@ -1,4 +1,4 @@ -package route +package rule import ( "strings" diff --git a/route/rule_item_domain_keyword.go b/route/rule/rule_item_domain_keyword.go similarity index 98% rename from route/rule_item_domain_keyword.go rename to route/rule/rule_item_domain_keyword.go index c6ca1e8c..6e19a10c 100644 --- a/route/rule_item_domain_keyword.go +++ b/route/rule/rule_item_domain_keyword.go @@ -1,4 +1,4 @@ -package route +package rule import ( "strings" diff --git a/route/rule_item_domain_regex.go b/route/rule/rule_item_domain_regex.go similarity index 99% rename from route/rule_item_domain_regex.go rename to route/rule/rule_item_domain_regex.go index b3555168..b9752a45 100644 --- a/route/rule_item_domain_regex.go +++ b/route/rule/rule_item_domain_regex.go @@ -1,4 +1,4 @@ -package route +package rule import ( "regexp" diff --git a/route/rule_item_geoip.go b/route/rule/rule_item_geoip.go similarity index 99% rename from route/rule_item_geoip.go rename to route/rule/rule_item_geoip.go index 3611613a..3c967fec 100644 --- a/route/rule_item_geoip.go +++ b/route/rule/rule_item_geoip.go @@ -1,4 +1,4 @@ -package route +package rule import ( "net/netip" diff --git a/route/rule_item_geosite.go b/route/rule/rule_item_geosite.go similarity index 98% rename from route/rule_item_geosite.go rename to route/rule/rule_item_geosite.go index 5fdbfe59..9e5e03c8 100644 --- a/route/rule_item_geosite.go +++ b/route/rule/rule_item_geosite.go @@ -1,4 +1,4 @@ -package route +package rule import ( "strings" diff --git a/route/rule_item_inbound.go b/route/rule/rule_item_inbound.go similarity index 98% rename from route/rule_item_inbound.go rename to route/rule/rule_item_inbound.go index 7e28781f..87e84740 100644 --- a/route/rule_item_inbound.go +++ b/route/rule/rule_item_inbound.go @@ -1,4 +1,4 @@ -package route +package rule import ( "strings" diff --git a/route/rule_item_ip_is_private.go b/route/rule/rule_item_ip_is_private.go similarity index 98% rename from route/rule_item_ip_is_private.go rename to route/rule/rule_item_ip_is_private.go index 6592a9d3..e185db1d 100644 --- a/route/rule_item_ip_is_private.go +++ b/route/rule/rule_item_ip_is_private.go @@ -1,4 +1,4 @@ -package route +package rule import ( "net/netip" diff --git a/route/rule_item_ipversion.go b/route/rule/rule_item_ipversion.go similarity index 97% rename from route/rule_item_ipversion.go rename to route/rule/rule_item_ipversion.go index 3d8762b4..8ab64942 100644 --- a/route/rule_item_ipversion.go +++ b/route/rule/rule_item_ipversion.go @@ -1,4 +1,4 @@ -package route +package rule import ( "github.com/sagernet/sing-box/adapter" diff --git a/route/rule_item_network.go b/route/rule/rule_item_network.go similarity index 98% rename from route/rule_item_network.go rename to route/rule/rule_item_network.go index fc54f425..bfb334d3 100644 --- a/route/rule_item_network.go +++ b/route/rule/rule_item_network.go @@ -1,4 +1,4 @@ -package route +package rule import ( "strings" diff --git a/route/rule_item_outbound.go b/route/rule/rule_item_outbound.go similarity index 98% rename from route/rule_item_outbound.go rename to route/rule/rule_item_outbound.go index 4b3e16fc..3f37dee7 100644 --- a/route/rule_item_outbound.go +++ b/route/rule/rule_item_outbound.go @@ -1,4 +1,4 @@ -package route +package rule import ( "strings" diff --git a/route/rule_item_package_name.go b/route/rule/rule_item_package_name.go similarity index 98% rename from route/rule_item_package_name.go rename to route/rule/rule_item_package_name.go index d1ca09eb..0066735c 100644 --- a/route/rule_item_package_name.go +++ b/route/rule/rule_item_package_name.go @@ -1,4 +1,4 @@ -package route +package rule import ( "strings" diff --git a/route/rule_item_port.go b/route/rule/rule_item_port.go similarity index 98% rename from route/rule_item_port.go rename to route/rule/rule_item_port.go index 62478933..af166ee6 100644 --- a/route/rule_item_port.go +++ b/route/rule/rule_item_port.go @@ -1,4 +1,4 @@ -package route +package rule import ( "strings" diff --git a/route/rule_item_port_range.go b/route/rule/rule_item_port_range.go similarity index 99% rename from route/rule_item_port_range.go rename to route/rule/rule_item_port_range.go index f87575f2..980f7d23 100644 --- a/route/rule_item_port_range.go +++ b/route/rule/rule_item_port_range.go @@ -1,4 +1,4 @@ -package route +package rule import ( "strconv" diff --git a/route/rule_item_process_name.go b/route/rule/rule_item_process_name.go similarity index 98% rename from route/rule_item_process_name.go rename to route/rule/rule_item_process_name.go index ce051666..fa0f7165 100644 --- a/route/rule_item_process_name.go +++ b/route/rule/rule_item_process_name.go @@ -1,4 +1,4 @@ -package route +package rule import ( "path/filepath" diff --git a/route/rule_item_process_path.go b/route/rule/rule_item_process_path.go similarity index 98% rename from route/rule_item_process_path.go rename to route/rule/rule_item_process_path.go index feae4b27..75dee476 100644 --- a/route/rule_item_process_path.go +++ b/route/rule/rule_item_process_path.go @@ -1,4 +1,4 @@ -package route +package rule import ( "strings" diff --git a/route/rule_item_process_path_regex.go b/route/rule/rule_item_process_path_regex.go similarity index 98% rename from route/rule_item_process_path_regex.go rename to route/rule/rule_item_process_path_regex.go index 01b2723c..76cf67b9 100644 --- a/route/rule_item_process_path_regex.go +++ b/route/rule/rule_item_process_path_regex.go @@ -1,4 +1,4 @@ -package route +package rule import ( "regexp" diff --git a/route/rule_item_protocol.go b/route/rule/rule_item_protocol.go similarity index 98% rename from route/rule_item_protocol.go rename to route/rule/rule_item_protocol.go index 1988f8ad..319b81d5 100644 --- a/route/rule_item_protocol.go +++ b/route/rule/rule_item_protocol.go @@ -1,4 +1,4 @@ -package route +package rule import ( "strings" diff --git a/route/rule_item_query_type.go b/route/rule/rule_item_query_type.go similarity index 98% rename from route/rule_item_query_type.go rename to route/rule/rule_item_query_type.go index 7b6efdd0..36b615f3 100644 --- a/route/rule_item_query_type.go +++ b/route/rule/rule_item_query_type.go @@ -1,4 +1,4 @@ -package route +package rule import ( "strings" diff --git a/route/rule_item_rule_set.go b/route/rule/rule_item_rule_set.go similarity index 99% rename from route/rule_item_rule_set.go rename to route/rule/rule_item_rule_set.go index b80fca99..a0115a04 100644 --- a/route/rule_item_rule_set.go +++ b/route/rule/rule_item_rule_set.go @@ -1,4 +1,4 @@ -package route +package rule import ( "strings" diff --git a/route/rule_item_user.go b/route/rule/rule_item_user.go similarity index 98% rename from route/rule_item_user.go rename to route/rule/rule_item_user.go index bed97fba..d635fa16 100644 --- a/route/rule_item_user.go +++ b/route/rule/rule_item_user.go @@ -1,4 +1,4 @@ -package route +package rule import ( "strings" diff --git a/route/rule_item_user_id.go b/route/rule/rule_item_user_id.go similarity index 98% rename from route/rule_item_user_id.go rename to route/rule/rule_item_user_id.go index 43ab704e..57372de0 100644 --- a/route/rule_item_user_id.go +++ b/route/rule/rule_item_user_id.go @@ -1,4 +1,4 @@ -package route +package rule import ( "strings" diff --git a/route/rule_item_wifi_bssid.go b/route/rule/rule_item_wifi_bssid.go similarity index 98% rename from route/rule_item_wifi_bssid.go rename to route/rule/rule_item_wifi_bssid.go index 3b1ff9c8..ae94bd6d 100644 --- a/route/rule_item_wifi_bssid.go +++ b/route/rule/rule_item_wifi_bssid.go @@ -1,4 +1,4 @@ -package route +package rule import ( "strings" diff --git a/route/rule_item_wifi_ssid.go b/route/rule/rule_item_wifi_ssid.go similarity index 98% rename from route/rule_item_wifi_ssid.go rename to route/rule/rule_item_wifi_ssid.go index 62cf935e..3a928f77 100644 --- a/route/rule_item_wifi_ssid.go +++ b/route/rule/rule_item_wifi_ssid.go @@ -1,4 +1,4 @@ -package route +package rule import ( "strings" diff --git a/route/rule_set.go b/route/rule/rule_set.go similarity index 60% rename from route/rule_set.go rename to route/rule/rule_set.go index a2c6d0c1..cdd0fc0a 100644 --- a/route/rule_set.go +++ b/route/rule/rule_set.go @@ -1,4 +1,4 @@ -package route +package rule import ( "context" @@ -41,3 +41,31 @@ func extractIPSetFromRule(rawRule adapter.HeadlessRule) []*netipx.IPSet { panic("unexpected rule type") } } + +func hasHeadlessRule(rules []option.HeadlessRule, cond func(rule option.DefaultHeadlessRule) bool) bool { + for _, rule := range rules { + switch rule.Type { + case C.RuleTypeDefault: + if cond(rule.DefaultOptions) { + return true + } + case C.RuleTypeLogical: + if hasHeadlessRule(rule.LogicalOptions.Rules, cond) { + return true + } + } + } + return false +} + +func isProcessHeadlessRule(rule option.DefaultHeadlessRule) bool { + return len(rule.ProcessName) > 0 || len(rule.ProcessPath) > 0 || len(rule.PackageName) > 0 +} + +func isWIFIHeadlessRule(rule option.DefaultHeadlessRule) bool { + return len(rule.WIFISSID) > 0 || len(rule.WIFIBSSID) > 0 +} + +func isIPCIDRHeadlessRule(rule option.DefaultHeadlessRule) bool { + return len(rule.IPCIDR) > 0 || rule.IPSet != nil +} diff --git a/route/rule_set_local.go b/route/rule/rule_set_local.go similarity index 99% rename from route/rule_set_local.go rename to route/rule/rule_set_local.go index c3ecf9ca..9186454e 100644 --- a/route/rule_set_local.go +++ b/route/rule/rule_set_local.go @@ -1,4 +1,4 @@ -package route +package rule import ( "context" diff --git a/route/rule_set_remote.go b/route/rule/rule_set_remote.go similarity index 99% rename from route/rule_set_remote.go rename to route/rule/rule_set_remote.go index 5045bf2d..f0557718 100644 --- a/route/rule_set_remote.go +++ b/route/rule/rule_set_remote.go @@ -1,4 +1,4 @@ -package route +package rule import ( "bytes" diff --git a/route/router_rule.go b/route/rule_conds.go similarity index 78% rename from route/router_rule.go rename to route/rule_conds.go index 4a99a31c..76ed84a2 100644 --- a/route/router_rule.go +++ b/route/rule_conds.go @@ -38,22 +38,6 @@ func hasDNSRule(rules []option.DNSRule, cond func(rule option.DefaultDNSRule) bo return false } -func hasHeadlessRule(rules []option.HeadlessRule, cond func(rule option.DefaultHeadlessRule) bool) bool { - for _, rule := range rules { - switch rule.Type { - case C.RuleTypeDefault: - if cond(rule.DefaultOptions) { - return true - } - case C.RuleTypeLogical: - if hasHeadlessRule(rule.LogicalOptions.Rules, cond) { - return true - } - } - } - return false -} - func isGeoIPRule(rule option.DefaultRule) bool { return len(rule.SourceGeoIP) > 0 && common.Any(rule.SourceGeoIP, notPrivateNode) || len(rule.GeoIP) > 0 && common.Any(rule.GeoIP, notPrivateNode) } @@ -93,11 +77,3 @@ func isWIFIRule(rule option.DefaultRule) bool { func isWIFIDNSRule(rule option.DefaultDNSRule) bool { return len(rule.WIFISSID) > 0 || len(rule.WIFIBSSID) > 0 } - -func isWIFIHeadlessRule(rule option.DefaultHeadlessRule) bool { - return len(rule.WIFISSID) > 0 || len(rule.WIFIBSSID) > 0 -} - -func isIPCIDRHeadlessRule(rule option.DefaultHeadlessRule) bool { - return len(rule.IPCIDR) > 0 || rule.IPSet != nil -} diff --git a/test/brutal_test.go b/test/brutal_test.go index bfe4d1fc..18aae2e2 100644 --- a/test/brutal_test.go +++ b/test/brutal_test.go @@ -76,9 +76,18 @@ func TestBrutalShadowsocks(t *testing.T) { Route: &option.RouteOptions{ Rules: []option.Rule{ { + Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ - Inbound: []string{"mixed-in"}, - Outbound: "ss-out", + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"mixed-in"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + + RouteOptions: option.RouteActionOptions{ + Outbound: "ss-out", + }, + }, }, }, }, @@ -165,9 +174,18 @@ func TestBrutalTrojan(t *testing.T) { Route: &option.RouteOptions{ Rules: []option.Rule{ { + Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ - Inbound: []string{"mixed-in"}, - Outbound: "ss-out", + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"mixed-in"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + + RouteOptions: option.RouteActionOptions{ + Outbound: "ss-out", + }, + }, }, }, }, @@ -238,9 +256,18 @@ func TestBrutalVMess(t *testing.T) { Route: &option.RouteOptions{ Rules: []option.Rule{ { + Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ - Inbound: []string{"mixed-in"}, - Outbound: "ss-out", + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"mixed-in"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + + RouteOptions: option.RouteActionOptions{ + Outbound: "ss-out", + }, + }, }, }, }, @@ -342,9 +369,18 @@ func TestBrutalVLESS(t *testing.T) { Route: &option.RouteOptions{ Rules: []option.Rule{ { + Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ - Inbound: []string{"mixed-in"}, - Outbound: "ss-out", + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"mixed-in"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + + RouteOptions: option.RouteActionOptions{ + Outbound: "ss-out", + }, + }, }, }, }, diff --git a/test/clash_test.go b/test/clash_test.go index ffd3e10c..bba7f3be 100644 --- a/test/clash_test.go +++ b/test/clash_test.go @@ -17,7 +17,7 @@ import ( "github.com/sagernet/sing/common/control" F "github.com/sagernet/sing/common/format" - "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/image" "github.com/docker/docker/client" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -68,7 +68,7 @@ func init() { } defer dockerClient.Close() - list, err := dockerClient.ImageList(context.Background(), types.ImageListOptions{All: true}) + list, err := dockerClient.ImageList(context.Background(), image.ListOptions{All: true}) if err != nil { log.Warn(err) return @@ -85,13 +85,13 @@ func init() { return false } - for _, image := range allImages { - if imageExist(image) { + for _, i := range allImages { + if imageExist(i) { continue } - log.Info("pulling image: ", image) - imageStream, err := dockerClient.ImagePull(context.Background(), image, types.ImagePullOptions{}) + log.Info("pulling image: ", i) + imageStream, err := dockerClient.ImagePull(context.Background(), i, image.PullOptions{}) if err != nil { panic(err) } diff --git a/test/direct_test.go b/test/direct_test.go index ec3cf88c..1dbf1de1 100644 --- a/test/direct_test.go +++ b/test/direct_test.go @@ -50,9 +50,18 @@ func _TestProxyProtocol(t *testing.T) { Route: &option.RouteOptions{ Rules: []option.Rule{ { + Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ - Inbound: []string{"mixed-in"}, - Outbound: "proxy-out", + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"mixed-in"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + + RouteOptions: option.RouteActionOptions{ + Outbound: "proxy-out", + }, + }, }, }, }, diff --git a/test/docker_test.go b/test/docker_test.go index ade813d7..a85dd12c 100644 --- a/test/docker_test.go +++ b/test/docker_test.go @@ -11,7 +11,6 @@ import ( F "github.com/sagernet/sing/common/format" "github.com/sagernet/sing/common/rw" - "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/docker/client" "github.com/docker/docker/pkg/stdcopy" @@ -85,10 +84,10 @@ func startDockerContainer(t *testing.T, options DockerOptions) { cleanContainer(dockerContainer.ID) }) - require.NoError(t, dockerClient.ContainerStart(context.Background(), dockerContainer.ID, types.ContainerStartOptions{})) + require.NoError(t, dockerClient.ContainerStart(context.Background(), dockerContainer.ID, container.StartOptions{})) if writeStdin { - stdinAttach, err := dockerClient.ContainerAttach(context.Background(), dockerContainer.ID, types.ContainerAttachOptions{ + stdinAttach, err := dockerClient.ContainerAttach(context.Background(), dockerContainer.ID, container.AttachOptions{ Stdin: writeStdin, Stream: true, }) @@ -98,7 +97,7 @@ func startDockerContainer(t *testing.T, options DockerOptions) { stdinAttach.Close() } if debug.Enabled { - attach, err := dockerClient.ContainerAttach(context.Background(), dockerContainer.ID, types.ContainerAttachOptions{ + attach, err := dockerClient.ContainerAttach(context.Background(), dockerContainer.ID, container.AttachOptions{ Stdout: true, Stderr: true, Logs: true, @@ -118,5 +117,5 @@ func cleanContainer(id string) error { return err } defer dockerClient.Close() - return dockerClient.ContainerRemove(context.Background(), id, types.ContainerRemoveOptions{Force: true}) + return dockerClient.ContainerRemove(context.Background(), id, container.RemoveOptions{Force: true}) } diff --git a/test/domain_inbound_test.go b/test/domain_inbound_test.go index f22fe249..1ca2121d 100644 --- a/test/domain_inbound_test.go +++ b/test/domain_inbound_test.go @@ -75,9 +75,18 @@ func TestTUICDomainUDP(t *testing.T) { Route: &option.RouteOptions{ Rules: []option.Rule{ { + Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ - Inbound: []string{"mixed-in"}, - Outbound: "tuic-out", + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"mixed-in"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + + RouteOptions: option.RouteActionOptions{ + Outbound: "tuic-out", + }, + }, }, }, }, diff --git a/test/ech_test.go b/test/ech_test.go index 35d5d891..90eae1f4 100644 --- a/test/ech_test.go +++ b/test/ech_test.go @@ -85,9 +85,18 @@ func TestECH(t *testing.T) { Route: &option.RouteOptions{ Rules: []option.Rule{ { + Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ - Inbound: []string{"mixed-in"}, - Outbound: "trojan-out", + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"mixed-in"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + + RouteOptions: option.RouteActionOptions{ + Outbound: "trojan-out", + }, + }, }, }, }, @@ -166,9 +175,18 @@ func TestECHQUIC(t *testing.T) { Route: &option.RouteOptions{ Rules: []option.Rule{ { + Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ - Inbound: []string{"mixed-in"}, - Outbound: "tuic-out", + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"mixed-in"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + + RouteOptions: option.RouteActionOptions{ + Outbound: "tuic-out", + }, + }, }, }, }, @@ -249,8 +267,16 @@ func TestECHHysteria2(t *testing.T) { { Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ - Inbound: []string{"mixed-in"}, - Outbound: "hy2-out", + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"mixed-in"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + + RouteOptions: option.RouteActionOptions{ + Outbound: "hy2-out", + }, + }, }, }, }, diff --git a/test/go.mod b/test/go.mod index 6caf6240..f9eefa16 100644 --- a/test/go.mod +++ b/test/go.mod @@ -1,25 +1,27 @@ module test -go 1.20 +go 1.23 + +toolchain go1.23.2 require github.com/sagernet/sing-box v0.0.0 replace github.com/sagernet/sing-box => ../ require ( - github.com/docker/docker v24.0.7+incompatible - github.com/docker/go-connections v0.4.0 - github.com/gofrs/uuid/v5 v5.2.0 - github.com/sagernet/quic-go v0.45.1-beta.2 - github.com/sagernet/sing v0.4.2 - github.com/sagernet/sing-dns v0.2.3 - github.com/sagernet/sing-quic v0.2.0-beta.12 + github.com/docker/docker v27.3.1+incompatible + github.com/docker/go-connections v0.5.0 + github.com/gofrs/uuid/v5 v5.3.0 + github.com/sagernet/quic-go v0.48.0-beta.1 + github.com/sagernet/sing v0.5.0-rc.4.0.20241022031908-cd17884118cb + github.com/sagernet/sing-dns v0.3.0-rc.2.0.20241021154031-a59e0fbba3ce + github.com/sagernet/sing-quic v0.3.0-rc.1 github.com/sagernet/sing-shadowsocks v0.2.7 github.com/sagernet/sing-shadowsocks2 v0.2.0 - github.com/spyzhov/ajson v0.9.0 + github.com/spyzhov/ajson v0.9.4 github.com/stretchr/testify v1.9.0 go.uber.org/goleak v1.3.0 - golang.org/x/net v0.25.0 + golang.org/x/net v0.30.0 ) require ( @@ -28,30 +30,38 @@ require ( github.com/andybalholm/brotli v1.0.6 // indirect github.com/caddyserver/certmagic v0.20.0 // indirect github.com/cloudflare/circl v1.3.7 // indirect + github.com/containerd/log v0.1.0 // indirect github.com/cretz/bine v0.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/distribution/reference v0.5.0 // indirect - github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/go-units v0.5.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/gaukas/godicttls v0.0.4 // indirect - github.com/go-chi/chi/v5 v5.0.12 // indirect + github.com/go-chi/chi/v5 v5.1.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gobwas/httphead v0.1.0 // indirect github.com/gobwas/pool v0.2.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/google/btree v1.1.2 // indirect + github.com/google/btree v1.1.3 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a // indirect - github.com/hashicorp/yamux v0.1.1 // indirect + github.com/hashicorp/yamux v0.1.2 // indirect + github.com/josharian/native v1.1.0 // indirect github.com/klauspost/compress v1.17.4 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/libdns/alidns v1.0.3 // indirect github.com/libdns/cloudflare v0.1.1 // indirect github.com/libdns/libdns v0.2.2 // indirect github.com/logrusorgru/aurora v2.0.3+incompatible // indirect + github.com/mdlayher/netlink v1.7.2 // indirect + github.com/mdlayher/socket v0.4.1 // indirect + github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa // indirect github.com/mholt/acmez v1.2.0 // indirect - github.com/miekg/dns v1.1.59 // indirect + github.com/miekg/dns v1.1.62 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/term v0.5.0 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/onsi/ginkgo/v2 v2.9.7 // indirect @@ -65,34 +75,41 @@ require ( github.com/quic-go/qtls-go1-20 v0.4.1 // indirect github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a // indirect github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 // indirect - github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f // indirect - github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba // indirect + github.com/sagernet/fswatch v0.1.1 // indirect + github.com/sagernet/gvisor v0.0.0-20241021032506-a4324256e4a3 // indirect + github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect + github.com/sagernet/nftables v0.3.0-beta.4 // indirect github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect - github.com/sagernet/sing-mux v0.2.0 // indirect + github.com/sagernet/sing-mux v0.2.1-0.20241020175909-fe6153f7a9ec // indirect github.com/sagernet/sing-shadowtls v0.1.4 // indirect - github.com/sagernet/sing-tun v0.3.2 // indirect + github.com/sagernet/sing-tun v0.4.0-rc.4.0.20241021153919-9ae45181180d // indirect github.com/sagernet/sing-vmess v0.1.12 // indirect github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect - github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6 // indirect - github.com/sagernet/utls v1.5.4 // indirect + github.com/sagernet/utls v1.6.7 // indirect github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8 // indirect github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 // indirect - github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect + github.com/vishvananda/netns v0.0.4 // indirect github.com/zeebo/blake3 v0.2.3 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect + go.opentelemetry.io/otel v1.31.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 // indirect + go.opentelemetry.io/otel/metric v1.31.0 // indirect + go.opentelemetry.io/otel/sdk v1.31.0 // indirect + go.opentelemetry.io/otel/trace v1.31.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect - golang.org/x/crypto v0.23.0 // indirect - golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect - golang.org/x/mod v0.18.0 // indirect - golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/text v0.16.0 // indirect - golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect - google.golang.org/grpc v1.63.2 // indirect - google.golang.org/protobuf v1.33.0 // indirect + golang.org/x/crypto v0.28.0 // indirect + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect + golang.org/x/mod v0.20.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/text v0.19.0 // indirect + golang.org/x/time v0.7.0 // indirect + golang.org/x/tools v0.24.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // indirect + google.golang.org/grpc v1.67.1 // indirect + google.golang.org/protobuf v1.35.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools/v3 v3.5.1 // indirect lukechampine.com/blake3 v1.3.0 // indirect diff --git a/test/go.sum b/test/go.sum index f482438e..e851ebd3 100644 --- a/test/go.sum +++ b/test/go.sum @@ -1,14 +1,19 @@ berty.tech/go-libtor v1.0.385 h1:RWK94C3hZj6Z2GdvePpHJLnWYobFr3bY/OdUJ5aoEXw= berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+fJEw= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc= github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NYacrNoZYiRM2jTg= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/cretz/bine v0.1.0/go.mod h1:6PF6fWAvYtwjRGkAuDEJeWNOv3a2hUouSP/yRYXmvHw= github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo= github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI= @@ -17,21 +22,23 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= -github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= -github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI= +github.com/docker/docker v27.3.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk= -github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI= -github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s= -github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= +github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= @@ -40,18 +47,26 @@ github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gofrs/uuid/v5 v5.2.0 h1:qw1GMx6/y8vhVsx626ImfKMuS5CvJmhIKKtuyvfajMM= -github.com/gofrs/uuid/v5 v5.2.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= +github.com/gofrs/uuid/v5 v5.3.0 h1:m0mUMr+oVYUdxpMLgSYCZiXe7PuVPnI94+OMeVBNedk= +github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= -github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk= github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= -github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= -github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= +github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= +github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= +github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= +github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= @@ -60,6 +75,7 @@ github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuOb github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/libdns/alidns v1.0.3 h1:LFHuGnbseq5+HCeGa1aW8awyX/4M2psB9962fdD2+yQ= github.com/libdns/alidns v1.0.3/go.mod h1:e18uAG6GanfRhcJj6/tps2rCMzQJaYVcGKT+ELjdjGE= github.com/libdns/cloudflare v0.1.1 h1:FVPfWwP8zZCqj268LZjmkDleXlHPlFU9KC4OJ3yn054= @@ -69,18 +85,28 @@ github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s= github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= +github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= +github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= +github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= +github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= +github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa h1:9mcjV+RGZVC3reJBNDjjNPyS8PmFG97zq56X7WNaFO4= +github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa/go.mod h1:4tLB5c8U0CxpkFM+AJJB77jEaVDbLH5XQvy42vAGsWw= github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30= github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE= -github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs= -github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk= +github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= +github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss= github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0= github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= +github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4= github.com/ooni/go-libtor v1.1.8 h1:Wo3V3DVTxl5vZdxtQakqYP+DAHx7pPtAFSl1bnAa08w= github.com/ooni/go-libtor v1.1.8/go.mod h1:q1YyLwRD9GeMyeerVvwc0vJ2YgwDLTp2bdVcrh/JXyI= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= @@ -101,53 +127,57 @@ github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkk github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM= github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 h1:YbmpqPQEMdlk9oFSKYWRqVuu9qzNiOayIonKmv1gCXY= github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1/go.mod h1:J2yAxTFPDjrDPhuAi9aWFz2L3ox9it4qAluBBbN0H5k= -github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f h1:NkhuupzH5ch7b/Y/6ZHJWrnNLoiNnSJaow6DPb8VW2I= -github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f/go.mod h1:KXmw+ouSJNOsuRpg4wgwwCQuunrGz4yoAqQjsLjc6N0= -github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba h1:EY5AS7CCtfmARNv2zXUOrsEMPFDGYxaw65JzA2p51Vk= -github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= -github.com/sagernet/quic-go v0.45.1-beta.2 h1:zkEeCbhdFFkrxKcuIRBtXNKci/1t2J/39QSG/sPvlmc= -github.com/sagernet/quic-go v0.45.1-beta.2/go.mod h1:+N3FqM9DAzOWfe64uxXuBejVJwX7DeW7BslzLO6N/xI= +github.com/sagernet/fswatch v0.1.1 h1:YqID+93B7VRfqIH3PArW/XpJv5H4OLEVWDfProGoRQs= +github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o= +github.com/sagernet/gvisor v0.0.0-20241021032506-a4324256e4a3 h1:RxEz7LhPNiF/gX/Hg+OXr5lqsM9iVAgmaK1L1vzlDRM= +github.com/sagernet/gvisor v0.0.0-20241021032506-a4324256e4a3/go.mod h1:ehZwnT2UpmOWAHFL48XdBhnd4Qu4hN2O3Ji0us3ZHMw= +github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis= +github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= +github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I= +github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8= +github.com/sagernet/quic-go v0.48.0-beta.1 h1:86hQZrmuoARI3BpDRkQaP0iAVpywA4YsRrzJPYuPKWg= +github.com/sagernet/quic-go v0.48.0-beta.1/go.mod h1:1WgdDIVD1Gybp40JTWketeSfKA/+or9YMLaG5VeTk4k= 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.4.2 h1:jzGNJdZVRI0xlAfFugsIQUPvyB9SuWvbJK7zQCXc4QM= -github.com/sagernet/sing v0.4.2/go.mod h1:ieZHA/+Y9YZfXs2I3WtuwgyCZ6GPsIR7HdKb1SdEnls= -github.com/sagernet/sing-dns v0.2.3 h1:YzeBUn2tR38F7HtvGEQ0kLRLmZWMEgi/+7wqa4Twb1k= -github.com/sagernet/sing-dns v0.2.3/go.mod h1:BJpJv6XLnrUbSyIntOT6DG9FW0f4fETmPAHvNjOprLg= -github.com/sagernet/sing-mux v0.2.0 h1:4C+vd8HztJCWNYfufvgL49xaOoOHXty2+EAjnzN3IYo= -github.com/sagernet/sing-mux v0.2.0/go.mod h1:khzr9AOPocLa+g53dBplwNDz4gdsyx/YM3swtAhlkHQ= -github.com/sagernet/sing-quic v0.2.0-beta.12 h1:BhvA5mmrDFEyDUQB5eeu+9UhF+ieyuNJ5Rsb0dAG3QY= -github.com/sagernet/sing-quic v0.2.0-beta.12/go.mod h1:YVpLfVi8BvYM7NMrjmnvcRm3E8iMETf1gFQmTQDN9jI= +github.com/sagernet/sing v0.5.0-rc.4.0.20241022031908-cd17884118cb h1:3IhGq2UmcbQfAcuqyE8RYKFapqEEa3eItS/MrZr+5l8= +github.com/sagernet/sing v0.5.0-rc.4.0.20241022031908-cd17884118cb/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= +github.com/sagernet/sing-dns v0.3.0-rc.2.0.20241021154031-a59e0fbba3ce h1:OfpxE5qnXMyU/9LtNgX4M7bGP11lJx4s+KZ3Sijb0HE= +github.com/sagernet/sing-dns v0.3.0-rc.2.0.20241021154031-a59e0fbba3ce/go.mod h1:TqLIelI+FAbVEdiTRolhGLOwvhVjY7oT+wezlOJUQ7M= +github.com/sagernet/sing-mux v0.2.1-0.20241020175909-fe6153f7a9ec h1:6Fd/VsEsw9qIjaGi1IBTZSb4b4v5JYtNcoiBtGsQC48= +github.com/sagernet/sing-mux v0.2.1-0.20241020175909-fe6153f7a9ec/go.mod h1:RSwqqHwbtTOX3vs6ms8vMtBGH/0ZNyLm/uwt6TlmR84= +github.com/sagernet/sing-quic v0.3.0-rc.1 h1:SlzL1yfEAKJyRduub8vzOVtbyTLAX7RZEEBZxO5utts= +github.com/sagernet/sing-quic v0.3.0-rc.1/go.mod h1:uX+aUHA0fgIN6U3WViseDpSdTQriViZ7qz0Wbsf1mNQ= github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8= github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE= github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg= github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ= github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k= github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4= -github.com/sagernet/sing-tun v0.3.2 h1:z0bLUT/YXH9RrJS9DsIpB0Bb9afl2hVJOmHd0zA3HJY= -github.com/sagernet/sing-tun v0.3.2/go.mod h1:DxLIyhjWU/HwGYoX0vNGg2c5QgTQIakphU1MuERR5tQ= +github.com/sagernet/sing-tun v0.4.0-rc.4.0.20241021153919-9ae45181180d h1:zWcIQM3eAKJGzy7zhqkO7zm7ZI890OdR4vSwx2mevS0= +github.com/sagernet/sing-tun v0.4.0-rc.4.0.20241021153919-9ae45181180d/go.mod h1:Xhv+Mz2nE7HZTwResni8EtTa7AMJz/S6uQLT5lV23M8= github.com/sagernet/sing-vmess v0.1.12 h1:2gFD8JJb+eTFMoa8FIVMnknEi+vCSfaiTXTfEYAYAPg= github.com/sagernet/sing-vmess v0.1.12/go.mod h1:luTSsfyBGAc9VhtCqwjR+dt1QgqBhuYBCONB/POhF8I= 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/tfo-go v0.0.0-20231209031829-7b5343ac1dc6 h1:z3SJQhVyU63FT26Wn/UByW6b7q8QKB0ZkPqsyqcz2PI= -github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6/go.mod h1:73xRZuxwkFk4aiLw28hG8W6o9cr2UPrGL9pdY2UTbvY= -github.com/sagernet/utls v1.5.4 h1:KmsEGbB2dKUtCNC+44NwAdNAqnqQ6GA4pTO0Yik56co= -github.com/sagernet/utls v1.5.4/go.mod h1:CTGxPWExIloRipK3XFpYv0OVyhO8kk3XCGW/ieyTh1s= +github.com/sagernet/utls v1.6.7 h1:Ep3+aJ8FUGGta+II2IEVNUc3EDhaRCZINWkj/LloIA8= +github.com/sagernet/utls v1.6.7/go.mod h1:Uua1TKO/FFuAhLr9rkaVnnrTmmiItzDjv1BUb2+ERwM= github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8 h1:R0OMYAScomNAVpTfbHFpxqJpvwuhxSRi+g6z7gZhABs= github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8/go.mod h1:K4J7/npM+VAMUeUmTa2JaA02JmyheP0GpRBOUvn3ecc= github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc= github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA= -github.com/spyzhov/ajson v0.9.0 h1:tF46gJGOenYVj+k9K1U1XpCxVWhmiyY5PsVCAs1+OJ0= -github.com/spyzhov/ajson v0.9.0/go.mod h1:a6oSw0MMb7Z5aD2tPoPO+jq11ETKgXUr2XktHdT8Wt8= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spyzhov/ajson v0.9.4 h1:MVibcTCgO7DY4IlskdqIlCmDOsUOZ9P7oKj8ifdcf84= +github.com/spyzhov/ajson v0.9.4/go.mod h1:a6oSw0MMb7Z5aD2tPoPO+jq11ETKgXUr2XktHdT8Wt8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg= -github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= +github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= @@ -156,6 +186,22 @@ github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg= github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ= github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM= +go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= +go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 h1:K0XaT3DwHAcV4nKLzcQvwAgSyisUghWoY20I7huthMk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0/go.mod h1:B5Ki776z/MBnVha1Nzwp5arlzBbE3+1jk+pGmaP5HME= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 h1:lUsI2TYsQw2r1IASwoROaCnjdj2cvC2+Jbxvk6nHnWU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0/go.mod h1:2HpZxxQurfGxJlJDblybejHB6RX6pmExPNe517hREw4= +go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE= +go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= +go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= +go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= +go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= +go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -169,31 +215,30 @@ golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaE golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY= -golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= -golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= +golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -201,35 +246,39 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= +golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= +golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= -google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= -google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 h1:T6rh4haD3GVYsgEfWExoCZA2o2FmbNyKpTuAxbEFPTg= +google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 h1:QCqS/PdaHTSWGvupk2F/ehwHtGc0/GYkT+3GAcR1CCc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/test/http_test.go b/test/http_test.go index 4b5fe70f..88385c27 100644 --- a/test/http_test.go +++ b/test/http_test.go @@ -49,9 +49,18 @@ func TestHTTPSelf(t *testing.T) { Route: &option.RouteOptions{ Rules: []option.Rule{ { + Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ - Inbound: []string{"mixed-in"}, - Outbound: "http-out", + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"mixed-in"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + + RouteOptions: option.RouteActionOptions{ + Outbound: "http-out", + }, + }, }, }, }, diff --git a/test/hysteria2_test.go b/test/hysteria2_test.go index f5494428..9ca2f5d3 100644 --- a/test/hysteria2_test.go +++ b/test/hysteria2_test.go @@ -94,8 +94,16 @@ func testHysteria2Self(t *testing.T, salamanderPassword string) { { Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ - Inbound: []string{"mixed-in"}, - Outbound: "hy2-out", + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"mixed-in"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + + RouteOptions: option.RouteActionOptions{ + Outbound: "hy2-out", + }, + }, }, }, }, diff --git a/test/hysteria_test.go b/test/hysteria_test.go index 90ff62dd..bde1b9fa 100644 --- a/test/hysteria_test.go +++ b/test/hysteria_test.go @@ -75,9 +75,18 @@ func TestHysteriaSelf(t *testing.T) { Route: &option.RouteOptions{ Rules: []option.Rule{ { + Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ - Inbound: []string{"mixed-in"}, - Outbound: "hy-out", + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"mixed-in"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + + RouteOptions: option.RouteActionOptions{ + Outbound: "hy-out", + }, + }, }, }, }, diff --git a/test/inbound_detour_test.go b/test/inbound_detour_test.go index c2ef57a5..9505c217 100644 --- a/test/inbound_detour_test.go +++ b/test/inbound_detour_test.go @@ -80,9 +80,18 @@ func TestChainedInbound(t *testing.T) { Route: &option.RouteOptions{ Rules: []option.Rule{ { + Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ - Inbound: []string{"mixed-in"}, - Outbound: "ss-out", + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"mixed-in"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + + RouteOptions: option.RouteActionOptions{ + Outbound: "ss-out", + }, + }, }, }, }, diff --git a/test/mux_cool_test.go b/test/mux_cool_test.go index ef47695a..81130fad 100644 --- a/test/mux_cool_test.go +++ b/test/mux_cool_test.go @@ -159,9 +159,18 @@ func TestMuxCoolSelf(t *testing.T) { Route: &option.RouteOptions{ Rules: []option.Rule{ { + Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ - Inbound: []string{"mixed-in"}, - Outbound: "vmess-out", + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"mixed-in"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + + RouteOptions: option.RouteActionOptions{ + Outbound: "vmess-out", + }, + }, }, }, }, diff --git a/test/mux_test.go b/test/mux_test.go index c02f2708..8d755185 100644 --- a/test/mux_test.go +++ b/test/mux_test.go @@ -102,9 +102,18 @@ func testShadowsocksMux(t *testing.T, options option.OutboundMultiplexOptions) { Route: &option.RouteOptions{ Rules: []option.Rule{ { + Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ - Inbound: []string{"mixed-in"}, - Outbound: "ss-out", + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"mixed-in"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + + RouteOptions: option.RouteActionOptions{ + Outbound: "ss-out", + }, + }, }, }, }, @@ -166,9 +175,18 @@ func testVMessMux(t *testing.T, options option.OutboundMultiplexOptions) { Route: &option.RouteOptions{ Rules: []option.Rule{ { + Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ - Inbound: []string{"mixed-in"}, - Outbound: "vmess-out", + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"mixed-in"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + + RouteOptions: option.RouteActionOptions{ + Outbound: "vmess-out", + }, + }, }, }, }, diff --git a/test/shadowsocks_test.go b/test/shadowsocks_test.go index 7d063d9a..4ef1ee9d 100644 --- a/test/shadowsocks_test.go +++ b/test/shadowsocks_test.go @@ -197,9 +197,18 @@ func testShadowsocksSelf(t *testing.T, method string, password string) { Route: &option.RouteOptions{ Rules: []option.Rule{ { + Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ - Inbound: []string{"mixed-in"}, - Outbound: "ss-out", + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"mixed-in"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + + RouteOptions: option.RouteActionOptions{ + Outbound: "ss-out", + }, + }, }, }, }, @@ -258,9 +267,18 @@ func TestShadowsocksUoT(t *testing.T) { Route: &option.RouteOptions{ Rules: []option.Rule{ { + Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ - Inbound: []string{"mixed-in"}, - Outbound: "ss-out", + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"mixed-in"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + + RouteOptions: option.RouteActionOptions{ + Outbound: "ss-out", + }, + }, }, }, }, @@ -319,9 +337,18 @@ func testShadowsocks2022EIH(t *testing.T, method string, password string) { Route: &option.RouteOptions{ Rules: []option.Rule{ { + Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ - Inbound: []string{"mixed-in"}, - Outbound: "ss-out", + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"mixed-in"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + + RouteOptions: option.RouteActionOptions{ + Outbound: "ss-out", + }, + }, }, }, }, diff --git a/test/shadowtls_test.go b/test/shadowtls_test.go index 2f53d46a..6f9ee1e5 100644 --- a/test/shadowtls_test.go +++ b/test/shadowtls_test.go @@ -118,12 +118,23 @@ func testShadowTLS(t *testing.T, version int, password string, utlsEanbled bool) }, }, Route: &option.RouteOptions{ - Rules: []option.Rule{{ - DefaultOptions: option.DefaultRule{ - Inbound: []string{"detour"}, - Outbound: "direct", + Rules: []option.Rule{ + { + Type: C.RuleTypeDefault, + DefaultOptions: option.DefaultRule{ + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"detour"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + + RouteOptions: option.RouteActionOptions{ + Outbound: "direct", + }, + }, + }, }, - }}, + }, }, }) testTCP(t, clientPort, testPort) @@ -239,12 +250,23 @@ func TestShadowTLSInbound(t *testing.T) { }, }, Route: &option.RouteOptions{ - Rules: []option.Rule{{ - DefaultOptions: option.DefaultRule{ - Inbound: []string{"in"}, - Outbound: "out", + Rules: []option.Rule{ + { + Type: C.RuleTypeDefault, + DefaultOptions: option.DefaultRule{ + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"in"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + + RouteOptions: option.RouteActionOptions{ + Outbound: "out", + }, + }, + }, }, - }}, + }, }, }) testTCP(t, clientPort, testPort) @@ -319,12 +341,23 @@ func TestShadowTLSOutbound(t *testing.T) { }, }, Route: &option.RouteOptions{ - Rules: []option.Rule{{ - DefaultOptions: option.DefaultRule{ - Inbound: []string{"detour"}, - Outbound: "direct", + Rules: []option.Rule{ + { + Type: C.RuleTypeDefault, + DefaultOptions: option.DefaultRule{ + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"detour"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + + RouteOptions: option.RouteActionOptions{ + Outbound: "direct", + }, + }, + }, }, - }}, + }, }, }) testTCP(t, clientPort, testPort) diff --git a/test/tfo_test.go b/test/tfo_test.go index cc97e189..7bd34e2d 100644 --- a/test/tfo_test.go +++ b/test/tfo_test.go @@ -60,9 +60,18 @@ func TestTCPSlowOpen(t *testing.T) { Route: &option.RouteOptions{ Rules: []option.Rule{ { + Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ - Inbound: []string{"mixed-in"}, - Outbound: "ss-out", + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"mixed-in"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + + RouteOptions: option.RouteActionOptions{ + Outbound: "ss-out", + }, + }, }, }, }, diff --git a/test/tls_test.go b/test/tls_test.go index da55faf5..cfc6c1a5 100644 --- a/test/tls_test.go +++ b/test/tls_test.go @@ -76,9 +76,18 @@ func TestUTLS(t *testing.T) { Route: &option.RouteOptions{ Rules: []option.Rule{ { + Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ - Inbound: []string{"mixed-in"}, - Outbound: "trojan-out", + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"mixed-in"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + + RouteOptions: option.RouteActionOptions{ + Outbound: "trojan-out", + }, + }, }, }, }, diff --git a/test/trojan_test.go b/test/trojan_test.go index d8659b2e..f88ec885 100644 --- a/test/trojan_test.go +++ b/test/trojan_test.go @@ -118,9 +118,18 @@ func TestTrojanSelf(t *testing.T) { Route: &option.RouteOptions{ Rules: []option.Rule{ { + Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ - Inbound: []string{"mixed-in"}, - Outbound: "trojan-out", + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"mixed-in"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + + RouteOptions: option.RouteActionOptions{ + Outbound: "trojan-out", + }, + }, }, }, }, @@ -177,9 +186,18 @@ func TestTrojanPlainSelf(t *testing.T) { Route: &option.RouteOptions{ Rules: []option.Rule{ { + Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ - Inbound: []string{"mixed-in"}, - Outbound: "trojan-out", + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"mixed-in"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + + RouteOptions: option.RouteActionOptions{ + Outbound: "trojan-out", + }, + }, }, }, }, diff --git a/test/tuic_test.go b/test/tuic_test.go index c2b71111..5b838f22 100644 --- a/test/tuic_test.go +++ b/test/tuic_test.go @@ -90,9 +90,18 @@ func testTUICSelf(t *testing.T, udpStream bool, zeroRTTHandshake bool) { Route: &option.RouteOptions{ Rules: []option.Rule{ { + Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ - Inbound: []string{"mixed-in"}, - Outbound: "tuic-out", + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"mixed-in"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + + RouteOptions: option.RouteActionOptions{ + Outbound: "tuic-out", + }, + }, }, }, }, diff --git a/test/v2ray_transport_test.go b/test/v2ray_transport_test.go index 2f39d18a..c7362f34 100644 --- a/test/v2ray_transport_test.go +++ b/test/v2ray_transport_test.go @@ -108,9 +108,18 @@ func testVMessTransportSelf(t *testing.T, server *option.V2RayTransportOptions, Route: &option.RouteOptions{ Rules: []option.Rule{ { + Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ - Inbound: []string{"mixed-in"}, - Outbound: "vmess-out", + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"mixed-in"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + + RouteOptions: option.RouteActionOptions{ + Outbound: "vmess-out", + }, + }, }, }, }, @@ -187,9 +196,18 @@ func testTrojanTransportSelf(t *testing.T, server *option.V2RayTransportOptions, Route: &option.RouteOptions{ Rules: []option.Rule{ { + Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ - Inbound: []string{"mixed-in"}, - Outbound: "vmess-out", + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"mixed-in"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + + RouteOptions: option.RouteActionOptions{ + Outbound: "vmess-out", + }, + }, }, }, }, @@ -270,9 +288,18 @@ func TestVMessQUICSelf(t *testing.T) { Route: &option.RouteOptions{ Rules: []option.Rule{ { + Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ - Inbound: []string{"mixed-in"}, - Outbound: "vmess-out", + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"mixed-in"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + + RouteOptions: option.RouteActionOptions{ + Outbound: "vmess-out", + }, + }, }, }, }, @@ -334,9 +361,18 @@ func testV2RayTransportNOTLSSelf(t *testing.T, transport *option.V2RayTransportO Route: &option.RouteOptions{ Rules: []option.Rule{ { + Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ - Inbound: []string{"mixed-in"}, - Outbound: "vmess-out", + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"mixed-in"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + + RouteOptions: option.RouteActionOptions{ + Outbound: "vmess-out", + }, + }, }, }, }, diff --git a/test/vmess_test.go b/test/vmess_test.go index cc7879ab..fcf7bf8f 100644 --- a/test/vmess_test.go +++ b/test/vmess_test.go @@ -315,9 +315,17 @@ func testVMessSelf(t *testing.T, security string, alterId int, globalPadding boo Route: &option.RouteOptions{ Rules: []option.Rule{ { + Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ - Inbound: []string{"mixed-in"}, - Outbound: "vmess-out", + RawDefaultRule: option.RawDefaultRule{ + Inbound: []string{"mixed-in"}, + }, + RuleAction: option.RuleAction{ + Action: C.RuleActionTypeRoute, + RouteOptions: option.RouteActionOptions{ + Outbound: "vmess-out", + }, + }, }, }, }, diff --git a/transport/v2ray/grpc.go b/transport/v2ray/grpc.go index 05bc5a2a..1b4250ad 100644 --- a/transport/v2ray/grpc.go +++ b/transport/v2ray/grpc.go @@ -10,15 +10,16 @@ import ( "github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/transport/v2raygrpc" "github.com/sagernet/sing-box/transport/v2raygrpclite" + "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" ) -func NewGRPCServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (adapter.V2RayServerTransport, error) { +func NewGRPCServer(ctx context.Context, logger logger.ContextLogger, options option.V2RayGRPCOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (adapter.V2RayServerTransport, error) { if options.ForceLite { - return v2raygrpclite.NewServer(ctx, options, tlsConfig, handler) + return v2raygrpclite.NewServer(ctx, logger, options, tlsConfig, handler) } - return v2raygrpc.NewServer(ctx, options, tlsConfig, handler) + return v2raygrpc.NewServer(ctx, logger, options, tlsConfig, handler) } func NewGRPCClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayGRPCOptions, tlsConfig tls.Config) (adapter.V2RayClientTransport, error) { diff --git a/transport/v2ray/grpc_lite.go b/transport/v2ray/grpc_lite.go index 94f6fad1..4f2814a7 100644 --- a/transport/v2ray/grpc_lite.go +++ b/transport/v2ray/grpc_lite.go @@ -9,12 +9,13 @@ import ( "github.com/sagernet/sing-box/common/tls" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/transport/v2raygrpclite" + "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" ) -func NewGRPCServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (adapter.V2RayServerTransport, error) { - return v2raygrpclite.NewServer(ctx, options, tlsConfig, handler) +func NewGRPCServer(ctx context.Context, logger logger.ContextLogger, options option.V2RayGRPCOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (adapter.V2RayServerTransport, error) { + return v2raygrpclite.NewServer(ctx, logger, options, tlsConfig, handler) } func NewGRPCClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayGRPCOptions, tlsConfig tls.Config) (adapter.V2RayClientTransport, error) { diff --git a/transport/v2ray/quic.go b/transport/v2ray/quic.go index 5471157a..4d3cdc6f 100644 --- a/transport/v2ray/quic.go +++ b/transport/v2ray/quic.go @@ -7,6 +7,7 @@ import ( "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/tls" "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" ) @@ -21,11 +22,11 @@ func RegisterQUICConstructor(server ServerConstructor[option.V2RayQUICOptions], quicClientConstructor = client } -func NewQUICServer(ctx context.Context, options option.V2RayQUICOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (adapter.V2RayServerTransport, error) { +func NewQUICServer(ctx context.Context, logger logger.ContextLogger, options option.V2RayQUICOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (adapter.V2RayServerTransport, error) { if quicServerConstructor == nil { return nil, os.ErrInvalid } - return quicServerConstructor(ctx, options, tlsConfig, handler) + return quicServerConstructor(ctx, logger, options, tlsConfig, handler) } func NewQUICClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options option.V2RayQUICOptions, tlsConfig tls.Config) (adapter.V2RayClientTransport, error) { diff --git a/transport/v2ray/transport.go b/transport/v2ray/transport.go index deb8a7f0..ab52f55e 100644 --- a/transport/v2ray/transport.go +++ b/transport/v2ray/transport.go @@ -11,33 +11,34 @@ import ( "github.com/sagernet/sing-box/transport/v2rayhttpupgrade" "github.com/sagernet/sing-box/transport/v2raywebsocket" 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" ) type ( - ServerConstructor[O any] func(ctx context.Context, options O, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (adapter.V2RayServerTransport, error) + ServerConstructor[O any] func(ctx context.Context, logger logger.ContextLogger, options O, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (adapter.V2RayServerTransport, error) ClientConstructor[O any] func(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, options O, tlsConfig tls.Config) (adapter.V2RayClientTransport, error) ) -func NewServerTransport(ctx context.Context, options option.V2RayTransportOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (adapter.V2RayServerTransport, error) { +func NewServerTransport(ctx context.Context, logger logger.ContextLogger, options option.V2RayTransportOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (adapter.V2RayServerTransport, error) { if options.Type == "" { return nil, nil } switch options.Type { case C.V2RayTransportTypeHTTP: - return v2rayhttp.NewServer(ctx, options.HTTPOptions, tlsConfig, handler) + return v2rayhttp.NewServer(ctx, logger, options.HTTPOptions, tlsConfig, handler) case C.V2RayTransportTypeWebsocket: - return v2raywebsocket.NewServer(ctx, options.WebsocketOptions, tlsConfig, handler) + return v2raywebsocket.NewServer(ctx, logger, options.WebsocketOptions, tlsConfig, handler) case C.V2RayTransportTypeQUIC: if tlsConfig == nil { return nil, C.ErrTLSRequired } - return NewQUICServer(ctx, options.QUICOptions, tlsConfig, handler) + return NewQUICServer(ctx, logger, options.QUICOptions, tlsConfig, handler) case C.V2RayTransportTypeGRPC: - return NewGRPCServer(ctx, options.GRPCOptions, tlsConfig, handler) + return NewGRPCServer(ctx, logger, options.GRPCOptions, tlsConfig, handler) case C.V2RayTransportTypeHTTPUpgrade: - return v2rayhttpupgrade.NewServer(ctx, options.HTTPUpgradeOptions, tlsConfig, handler) + return v2rayhttpupgrade.NewServer(ctx, logger, options.HTTPUpgradeOptions, tlsConfig, handler) default: return nil, E.New("unknown transport type: " + options.Type) } diff --git a/transport/v2raygrpc/client.go b/transport/v2raygrpc/client.go index 1e72040a..af922b45 100644 --- a/transport/v2raygrpc/client.go +++ b/transport/v2raygrpc/client.go @@ -105,7 +105,7 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) { cancel(err) return nil, err } - return NewGRPCConn(stream, cancel), nil + return NewGRPCConn(stream), nil } func (c *Client) Close() error { diff --git a/transport/v2raygrpc/conn.go b/transport/v2raygrpc/conn.go index bc78f91e..0a0a627f 100644 --- a/transport/v2raygrpc/conn.go +++ b/transport/v2raygrpc/conn.go @@ -5,7 +5,6 @@ import ( "os" "time" - "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/baderror" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" @@ -15,17 +14,15 @@ var _ net.Conn = (*GRPCConn)(nil) type GRPCConn struct { GunService - cancel common.ContextCancelCauseFunc - cache []byte + cache []byte } -func NewGRPCConn(service GunService, cancel common.ContextCancelCauseFunc) *GRPCConn { +func NewGRPCConn(service GunService) *GRPCConn { if client, isClient := service.(GunService_TunClient); isClient { service = &clientConnWrapper{client} } return &GRPCConn{ GunService: service, - cancel: cancel, } } @@ -38,7 +35,6 @@ func (c *GRPCConn) Read(b []byte) (n int, err error) { hunk, err := c.Recv() err = baderror.WrapGRPC(err) if err != nil { - c.cancel(err) return } n = copy(b, hunk.Data) @@ -51,14 +47,12 @@ func (c *GRPCConn) Read(b []byte) (n int, err error) { func (c *GRPCConn) Write(b []byte) (n int, err error) { err = baderror.WrapGRPC(c.Send(&Hunk{Data: b})) if err != nil { - c.cancel(err) return } return len(b), nil } func (c *GRPCConn) Close() error { - c.cancel(net.ErrClosed) return nil } diff --git a/transport/v2raygrpc/server.go b/transport/v2raygrpc/server.go index 15088b26..b6b13f82 100644 --- a/transport/v2raygrpc/server.go +++ b/transport/v2raygrpc/server.go @@ -9,8 +9,10 @@ import ( "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/tls" + "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing/common" + "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" @@ -25,11 +27,12 @@ var _ adapter.V2RayServerTransport = (*Server)(nil) type Server struct { ctx context.Context - handler N.TCPConnectionHandler + logger logger.ContextLogger + handler adapter.V2RayServerTransportHandler server *grpc.Server } -func NewServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig tls.ServerConfig, handler N.TCPConnectionHandler) (*Server, error) { +func NewServer(ctx context.Context, logger logger.ContextLogger, options option.V2RayGRPCOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (*Server, error) { var serverOptions []grpc.ServerOption if tlsConfig != nil { if !common.Contains(tlsConfig.NextProtos(), http2.NextProtoTLS) { @@ -43,17 +46,16 @@ func NewServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig t Timeout: time.Duration(options.PingTimeout), })) } - server := &Server{ctx, handler, grpc.NewServer(serverOptions...)} + server := &Server{ctx, logger, handler, grpc.NewServer(serverOptions...)} RegisterGunServiceCustomNameServer(server.server, server, options.ServiceName) return server, nil } func (s *Server) Tun(server GunService_TunServer) error { - ctx, cancel := common.ContextWithCancelCause(s.ctx) - conn := NewGRPCConn(server, cancel) - var metadata M.Metadata + conn := NewGRPCConn(server) + var source M.Socksaddr if remotePeer, loaded := peer.FromContext(server.Context()); loaded { - metadata.Source = M.SocksaddrFromNet(remotePeer.Addr) + source = M.SocksaddrFromNet(remotePeer.Addr) } if grpcMetadata, loaded := gM.FromIncomingContext(server.Context()); loaded { forwardFrom := strings.Join(grpcMetadata.Get("X-Forwarded-For"), ",") @@ -61,13 +63,16 @@ func (s *Server) Tun(server GunService_TunServer) error { for _, from := range strings.Split(forwardFrom, ",") { originAddr := M.ParseSocksaddr(from) if originAddr.IsValid() { - metadata.Source = originAddr.Unwrap() + source = originAddr.Unwrap() } } } } - go s.handler.NewConnection(ctx, conn, metadata) - <-ctx.Done() + done := make(chan struct{}) + go s.handler.NewConnectionEx(log.ContextWithNewID(s.ctx), conn, source, M.Socksaddr{}, N.OnceClose(func(it error) { + close(done) + })) + <-done return nil } diff --git a/transport/v2raygrpclite/server.go b/transport/v2raygrpclite/server.go index 6d3e42eb..622d785a 100644 --- a/transport/v2raygrpclite/server.go +++ b/transport/v2raygrpclite/server.go @@ -10,10 +10,12 @@ import ( "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/tls" + "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/transport/v2rayhttp" "github.com/sagernet/sing/common" 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" aTLS "github.com/sagernet/sing/common/tls" @@ -26,18 +28,19 @@ import ( var _ adapter.V2RayServerTransport = (*Server)(nil) type Server struct { - tlsConfig tls.ServerConfig - handler adapter.V2RayServerTransportHandler - errorHandler E.Handler - httpServer *http.Server - h2Server *http2.Server - h2cHandler http.Handler - path string + tlsConfig tls.ServerConfig + logger logger.ContextLogger + handler adapter.V2RayServerTransportHandler + httpServer *http.Server + h2Server *http2.Server + h2cHandler http.Handler + path string } -func NewServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (*Server, error) { +func NewServer(ctx context.Context, logger logger.ContextLogger, options option.V2RayGRPCOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (*Server, error) { server := &Server{ tlsConfig: tlsConfig, + logger: logger, handler: handler, path: "/" + options.ServiceName + "/Tun", h2Server: &http2.Server{ @@ -49,6 +52,9 @@ func NewServer(ctx context.Context, options option.V2RayGRPCOptions, tlsConfig t BaseContext: func(net.Listener) context.Context { return ctx }, + ConnContext: func(ctx context.Context, c net.Conn) context.Context { + return log.ContextWithNewID(ctx) + }, } server.h2cHandler = h2c.NewHandler(server, server.h2Server) return server, nil @@ -74,10 +80,12 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) { writer.Header().Set("Content-Type", "application/grpc") writer.Header().Set("TE", "trailers") writer.WriteHeader(http.StatusOK) - var metadata M.Metadata - metadata.Source = sHttp.SourceAddress(request) + done := make(chan struct{}) conn := v2rayhttp.NewHTTP2Wrapper(newGunConn(request.Body, writer, writer.(http.Flusher))) - s.handler.NewConnection(request.Context(), conn, metadata) + s.handler.NewConnectionEx(request.Context(), conn, sHttp.SourceAddress(request), M.Socksaddr{}, N.OnceClose(func(it error) { + close(done) + })) + <-done conn.CloseWrapper() } @@ -85,7 +93,7 @@ func (s *Server) invalidRequest(writer http.ResponseWriter, request *http.Reques if statusCode > 0 { writer.WriteHeader(statusCode) } - s.handler.NewError(request.Context(), E.Cause(err, "process connection from ", request.RemoteAddr)) + s.logger.ErrorContext(request.Context(), E.Cause(err, "process connection from ", request.RemoteAddr)) } func (s *Server) Network() []string { diff --git a/transport/v2rayhttp/server.go b/transport/v2rayhttp/server.go index cad7d906..e0ee42a7 100644 --- a/transport/v2rayhttp/server.go +++ b/transport/v2rayhttp/server.go @@ -11,11 +11,13 @@ import ( "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/tls" C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing/common" "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" aTLS "github.com/sagernet/sing/common/tls" @@ -29,6 +31,7 @@ var _ adapter.V2RayServerTransport = (*Server)(nil) type Server struct { ctx context.Context + logger logger.ContextLogger tlsConfig tls.ServerConfig handler adapter.V2RayServerTransportHandler httpServer *http.Server @@ -40,7 +43,7 @@ type Server struct { headers http.Header } -func NewServer(ctx context.Context, options option.V2RayHTTPOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (*Server, error) { +func NewServer(ctx context.Context, logger logger.ContextLogger, options option.V2RayHTTPOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (*Server, error) { server := &Server{ ctx: ctx, tlsConfig: tlsConfig, @@ -63,6 +66,9 @@ func NewServer(ctx context.Context, options option.V2RayHTTPOptions, tlsConfig t BaseContext: func(net.Listener) context.Context { return ctx }, + ConnContext: func(ctx context.Context, c net.Conn) context.Context { + return log.ContextWithNewID(ctx) + }, } server.h2cHandler = h2c.NewHandler(server, server.h2Server) return server, nil @@ -95,8 +101,7 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) { } } - var metadata M.Metadata - metadata.Source = sHttp.SourceAddress(request) + source := sHttp.SourceAddress(request) if h, ok := writer.(http.Hijacker); ok { var requestBody *buf.Buffer if contentLength := int(request.ContentLength); contentLength > 0 { @@ -127,14 +132,18 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) { if requestBody != nil { conn = bufio.NewCachedConn(conn, requestBody) } - s.handler.NewConnection(request.Context(), conn, metadata) + s.handler.NewConnectionEx(request.Context(), conn, source, M.Socksaddr{}, nil) } else { writer.WriteHeader(http.StatusOK) + done := make(chan struct{}) conn := NewHTTP2Wrapper(&ServerHTTPConn{ NewHTTPConn(request.Body, writer), writer.(http.Flusher), }) - s.handler.NewConnection(request.Context(), conn, metadata) + s.handler.NewConnectionEx(request.Context(), conn, source, M.Socksaddr{}, N.OnceClose(func(it error) { + close(done) + })) + <-done conn.CloseWrapper() } } @@ -143,7 +152,7 @@ func (s *Server) invalidRequest(writer http.ResponseWriter, request *http.Reques if statusCode > 0 { writer.WriteHeader(statusCode) } - s.handler.NewError(request.Context(), E.Cause(err, "process connection from ", request.RemoteAddr)) + s.logger.ErrorContext(request.Context(), E.Cause(err, "process connection from ", request.RemoteAddr)) } func (s *Server) Network() []string { diff --git a/transport/v2rayhttpupgrade/server.go b/transport/v2rayhttpupgrade/server.go index a3b5d23e..6a42912e 100644 --- a/transport/v2rayhttpupgrade/server.go +++ b/transport/v2rayhttpupgrade/server.go @@ -10,9 +10,11 @@ import ( "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/tls" C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing/common" 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" aTLS "github.com/sagernet/sing/common/tls" @@ -23,6 +25,7 @@ var _ adapter.V2RayServerTransport = (*Server)(nil) type Server struct { ctx context.Context + logger logger.ContextLogger tlsConfig tls.ServerConfig handler adapter.V2RayServerTransportHandler httpServer *http.Server @@ -31,7 +34,7 @@ type Server struct { headers http.Header } -func NewServer(ctx context.Context, options option.V2RayHTTPUpgradeOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (*Server, error) { +func NewServer(ctx context.Context, logger logger.ContextLogger, options option.V2RayHTTPUpgradeOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (*Server, error) { server := &Server{ ctx: ctx, tlsConfig: tlsConfig, @@ -50,6 +53,9 @@ func NewServer(ctx context.Context, options option.V2RayHTTPUpgradeOptions, tlsC BaseContext: func(net.Listener) context.Context { return ctx }, + ConnContext: func(ctx context.Context, c net.Conn) context.Context { + return log.ContextWithNewID(ctx) + }, TLSNextProto: make(map[string]func(*http.Server, *tls.STDConn, http.Handler)), } return server, nil @@ -104,16 +110,14 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) { s.invalidRequest(writer, request, http.StatusInternalServerError, E.Cause(err, "hijack failed")) return } - var metadata M.Metadata - metadata.Source = sHttp.SourceAddress(request) - s.handler.NewConnection(request.Context(), conn, metadata) + s.handler.NewConnectionEx(request.Context(), conn, sHttp.SourceAddress(request), M.Socksaddr{}, nil) } func (s *Server) invalidRequest(writer http.ResponseWriter, request *http.Request, statusCode int, err error) { if statusCode > 0 { writer.WriteHeader(statusCode) } - s.handler.NewError(request.Context(), E.Cause(err, "process connection from ", request.RemoteAddr)) + s.logger.ErrorContext(request.Context(), E.Cause(err, "process connection from ", request.RemoteAddr)) } func (s *Server) Network() []string { diff --git a/transport/v2rayquic/server.go b/transport/v2rayquic/server.go index f7721030..4c4397e6 100644 --- a/transport/v2rayquic/server.go +++ b/transport/v2rayquic/server.go @@ -15,6 +15,8 @@ import ( "github.com/sagernet/sing-box/option" "github.com/sagernet/sing-quic" "github.com/sagernet/sing/common" + 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" ) @@ -23,6 +25,7 @@ var _ adapter.V2RayServerTransport = (*Server)(nil) type Server struct { ctx context.Context + logger logger.ContextLogger tlsConfig tls.ServerConfig quicConfig *quic.Config handler adapter.V2RayServerTransportHandler @@ -30,7 +33,7 @@ type Server struct { quicListener qtls.Listener } -func NewServer(ctx context.Context, options option.V2RayQUICOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (adapter.V2RayServerTransport, error) { +func NewServer(ctx context.Context, logger logger.ContextLogger, options option.V2RayQUICOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (adapter.V2RayServerTransport, error) { quicConfig := &quic.Config{ DisablePathMTUDiscovery: !C.IsLinux && !C.IsWindows, } @@ -39,6 +42,7 @@ func NewServer(ctx context.Context, options option.V2RayQUICOptions, tlsConfig t } server := &Server{ ctx: ctx, + logger: logger, tlsConfig: tlsConfig, quicConfig: quicConfig, handler: handler, @@ -73,8 +77,8 @@ func (s *Server) acceptLoop() { } go func() { hErr := s.streamAcceptLoop(conn) - if hErr != nil { - s.handler.NewError(conn.Context(), hErr) + if hErr != nil && !E.IsClosedOrCanceled(hErr) { + s.logger.ErrorContext(conn.Context(), hErr) } }() } @@ -86,7 +90,7 @@ func (s *Server) streamAcceptLoop(conn quic.Connection) error { if err != nil { return err } - go s.handler.NewConnection(conn.Context(), &StreamWrapper{Conn: conn, Stream: stream}, M.Metadata{}) + go s.handler.NewConnectionEx(conn.Context(), &StreamWrapper{Conn: conn, Stream: stream}, M.SocksaddrFromNet(conn.RemoteAddr()), M.Socksaddr{}, nil) } } diff --git a/transport/v2raywebsocket/server.go b/transport/v2raywebsocket/server.go index 86f2de9c..ccabf086 100644 --- a/transport/v2raywebsocket/server.go +++ b/transport/v2raywebsocket/server.go @@ -11,11 +11,13 @@ import ( "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/tls" C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing/common" "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" aTLS "github.com/sagernet/sing/common/tls" @@ -27,6 +29,7 @@ var _ adapter.V2RayServerTransport = (*Server)(nil) type Server struct { ctx context.Context + logger logger.ContextLogger tlsConfig tls.ServerConfig handler adapter.V2RayServerTransportHandler httpServer *http.Server @@ -36,9 +39,10 @@ type Server struct { upgrader ws.HTTPUpgrader } -func NewServer(ctx context.Context, options option.V2RayWebsocketOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (*Server, error) { +func NewServer(ctx context.Context, logger logger.ContextLogger, options option.V2RayWebsocketOptions, tlsConfig tls.ServerConfig, handler adapter.V2RayServerTransportHandler) (*Server, error) { server := &Server{ ctx: ctx, + logger: logger, tlsConfig: tlsConfig, handler: handler, path: options.Path, @@ -59,6 +63,9 @@ func NewServer(ctx context.Context, options option.V2RayWebsocketOptions, tlsCon BaseContext: func(net.Listener) context.Context { return ctx }, + ConnContext: func(ctx context.Context, c net.Conn) context.Context { + return log.ContextWithNewID(ctx) + }, } return server, nil } @@ -102,20 +109,19 @@ func (s *Server) ServeHTTP(writer http.ResponseWriter, request *http.Request) { s.invalidRequest(writer, request, 0, E.Cause(err, "upgrade websocket connection")) return } - var metadata M.Metadata - metadata.Source = sHttp.SourceAddress(request) - conn = NewConn(wsConn, metadata.Source.TCPAddr(), ws.StateServerSide) + source := sHttp.SourceAddress(request) + conn = NewConn(wsConn, source, ws.StateServerSide) if len(earlyData) > 0 { conn = bufio.NewCachedConn(conn, buf.As(earlyData)) } - s.handler.NewConnection(request.Context(), conn, metadata) + s.handler.NewConnectionEx(request.Context(), conn, source, M.Socksaddr{}, nil) } func (s *Server) invalidRequest(writer http.ResponseWriter, request *http.Request, statusCode int, err error) { if statusCode > 0 { writer.WriteHeader(statusCode) } - s.handler.NewError(request.Context(), E.Cause(err, "process connection from ", request.RemoteAddr)) + s.logger.ErrorContext(request.Context(), E.Cause(err, "process connection from ", request.RemoteAddr)) } func (s *Server) Network() []string {