Files
sing-box-extended-mirror/route/rule/rule_item_rule_set.go
T
世界 08986ad405 Fix address filter matching for inverted legacy DNS rules
Inverted rules whose address filter conditions come from rule sets or
logical rules were rejected during pre-lookup matching, so the lookup was
never sent and the rules never matched; for plain ip rule sets this was a
regression from the rule-set invert fix in 1.12.22. Pre-lookup matching
now tracks whether the result depends on deferred address filter
conditions and defers such rules to the response phase, while results
already determined by evaluated conditions are still decided during
pre-lookup.
2026-08-30 17:41:46 +08:00

166 lines
4.1 KiB
Go

package rule
import (
"strings"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
)
var _ RuleItem = (*RuleSetItem)(nil)
type RuleSetItem struct {
router adapter.Router
tagList []string
setList []adapter.RuleSet
ipCidrMatchSource bool
ipCidrAcceptEmpty bool
}
func NewRuleSetItem(router adapter.Router, tagList []string, ipCIDRMatchSource bool, ipCidrAcceptEmpty bool) *RuleSetItem {
return &RuleSetItem{
router: router,
tagList: tagList,
ipCidrMatchSource: ipCIDRMatchSource,
ipCidrAcceptEmpty: ipCidrAcceptEmpty,
}
}
func (r *RuleSetItem) Start() error {
_ = r.Close()
for _, tag := range r.tagList {
ruleSet, loaded := r.router.RuleSet(tag)
if !loaded {
_ = r.Close()
return E.New("rule-set not found: ", tag)
}
ruleSet.IncRef()
r.setList = append(r.setList, ruleSet)
}
return nil
}
func (r *RuleSetItem) Close() error {
for _, ruleSet := range r.setList {
ruleSet.DecRef()
}
clear(r.setList)
r.setList = nil
return nil
}
func (r *RuleSetItem) Match(metadata *adapter.InboundContext) bool {
for _, ruleSet := range r.setList {
nestedMetadata := r.nestedMetadata(metadata)
if ruleSet.Match(&nestedMetadata) {
return true
}
}
return false
}
func (r *RuleSetItem) matchWithOuterGroups(metadata *adapter.InboundContext, outerGroups ruleGroupMatch) bool {
outerDone := outerGroups.done()
var (
matched bool
deferredGroups uint8
)
for _, ruleSet := range r.setList {
nestedMetadata := r.nestedMetadata(metadata)
if provider, isProvider := ruleSet.(mergeableRuleProvider); isProvider {
branch := provider.mergeableRule()
if branch != nil {
branchGroups, branchMatched := branch.evaluateForMerge(&nestedMetadata)
if branchMatched {
merged := outerGroups.mergeWith(branchGroups)
if merged.done() {
branchDeferredGroups := nestedMetadata.DeferredIPCIDRMatchGroups &^ uint8(merged.satisfied)
if branchDeferredGroups == 0 {
metadata.DeferredIPCIDRMatchGroups &^= uint8(merged.satisfied)
return true
}
matched = true
deferredGroups |= branchDeferredGroups
}
}
continue
}
}
if outerDone && ruleSet.Match(&nestedMetadata) {
if nestedMetadata.DeferredIPCIDRMatchGroups == 0 {
return true
}
matched = true
deferredGroups |= nestedMetadata.DeferredIPCIDRMatchGroups
}
}
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
}
type mergeableRuleProvider interface {
mergeableRule() *DefaultHeadlessRule
}
func mergeableRuleIn(rules []adapter.HeadlessRule) *DefaultHeadlessRule {
if len(rules) != 1 {
return nil
}
rule, isDefault := rules[0].(*DefaultHeadlessRule)
if !isDefault || rule.invert || rule.ruleSetItem != nil {
return nil
}
return rule
}
func matchAnyHeadlessRule(rules []adapter.HeadlessRule, metadata *adapter.InboundContext) bool {
var (
matched bool
deferredGroups uint8
)
for _, rule := range rules {
nestedMetadata := *metadata
nestedMetadata.ResetRuleMatchCache()
if rule.Match(&nestedMetadata) {
if nestedMetadata.DeferredIPCIDRMatchGroups == 0 {
return true
}
matched = true
deferredGroups |= nestedMetadata.DeferredIPCIDRMatchGroups
}
}
if matched {
metadata.DeferredIPCIDRMatchGroups |= deferredGroups
}
return matched
}
func (r *RuleSetItem) ContainsDestinationIPCIDRRule() bool {
if r.ipCidrMatchSource {
return false
}
return common.Any(r.setList, func(ruleSet adapter.RuleSet) bool {
return ruleSet.Metadata().ContainsIPCIDRRule
})
}
func (r *RuleSetItem) String() string {
if len(r.tagList) == 1 {
return F.ToString("rule_set=", r.tagList[0])
} else {
return F.ToString("rule_set=[", strings.Join(r.tagList, " "), "]")
}
}