From 1bbab1c8c39cff50c7543f5aaee122644db9144e Mon Sep 17 00:00:00 2001 From: Alex Hope-O'Connor Date: Mon, 30 Mar 2026 19:41:08 +1000 Subject: [PATCH] Fix address/local rule: skip NXDOMAIN address=/domain/ form GetDomainsFromAddressDirective only considers lines with a non-empty target after the final slash; address=/domain/ applies to all RR types and should not require local=. Add tests for trailing-slash and # forms. --- .../CrossOption/CrossOptionRulesTests.cs | 22 +++++++++++++++++++ .../Validation/Rules/CrossOptionRules.cs | 8 ++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/DnsmasqWebUI.Tests/Services/EffectiveConfig/CrossOption/CrossOptionRulesTests.cs b/src/DnsmasqWebUI.Tests/Services/EffectiveConfig/CrossOption/CrossOptionRulesTests.cs index 2f4625a..8e5e1bd 100644 --- a/src/DnsmasqWebUI.Tests/Services/EffectiveConfig/CrossOption/CrossOptionRulesTests.cs +++ b/src/DnsmasqWebUI.Tests/Services/EffectiveConfig/CrossOption/CrossOptionRulesTests.cs @@ -390,6 +390,28 @@ public class CrossOptionRulesTests Assert.Empty(rule.Evaluate(Ctx(cfg))); } + [Fact] + public void AddressLocalDnsmasq286CompatibilityRule_No_issue_for_nxdomain_form_without_ip() + { + var cfg = CrossOptionTestHelpers.BaselineConfig() with + { + AddressValues = ["/ads.example/"] + }; + var rule = new AddressLocalDnsmasq286CompatibilityRule(); + Assert.Empty(rule.Evaluate(Ctx(cfg))); + } + + [Fact] + public void AddressLocalDnsmasq286CompatibilityRule_Warns_for_hash_target_without_local() + { + var cfg = CrossOptionTestHelpers.BaselineConfig() with + { + AddressValues = ["/blocked.example/#"] + }; + var rule = new AddressLocalDnsmasq286CompatibilityRule(); + Assert.Single(rule.Evaluate(Ctx(cfg))); + } + [Fact] public void DnssecCheckUnsignedRequiresDnssecRule_Warns_when_dnssec_check_unsigned_set_without_dnssec() { diff --git a/src/DnsmasqWebUI/Infrastructure/Services/EffectiveConfig/Validation/Rules/CrossOptionRules.cs b/src/DnsmasqWebUI/Infrastructure/Services/EffectiveConfig/Validation/Rules/CrossOptionRules.cs index 4dfd2c3..ebfcfb3 100644 --- a/src/DnsmasqWebUI/Infrastructure/Services/EffectiveConfig/Validation/Rules/CrossOptionRules.cs +++ b/src/DnsmasqWebUI/Infrastructure/Services/EffectiveConfig/Validation/Rules/CrossOptionRules.cs @@ -442,13 +442,19 @@ public sealed class AddressLocalDnsmasq286CompatibilityRule : IEffectiveConfigCr ]; } + // Only address=/domain/IP (or #, etc.) is affected by 2.86 A/AAAA-only behavior. address=/domain/ + // (nothing after the last slash) is NXDOMAIN for all RR types and does not need local=. private static IReadOnlyList GetDomainsFromAddressDirective(string value) { var t = value.Trim(); if (!t.StartsWith('/')) return []; var end = t.LastIndexOf('/'); - if (end <= 1) + if (end < 1) + return []; + + var addressPayload = t[(end + 1)..].Trim(); + if (addressPayload.Length == 0) return []; var domainsPart = t[1..end];