From 097ef7ef911b71f83046a7c37ca9f5823cc293a0 Mon Sep 17 00:00:00 2001 From: Saleh Date: Tue, 4 Aug 2026 04:49:50 +0300 Subject: [PATCH] plugin/file: run additional processing for CNAME/DNAME answers (#8337) externalLookup, which resolves the chase for ordinary CNAME, wildcard CNAME, and DNAME answers, returned a nil additional section. So a chased SRV/MX/SVCB/HTTPS answer with an in-bailiwick target was missing the target's A/AAAA glue, unlike the direct path. Run additionalProcessing at externalLookup's return points so all three callers add the glue, and add ordinary-CNAME, wildcard-CNAME, and DNAME regression cases. Fixes #6628 Signed-off-by: Saleh --- plugin/file/lookup.go | 13 ++++++---- plugin/file/lookup_test.go | 52 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/plugin/file/lookup.go b/plugin/file/lookup.go index a187ae37f..7c4aea0ce 100644 --- a/plugin/file/lookup.go +++ b/plugin/file/lookup.go @@ -350,7 +350,10 @@ func (z *Zone) authority(do bool, result Result) []dns.RR { return z.ns(do) } -// externalLookup adds signatures and tries to resolve CNAMEs that point to external names. +// externalLookup adds signatures and tries to resolve CNAMEs that point to +// external names. It also runs additional-section processing on the resolved +// answer so in-bailiwick SRV/MX/SVCB/HTTPS targets get their A/AAAA glue, like +// the direct path. func (z *Zone) externalLookup(ctx context.Context, state request.Request, tr *tree.Tree, elem *tree.Elem, rrs []dns.RR) ([]dns.RR, []dns.RR, []dns.RR, Result) { qtype := state.QType() do := state.Do() @@ -369,7 +372,7 @@ func (z *Zone) externalLookup(ctx context.Context, state request.Request, tr *tr if elem == nil || (qtype == dns.TypeNS || qtype == dns.TypeSOA && targetName == z.origin) { lookupRRs, result := z.doLookup(ctx, state, targetName, qtype) rrs = append(rrs, lookupRRs...) - return rrs, z.authority(do, result), nil, result + return rrs, z.authority(do, result), z.additionalProcessing(rrs, do), result } i := 0 @@ -392,12 +395,12 @@ Redo: if elem == nil || (qtype == dns.TypeNS || qtype == dns.TypeSOA && targetName == z.origin) { lookupRRs, result := z.doLookup(ctx, state, targetName, qtype) rrs = append(rrs, lookupRRs...) - return rrs, z.authority(do, result), nil, result + return rrs, z.authority(do, result), z.additionalProcessing(rrs, do), result } i++ if i > 8 { - return rrs, z.ns(do), nil, Success + return rrs, z.ns(do), z.additionalProcessing(rrs, do), Success } goto Redo @@ -414,7 +417,7 @@ Redo: } } - return rrs, z.ns(do), nil, Success + return rrs, z.ns(do), z.additionalProcessing(rrs, do), Success } // findDelegation returns the first zone cut between the zone apex and qname. diff --git a/plugin/file/lookup_test.go b/plugin/file/lookup_test.go index 0c794ce7e..c131d8fa6 100644 --- a/plugin/file/lookup_test.go +++ b/plugin/file/lookup_test.go @@ -408,6 +408,48 @@ var additionalTestCases = []test.Case{ test.AAAA("other.example.org. 1800 IN AAAA 2001:db8::20"), }, }, + { + // An ordinary CNAME chased to an SRV still needs the target's glue (#6628). + Qname: "cname-srv.example.org.", Qtype: dns.TypeSRV, + Answer: []dns.RR{ + test.CNAME("cname-srv.example.org. 1800 IN CNAME srv.example.org."), + test.SRV("srv.example.org. 1800 IN SRV 10 50 8080 host.example.org."), + test.SRV("srv.example.org. 1800 IN SRV 20 50 8080 host.example.org."), + }, + Ns: additionalAuth, + Extra: []dns.RR{ + test.A("host.example.org. 1800 IN A 192.0.2.10"), + test.AAAA("host.example.org. 1800 IN AAAA 2001:db8::10"), + }, + }, + { + // A wildcard CNAME chased to an SRV. + Qname: "foo.wild.example.org.", Qtype: dns.TypeSRV, + Answer: []dns.RR{ + test.CNAME("foo.wild.example.org. 1800 IN CNAME srv.example.org."), + test.SRV("srv.example.org. 1800 IN SRV 10 50 8080 host.example.org."), + test.SRV("srv.example.org. 1800 IN SRV 20 50 8080 host.example.org."), + }, + Ns: additionalAuth, + Extra: []dns.RR{ + test.A("host.example.org. 1800 IN A 192.0.2.10"), + test.AAAA("host.example.org. 1800 IN AAAA 2001:db8::10"), + }, + }, + { + // A DNAME substitution landing on an SRV (#6628). + Qname: "srv.dname.example.org.", Qtype: dns.TypeSRV, + Answer: []dns.RR{ + test.DNAME("dname.example.org. 1800 IN DNAME dsub.example.org."), + test.CNAME("srv.dname.example.org. 1800 IN CNAME srv.dsub.example.org."), + test.SRV("srv.dsub.example.org. 1800 IN SRV 10 50 8080 host.example.org."), + }, + Ns: additionalAuth, + Extra: []dns.RR{ + test.A("host.example.org. 1800 IN A 192.0.2.10"), + test.AAAA("host.example.org. 1800 IN AAAA 2001:db8::10"), + }, + }, } func TestAdditionalSectionDeduplication(t *testing.T) { @@ -504,4 +546,14 @@ mixed IN SRV 10 50 8080 host.example.org. ; Two MX records with distinct targets. two IN MX 10 host.example.org. IN MX 20 other.example.org. + +; A CNAME resolving to an SRV: the ordinary-CNAME chase must still add glue. +cname-srv IN CNAME srv.example.org. + +; A wildcard CNAME resolving to an SRV. +*.wild IN CNAME srv.example.org. + +; A DNAME whose subtree holds an SRV: the DNAME chase must add glue too. +dname IN DNAME dsub.example.org. +srv.dsub IN SRV 10 50 8080 host.example.org. `