diff --git a/pdns/recursordist/pdns_recursor.cc b/pdns/recursordist/pdns_recursor.cc index 7b2419fd8860..d5fdf0824869 100644 --- a/pdns/recursordist/pdns_recursor.cc +++ b/pdns/recursordist/pdns_recursor.cc @@ -760,6 +760,24 @@ int getFakeAAAARecords(const DNSName& qname, ComboAddress prefix, vector seenSOAs; + ret.erase(std::remove_if( + ret.begin(), + ret.end(), + [&seenSOAs](DNSRecord& record) { + if (record.d_type == QType::SOA) { + if (seenSOAs.count(record.d_name) > 0) { + // We've had this SOA before, remove it + return true; + } + seenSOAs.insert(record.d_name); + } + return false; + }), + ret.end()); + } t_Counters.at(rec::Counter::dns64prefixanswers)++; return rcode; } diff --git a/regression-tests.recursor-dnssec/recursortests.py b/regression-tests.recursor-dnssec/recursortests.py index c19426965e0b..1bb989c000ef 100644 --- a/regression-tests.recursor-dnssec/recursortests.py +++ b/regression-tests.recursor-dnssec/recursortests.py @@ -805,7 +805,7 @@ def sendUDPQuery(cls, query, timeout=2.0, decode=True, fwparams=dict()): return message @classmethod - def sendTCPQuery(cls, query, timeout=2.0): + def sendTCPQuery(cls, query, timeout=2.0, decode=True, fwparams=dict()): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) if timeout: sock.settimeout(timeout) @@ -831,7 +831,9 @@ def sendTCPQuery(cls, query, timeout=2.0): message = None if data: - message = dns.message.from_wire(data) + if not decode: + return data + message = dns.message.from_wire(data, **fwparams) return message @classmethod diff --git a/regression-tests.recursor-dnssec/test_DNS64.py b/regression-tests.recursor-dnssec/test_DNS64.py index abf6fdb23ac2..3b1e671ff993 100644 --- a/regression-tests.recursor-dnssec/test_DNS64.py +++ b/regression-tests.recursor-dnssec/test_DNS64.py @@ -32,9 +32,11 @@ def generateRecursorConfig(cls, confdir): @ 3600 IN SOA {soa} www 3600 IN A 192.0.2.42 www 3600 IN TXT "does exist" +txt 3600 IN TXT "a and aaaa do not exist" aaaa 3600 IN AAAA 2001:db8::1 cname 3600 IN CNAME cname2.example.dns64. cname2 3600 IN CNAME www.example.dns64. +cname3 3600 IN CNAME txt.example.dns64. formerr 3600 IN A 192.0.2.43 """.format(soa=cls._SOA)) @@ -107,6 +109,22 @@ def testCNAMEToA(self): for expected in expectedResults: self.assertRRsetInAnswer(res, expected) + # there is a CNAME from the name to a name that is NODATA for both A and AAAA + # so we should get a NODATA with a single SOA record (#14362) + def testCNAMEToNoData(self): + qname = 'cname3.example.dns64.' + + expectedAnswer = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'CNAME', 'txt.example.dns64.') + query = dns.message.make_query(qname, 'AAAA', want_dnssec=True) + for method in ("sendUDPQuery", "sendTCPQuery"): + sender = getattr(self, method) + res = sender(query, 2.0, True, {"one_rr_per_rrset": True}) # we want to detect dups + self.assertRcodeEqual(res, dns.rcode.NOERROR) + self.assertEqual(len(res.answer), 1) + self.assertEqual(len(res.authority), 1) + self.assertRRsetInAnswer(res, expectedAnswer) + self.assertAuthorityHasSOA(res) + # this type (AAAA) does not exist for this name and there is no A record either, we should get a NXDomain def testNXD(self): qname = 'nxd.example.dns64.' @@ -117,6 +135,18 @@ def testNXD(self): res = sender(query) self.assertRcodeEqual(res, dns.rcode.NXDOMAIN) + # this type (AAAA) does not exist for this name and there is no A record either, we should get a NODATA as TXT does exist + def testNoData(self): + qname = 'txt.example.dns64.' + + query = dns.message.make_query(qname, 'AAAA', want_dnssec=True) + for method in ("sendUDPQuery", "sendTCPQuery"): + sender = getattr(self, method) + res = sender(query, 2.0, True, {"one_rr_per_rrset": True}) # we want to detect dups + self.assertRcodeEqual(res, dns.rcode.NOERROR) + self.assertEqual(len(res.answer), 0) + self.assertEqual(len(res.authority), 1) + # there is an AAAA record, we should get it def testExistingAAAA(self): qname = 'aaaa.example.dns64.'