diff --git a/protocol/tailscale/dns_transport.go b/protocol/tailscale/dns_transport.go index d91b498f..b48aa823 100644 --- a/protocol/tailscale/dns_transport.go +++ b/protocol/tailscale/dns_transport.go @@ -129,7 +129,7 @@ func (t *DNSTransport) updateDNSServers(routeConfig *router.Config, dnsConfig *n } myResolvers = append(myResolvers, myResolver) } - routes[domain.WithTrailingDot()] = myResolvers + routes[mDNS.CanonicalName(domain.WithTrailingDot())] = myResolvers } hosts := make(map[string][]netip.Addr) for domain, addresses := range dnsConfig.Hosts { @@ -275,14 +275,25 @@ func (t *DNSTransport) PreferredDomain(domain string) bool { if t.acceptSearchDomain && len(searchDomains) > 0 && mDNS.CountLabel(domain) == 1 { return true } + canonicalDomain := mDNS.CanonicalName(domain) for suffix := range routes { - if mDNS.IsSubDomain(suffix, domain) { + if matchDomainSuffix(canonicalDomain, suffix) { return true } } return false } +func matchDomainSuffix(domain string, suffix string) bool { + if suffix == "." || suffix == "" { + return true + } + if !strings.HasSuffix(domain, suffix) { + return false + } + return len(domain) == len(suffix) || domain[len(domain)-len(suffix)-1] == '.' +} + func (t *DNSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { done := make(chan struct{}) var ( @@ -375,8 +386,9 @@ func (t *DNSTransport) exchangeOnce(ctx context.Context, message *mDNS.Msg, allo } return } + canonicalName := mDNS.CanonicalName(question.Name) for domainSuffix, transports := range routes { - if mDNS.IsSubDomain(domainSuffix, question.Name) { + if matchDomainSuffix(canonicalName, domainSuffix) { if len(transports) == 0 { callback(&mDNS.Msg{ MsgHdr: mDNS.MsgHdr{ diff --git a/protocol/tailscale/endpoint.go b/protocol/tailscale/endpoint.go index 4cd0ecb9..f46a4869 100644 --- a/protocol/tailscale/endpoint.go +++ b/protocol/tailscale/endpoint.go @@ -953,7 +953,7 @@ func (t *Endpoint) PreferredDomain(metadata *adapter.InboundContext, domain stri } } for _, suffix := range t.routeSuffixes.Load() { - if mDNS.IsSubDomain(suffix, domain) { + if matchDomainSuffix(domain, suffix) { return true } } @@ -1000,7 +1000,7 @@ func (t *Endpoint) onReconfig(cfg *wgcfg.Config, routerCfg *router.Config, dnsCf } routeSuffixes := make([]string, 0, len(dnsCfg.Routes)) for fqdn := range dnsCfg.Routes { - routeSuffixes = append(routeSuffixes, fqdn.WithoutTrailingDot()) + routeSuffixes = append(routeSuffixes, strings.ToLower(fqdn.WithoutTrailingDot())) } t.routeDomains.Store(routeDomains) t.routeSuffixes.Store(routeSuffixes) diff --git a/route/rule/match_state.go b/route/rule/match_state.go index f7f937f8..e6b4fd84 100644 --- a/route/rule/match_state.go +++ b/route/rule/match_state.go @@ -1,5 +1,7 @@ package rule +import "github.com/sagernet/sing-box/adapter" + type ruleMatchState uint8 const ( @@ -24,3 +26,23 @@ func (g ruleGroupMatch) mergeWith(other ruleGroupMatch) ruleGroupMatch { satisfied: g.satisfied | other.satisfied, } } + +type ruleMatchSnapshot struct { + ipCidrMatchSource bool + ipCidrAcceptEmpty bool + deferredIPCIDRMatchGroups uint8 +} + +func snapshotRuleMatch(metadata *adapter.InboundContext) ruleMatchSnapshot { + return ruleMatchSnapshot{ + ipCidrMatchSource: metadata.IPCIDRMatchSource, + ipCidrAcceptEmpty: metadata.IPCIDRAcceptEmpty, + deferredIPCIDRMatchGroups: metadata.DeferredIPCIDRMatchGroups, + } +} + +func (s ruleMatchSnapshot) restore(metadata *adapter.InboundContext) { + metadata.IPCIDRMatchSource = s.ipCidrMatchSource + metadata.IPCIDRAcceptEmpty = s.ipCidrAcceptEmpty + metadata.DeferredIPCIDRMatchGroups = s.deferredIPCIDRMatchGroups +} diff --git a/route/rule/rule_abstract.go b/route/rule/rule_abstract.go index f21df788..cb7d5060 100644 --- a/route/rule/rule_abstract.go +++ b/route/rule/rule_abstract.go @@ -206,32 +206,32 @@ func (r *abstractLogicalRule) Match(metadata *adapter.InboundContext) bool { matched bool deferredGroups uint8 ) + snapshot := snapshotRuleMatch(metadata) if r.mode == C.LogicalTypeAnd { matched = true for _, rule := range r.rules { - nestedMetadata := *metadata - nestedMetadata.ResetRuleCache() - if !rule.Match(&nestedMetadata) { + metadata.ResetRuleCache() + if !rule.Match(metadata) { matched = false deferredGroups = 0 break } - deferredGroups |= nestedMetadata.DeferredIPCIDRMatchGroups + deferredGroups |= metadata.DeferredIPCIDRMatchGroups } } else { for _, rule := range r.rules { - nestedMetadata := *metadata - nestedMetadata.ResetRuleCache() - if rule.Match(&nestedMetadata) { + metadata.ResetRuleCache() + if rule.Match(metadata) { matched = true - if nestedMetadata.DeferredIPCIDRMatchGroups == 0 { + if metadata.DeferredIPCIDRMatchGroups == 0 { deferredGroups = 0 break } - deferredGroups |= nestedMetadata.DeferredIPCIDRMatchGroups + deferredGroups |= metadata.DeferredIPCIDRMatchGroups } } } + snapshot.restore(metadata) if matched { metadata.DeferredIPCIDRMatchGroups |= deferredGroups } diff --git a/route/rule/rule_item_rule_set.go b/route/rule/rule_item_rule_set.go index eb7c74e1..256183e2 100644 --- a/route/rule/rule_item_rule_set.go +++ b/route/rule/rule_item_rule_set.go @@ -52,12 +52,15 @@ func (r *RuleSetItem) Close() error { } func (r *RuleSetItem) Match(metadata *adapter.InboundContext) bool { + snapshot := snapshotRuleMatch(metadata) for _, ruleSet := range r.setList { - nestedMetadata := r.nestedMetadata(metadata) - if ruleSet.Match(&nestedMetadata) { + r.prepareNestedMatch(metadata) + if ruleSet.Match(metadata) { + snapshot.restore(metadata) return true } } + snapshot.restore(metadata) return false } @@ -67,17 +70,19 @@ func (r *RuleSetItem) matchWithOuterGroups(metadata *adapter.InboundContext, out matched bool deferredGroups uint8 ) + snapshot := snapshotRuleMatch(metadata) for _, ruleSet := range r.setList { - nestedMetadata := r.nestedMetadata(metadata) + r.prepareNestedMatch(metadata) if provider, isProvider := ruleSet.(mergeableRuleProvider); isProvider { branch := provider.mergeableRule() if branch != nil { - branchGroups, branchMatched := branch.evaluateForMerge(&nestedMetadata) + branchGroups, branchMatched := branch.evaluateForMerge(metadata) if branchMatched { merged := outerGroups.mergeWith(branchGroups) if merged.done() { - branchDeferredGroups := nestedMetadata.DeferredIPCIDRMatchGroups &^ uint8(merged.satisfied) + branchDeferredGroups := metadata.DeferredIPCIDRMatchGroups &^ uint8(merged.satisfied) if branchDeferredGroups == 0 { + snapshot.restore(metadata) metadata.DeferredIPCIDRMatchGroups &^= uint8(merged.satisfied) return true } @@ -88,26 +93,26 @@ func (r *RuleSetItem) matchWithOuterGroups(metadata *adapter.InboundContext, out continue } } - if outerDone && ruleSet.Match(&nestedMetadata) { - if nestedMetadata.DeferredIPCIDRMatchGroups == 0 { + if outerDone && ruleSet.Match(metadata) { + if metadata.DeferredIPCIDRMatchGroups == 0 { + snapshot.restore(metadata) return true } matched = true - deferredGroups |= nestedMetadata.DeferredIPCIDRMatchGroups + deferredGroups |= metadata.DeferredIPCIDRMatchGroups } } + snapshot.restore(metadata) if matched { metadata.DeferredIPCIDRMatchGroups |= deferredGroups } return matched } -func (r *RuleSetItem) nestedMetadata(metadata *adapter.InboundContext) adapter.InboundContext { - nestedMetadata := *metadata - nestedMetadata.ResetRuleMatchCache() - nestedMetadata.IPCIDRMatchSource = r.ipCidrMatchSource - nestedMetadata.IPCIDRAcceptEmpty = r.ipCidrAcceptEmpty - return nestedMetadata +func (r *RuleSetItem) prepareNestedMatch(metadata *adapter.InboundContext) { + metadata.ResetRuleMatchCache() + metadata.IPCIDRMatchSource = r.ipCidrMatchSource + metadata.IPCIDRAcceptEmpty = r.ipCidrAcceptEmpty } type mergeableRuleProvider interface { @@ -130,17 +135,19 @@ func matchAnyHeadlessRule(rules []adapter.HeadlessRule, metadata *adapter.Inboun matched bool deferredGroups uint8 ) + snapshot := snapshotRuleMatch(metadata) for _, rule := range rules { - nestedMetadata := *metadata - nestedMetadata.ResetRuleMatchCache() - if rule.Match(&nestedMetadata) { - if nestedMetadata.DeferredIPCIDRMatchGroups == 0 { + metadata.ResetRuleMatchCache() + if rule.Match(metadata) { + if metadata.DeferredIPCIDRMatchGroups == 0 { + snapshot.restore(metadata) return true } matched = true - deferredGroups |= nestedMetadata.DeferredIPCIDRMatchGroups + deferredGroups |= metadata.DeferredIPCIDRMatchGroups } } + snapshot.restore(metadata) if matched { metadata.DeferredIPCIDRMatchGroups |= deferredGroups }