diff --git a/go.mod b/go.mod index fca179a4..5ebdd34c 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,7 @@ require ( github.com/sagernet/sing-shadowsocks v0.2.8 github.com/sagernet/sing-shadowsocks2 v0.2.1 github.com/sagernet/sing-shadowtls v0.2.0 - github.com/sagernet/sing-tun v0.6.8 + github.com/sagernet/sing-tun v0.6.9 github.com/sagernet/sing-vmess v0.2.3 github.com/sagernet/smux v1.5.34-mod.2 github.com/sagernet/utls v1.6.7 diff --git a/go.sum b/go.sum index 55f165d4..55b2f980 100644 --- a/go.sum +++ b/go.sum @@ -133,8 +133,8 @@ github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnq github.com/sagernet/sing-shadowsocks2 v0.2.1/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ= github.com/sagernet/sing-shadowtls v0.2.0 h1:cLKe4OAOFwuhmAIuPLj//CIL7Q9js+pIDardhJ+/osk= github.com/sagernet/sing-shadowtls v0.2.0/go.mod h1:agU+Fw5X+xnWVyRHyFthoZCX3MfWKCFPm4JUf+1oaxo= -github.com/sagernet/sing-tun v0.6.8 h1:tr+LKHe09C2I9GfNuB2vnzaZm+ekoNlAhLLrdiLjtAA= -github.com/sagernet/sing-tun v0.6.8/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE= +github.com/sagernet/sing-tun v0.6.9 h1:uP8O4Q7U9QesjWumgxd2S9fjT3c6aEPWl5RB6uBdVB8= +github.com/sagernet/sing-tun v0.6.9/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE= github.com/sagernet/sing-vmess v0.2.3 h1:z6Ym8dnZG7k1fP3+54vz8G0tvRVJeOoTFFeUPwXTD44= github.com/sagernet/sing-vmess v0.2.3/go.mod h1:jDAZ0A0St1zVRkyvhAPRySOFfhC+4SQtO5VYyeFotgA= github.com/sagernet/smux v1.5.34-mod.2 h1:gkmBjIjlJ2zQKpLigOkFur5kBKdV6bNRoFu2WkltRQ4= diff --git a/route/dns.go b/route/dns.go index 8d57c646..6545ce04 100644 --- a/route/dns.go +++ b/route/dns.go @@ -2,15 +2,14 @@ package route import ( "context" - "errors" "net" "time" "github.com/sagernet/sing-box/adapter" C "github.com/sagernet/sing-box/constant" dnsOutbound "github.com/sagernet/sing-box/protocol/dns" + R "github.com/sagernet/sing-box/route/rule" "github.com/sagernet/sing-dns" - "github.com/sagernet/sing-tun" "github.com/sagernet/sing/common/buf" E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" @@ -58,7 +57,7 @@ func (r *Router) hijackDNSPacket(ctx context.Context, conn N.PacketConn, packetB func ExchangeDNSPacket(ctx context.Context, router *Router, conn N.PacketConn, buffer *buf.Buffer, metadata adapter.InboundContext, destination M.Socksaddr) { err := exchangeDNSPacket(ctx, router, conn, buffer, metadata, destination) - if err != nil && !errors.Is(err, tun.ErrDrop) && !E.IsClosedOrCanceled(err) { + if err != nil && !R.IsRejected(err) && !E.IsClosedOrCanceled(err) { router.dnsLogger.ErrorContext(ctx, E.Cause(err, "process packet connection")) } } diff --git a/route/route.go b/route/route.go index d9bf2638..b4c00444 100644 --- a/route/route.go +++ b/route/route.go @@ -16,7 +16,7 @@ import ( "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/route/rule" + R "github.com/sagernet/sing-box/route/rule" "github.com/sagernet/sing-dns" "github.com/sagernet/sing-mux" "github.com/sagernet/sing-vmess" @@ -51,7 +51,7 @@ func (r *Router) RouteConnectionEx(ctx context.Context, conn net.Conn, metadata err := r.routeConnection(ctx, conn, metadata, onClose) if err != nil { N.CloseOnHandshakeFailure(conn, onClose, err) - if E.IsClosedOrCanceled(err) { + if E.IsClosedOrCanceled(err) || R.IsRejected(err) { r.logger.DebugContext(ctx, "connection closed: ", err) } else { r.logger.ErrorContext(ctx, err) @@ -101,7 +101,7 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad var selectedOutbound adapter.Outbound if selectedRule != nil { switch action := selectedRule.Action().(type) { - case *rule.RuleActionRoute: + case *R.RuleActionRoute: var loaded bool selectedOutbound, loaded = r.outbound.Outbound(action.Outbound) if !loaded { @@ -112,11 +112,11 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad buf.ReleaseMulti(buffers) return E.New("TCP is not supported by outbound: ", selectedOutbound.Tag()) } - case *rule.RuleActionReject: + case *R.RuleActionReject: buf.ReleaseMulti(buffers) N.CloseOnHandshakeFailure(conn, onClose, action.Error(ctx)) return nil - case *rule.RuleActionHijackDNS: + case *R.RuleActionHijackDNS: for _, buffer := range buffers { conn = bufio.NewCachedConn(conn, buffer) } @@ -154,7 +154,7 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m })) if err != nil { conn.Close() - if E.IsClosedOrCanceled(err) { + if E.IsClosedOrCanceled(err) || R.IsRejected(err) { r.logger.DebugContext(ctx, "connection closed: ", err) } else { r.logger.ErrorContext(ctx, err) @@ -171,7 +171,7 @@ func (r *Router) RoutePacketConnectionEx(ctx context.Context, conn N.PacketConn, err := r.routePacketConnection(ctx, conn, metadata, onClose) if err != nil { N.CloseOnHandshakeFailure(conn, onClose, err) - if E.IsClosedOrCanceled(err) { + if E.IsClosedOrCanceled(err) || R.IsRejected(err) { r.logger.DebugContext(ctx, "connection closed: ", err) } else { r.logger.ErrorContext(ctx, err) @@ -217,7 +217,7 @@ func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, m var selectReturn bool if selectedRule != nil { switch action := selectedRule.Action().(type) { - case *rule.RuleActionRoute: + case *R.RuleActionRoute: var loaded bool selectedOutbound, loaded = r.outbound.Outbound(action.Outbound) if !loaded { @@ -228,11 +228,11 @@ func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, m N.ReleaseMultiPacketBuffer(packetBuffers) return E.New("UDP is not supported by outbound: ", selectedOutbound.Tag()) } - case *rule.RuleActionReject: + case *R.RuleActionReject: N.ReleaseMultiPacketBuffer(packetBuffers) N.CloseOnHandshakeFailure(conn, onClose, action.Error(ctx)) return nil - case *rule.RuleActionHijackDNS: + case *R.RuleActionHijackDNS: r.hijackDNSPacket(ctx, conn, packetBuffers, metadata, onClose) return nil } @@ -271,7 +271,7 @@ func (r *Router) PreMatch(metadata adapter.InboundContext) error { if selectedRule == nil { return nil } - rejectAction, isReject := selectedRule.Action().(*rule.RuleActionReject) + rejectAction, isReject := selectedRule.Action().(*R.RuleActionReject) if !isReject { return nil } @@ -346,7 +346,7 @@ func (r *Router) matchRule( //nolint:staticcheck if metadata.InboundOptions != common.DefaultValue[option.InboundOptions]() { if !preMatch && metadata.InboundOptions.SniffEnabled { - newBuffer, newPackerBuffers, newErr := r.actionSniff(ctx, metadata, &rule.RuleActionSniff{ + newBuffer, newPackerBuffers, newErr := r.actionSniff(ctx, metadata, &R.RuleActionSniff{ OverrideDestination: metadata.InboundOptions.SniffOverrideDestination, Timeout: time.Duration(metadata.InboundOptions.SniffTimeout), }, inputConn, inputPacketConn, nil) @@ -361,7 +361,7 @@ func (r *Router) matchRule( } } if dns.DomainStrategy(metadata.InboundOptions.DomainStrategy) != dns.DomainStrategyAsIS { - fatalErr = r.actionResolve(ctx, metadata, &rule.RuleActionResolve{ + fatalErr = r.actionResolve(ctx, metadata, &R.RuleActionResolve{ Strategy: dns.DomainStrategy(metadata.InboundOptions.DomainStrategy), }) if fatalErr != nil { @@ -398,11 +398,11 @@ match: } } } - var routeOptions *rule.RuleActionRouteOptions + var routeOptions *R.RuleActionRouteOptions switch action := currentRule.Action().(type) { - case *rule.RuleActionRoute: + case *R.RuleActionRoute: routeOptions = &action.RuleActionRouteOptions - case *rule.RuleActionRouteOptions: + case *R.RuleActionRouteOptions: routeOptions = action } if routeOptions != nil { @@ -448,7 +448,7 @@ match: } } switch action := currentRule.Action().(type) { - case *rule.RuleActionSniff: + case *R.RuleActionSniff: if !preMatch { newBuffer, newPacketBuffers, newErr := r.actionSniff(ctx, metadata, action, inputConn, inputPacketConn, buffers) if newErr != nil { @@ -465,7 +465,7 @@ match: selectedRuleIndex = currentRuleIndex break match } - case *rule.RuleActionResolve: + case *R.RuleActionResolve: fatalErr = r.actionResolve(ctx, metadata, action) if fatalErr != nil { return @@ -485,7 +485,7 @@ match: } func (r *Router) actionSniff( - ctx context.Context, metadata *adapter.InboundContext, action *rule.RuleActionSniff, + ctx context.Context, metadata *adapter.InboundContext, action *R.RuleActionSniff, inputConn net.Conn, inputPacketConn N.PacketConn, inputBuffers []*buf.Buffer, ) (buffer *buf.Buffer, packetBuffers []*N.PacketBuffer, fatalErr error) { if sniff.Skip(metadata) { @@ -645,7 +645,7 @@ func (r *Router) actionSniff( return } -func (r *Router) actionResolve(ctx context.Context, metadata *adapter.InboundContext, action *rule.RuleActionResolve) error { +func (r *Router) actionResolve(ctx context.Context, metadata *adapter.InboundContext, action *R.RuleActionResolve) error { if metadata.Destination.IsFqdn() { metadata.DNSServer = action.Server addresses, err := r.Lookup(adapter.WithContext(ctx, metadata), metadata.Destination.Fqdn, action.Strategy) diff --git a/route/route_dns.go b/route/route_dns.go index 0e22cdfa..c5aced09 100644 --- a/route/route_dns.go +++ b/route/route_dns.go @@ -170,7 +170,7 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er Question: []mDNS.Question{message.Question[0]}, }, nil case C.RuleActionRejectMethodDrop: - return nil, tun.ErrDrop + return nil, &R.RejectedError{Cause: tun.ErrDrop} } } } @@ -289,7 +289,7 @@ func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainS case C.RuleActionRejectMethodDefault: return nil, nil case C.RuleActionRejectMethodDrop: - return nil, tun.ErrDrop + return nil, &R.RejectedError{Cause: tun.ErrDrop} } } } diff --git a/route/rule/rule_action.go b/route/rule/rule_action.go index d4c6625d..ef71e232 100644 --- a/route/rule/rule_action.go +++ b/route/rule/rule_action.go @@ -2,6 +2,7 @@ package rule import ( "context" + "errors" "net/netip" "strings" "sync" @@ -250,6 +251,23 @@ func (r *RuleActionDirect) String() string { return "direct" + r.description } +type RejectedError struct { + Cause error +} + +func (r *RejectedError) Error() string { + return "rejected" +} + +func (r *RejectedError) Unwrap() error { + return r.Cause +} + +func IsRejected(err error) bool { + var rejected *RejectedError + return errors.As(err, &rejected) +} + type RuleActionReject struct { Method string NoDrop bool @@ -273,9 +291,9 @@ func (r *RuleActionReject) Error(ctx context.Context) error { var returnErr error switch r.Method { case C.RuleActionRejectMethodDefault: - returnErr = syscall.ECONNREFUSED + returnErr = &RejectedError{syscall.ECONNREFUSED} case C.RuleActionRejectMethodDrop: - return tun.ErrDrop + return &RejectedError{tun.ErrDrop} default: panic(F.ToString("unknown reject method: ", r.Method)) } @@ -293,7 +311,7 @@ func (r *RuleActionReject) Error(ctx context.Context) error { if ctx != nil { r.logger.DebugContext(ctx, "dropped due to flooding") } - return tun.ErrDrop + return &RejectedError{tun.ErrDrop} } return returnErr }