dns: document non-response rule_set address-filter semantics

This commit is contained in:
世界
2026-03-25 16:59:06 +08:00
parent 264efce753
commit 93bc5caea9
2 changed files with 11 additions and 0 deletions

View File

@@ -810,6 +810,11 @@ func validateNonLegacyAddressFilterDefaultRule(rule option.DefaultDNSRule) (bool
if (len(rule.IPCIDR) > 0 || rule.IPIsPrivate) && !rule.MatchResponse {
return false, E.New("ip_cidr and ip_is_private require match_response in DNS evaluate mode")
}
// Intentionally do not reject rule_set here. A referenced rule set may mix
// destination-IP predicates with pre-response predicates such as domain items.
// When match_response is false, those destination-IP branches fail closed during
// pre-response evaluation instead of consuming DNS response state, while sibling
// non-response branches remain matchable.
if rule.IPAcceptAny {
return false, E.New("ip_accept_any is removed in DNS evaluate mode, use ip_cidr with match_response")
}

View File

@@ -625,6 +625,9 @@ func TestDNSRuleSetSemantics(t *testing.T) {
rule := dnsRuleForTest(func(rule *abstractDefaultRule) {
addRuleSetItem(rule, &RuleSetItem{setList: []adapter.RuleSet{ruleSet}})
})
// This is accepted without match_response so mixed rule_set deployments keep
// working; the destination-IP-only branch simply cannot match before a DNS
// response is available.
require.False(t, rule.Match(&metadata))
})
t.Run("pre lookup ruleset destination cidr does not fall back to other predicates", func(t *testing.T) {
@@ -655,6 +658,9 @@ func TestDNSRuleSetSemantics(t *testing.T) {
rule := dnsRuleForTest(func(rule *abstractDefaultRule) {
addRuleSetItem(rule, &RuleSetItem{setList: []adapter.RuleSet{ruleSet}})
})
// Destination-IP predicates inside rule_set fail closed before the DNS response,
// but they must not force validation errors or suppress sibling non-response
// branches.
require.True(t, rule.Match(&metadata))
})
}