From: Remi Gacogne Date: Mon, 23 Jan 2023 14:56:24 +0000 (+0100) Subject: dnsdist: Stop caching responses to XFR queries X-Git-Tag: dnsdist-1.8.0-rc1~90^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=8eff5c8d8bc76186fa2bf03aeef8db1068dcfabf;p=thirdparty%2Fpdns.git dnsdist: Stop caching responses to XFR queries --- diff --git a/pdns/dnsdist-cache.cc b/pdns/dnsdist-cache.cc index 3fe10336e0..9b345e25e8 100644 --- a/pdns/dnsdist-cache.cc +++ b/pdns/dnsdist-cache.cc @@ -122,6 +122,9 @@ void DNSDistPacketCache::insert(uint32_t key, const boost::optional& su if (response.size() < sizeof(dnsheader)) { return; } + if (qtype == QType::AXFR || qtype == QType::IXFR) { + return; + } uint32_t minTTL; @@ -194,6 +197,11 @@ void DNSDistPacketCache::insert(uint32_t key, const boost::optional& su bool DNSDistPacketCache::get(DNSQuestion& dq, uint16_t queryId, uint32_t* keyOut, boost::optional& subnet, bool dnssecOK, bool receivedOverUDP, uint32_t allowExpired, bool skipAging, bool truncatedOK, bool recordMiss) { + if (dq.ids.qtype == QType::AXFR || dq.ids.qtype == QType::IXFR) { + d_misses++; + return false; + } + const auto& dnsQName = dq.ids.qname.getStorage(); uint32_t key = getKey(dnsQName, dq.ids.qname.wirelength(), dq.getData(), receivedOverUDP); diff --git a/pdns/dnsdistdist/docs/advanced/axfr.rst b/pdns/dnsdistdist/docs/advanced/axfr.rst index 0be88d31f5..822e7fc587 100644 --- a/pdns/dnsdistdist/docs/advanced/axfr.rst +++ b/pdns/dnsdistdist/docs/advanced/axfr.rst @@ -43,6 +43,9 @@ to do that are described in :doc:`Passing the source address to the backend xfrTypes = { QType::AXFR, QType::IXFR }; + for (const auto& type : xfrTypes) { + bool dnssecOK = false; + const time_t now = time(nullptr); + InternalQueryState ids; + ids.qtype = type; + ids.qclass = QClass::IN; + ids.protocol = dnsdist::Protocol::DoUDP; + ids.qname = DNSName("powerdns.com."); + + PacketBuffer query; + GenericDNSPacketWriter pwQ(query, ids.qname, ids.qtype, ids.qclass, 0); + pwQ.getHeader()->rd = 1; + + PacketBuffer response; + GenericDNSPacketWriter pwR(response, ids.qname, ids.qtype, ids.qclass, 0); + pwR.getHeader()->rd = 1; + pwR.getHeader()->ra = 1; + pwR.getHeader()->qr = 1; + pwR.getHeader()->id = pwQ.getHeader()->id; + pwR.startRecord(ids.qname, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER); + pwR.xfr32BitInt(0x01020304); + pwR.commit(); + + uint32_t key = 0; + boost::optional subnet; + DNSQuestion dq(ids, query); + bool found = PC.get(dq, 0, &key, subnet, dnssecOK, receivedOverUDP); + BOOST_CHECK_EQUAL(found, false); + BOOST_CHECK(!subnet); + + PC.insert(key, subnet, *(getFlagsFromDNSHeader(dq.getHeader())), dnssecOK, ids.qname, ids.qtype, ids.qclass, response, receivedOverUDP, 0, boost::none); + found = PC.get(dq, pwR.getHeader()->id, &key, subnet, dnssecOK, receivedOverUDP, 0, true); + BOOST_CHECK_EQUAL(found, false); + } +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/regression-tests.dnsdist/test_Caching.py b/regression-tests.dnsdist/test_Caching.py index dafa0c89ab..4dd5f88f0c 100644 --- a/regression-tests.dnsdist/test_Caching.py +++ b/regression-tests.dnsdist/test_Caching.py @@ -233,6 +233,80 @@ class TestCaching(DNSDistTest): value = self._responsesCounter[key] self.assertEqual(value, numberOfQueries) + def testAXFRResponse(self): + """ + Cache: AXFR should not be cached + + dnsdist should not cache responses to AXFR queries. + """ + name = 'axfr.cache.tests.powerdns.com.' + query = dns.message.make_query(name, 'AXFR', 'IN') + response = dns.message.make_response(query) + soa = dns.rrset.from_text(name, + 60, + dns.rdataclass.IN, + dns.rdatatype.SOA, + 'ns.' + name + ' hostmaster.' + name + ' 1 3600 3600 3600 60') + response.answer.append(soa) + response.answer.append(dns.rrset.from_text(name, + 60, + dns.rdataclass.IN, + dns.rdatatype.A, + '192.0.2.1')) + response.answer.append(soa) + numberOfQueries = 5 + + for _ in range(numberOfQueries): + for method in ("sendUDPQuery", "sendTCPQuery"): + sender = getattr(self, method) + (receivedQuery, receivedResponse) = sender(query, response) + self.assertTrue(receivedQuery) + self.assertTrue(receivedResponse) + receivedQuery.id = query.id + self.assertEqual(query, receivedQuery) + self.assertEqual(receivedResponse, response) + + for key in self._responsesCounter: + value = self._responsesCounter[key] + self.assertEqual(value, numberOfQueries) + + def testIXFRResponse(self): + """ + Cache: IXFR should not be cached + + dnsdist should not cache responses to IXFR queries. + """ + name = 'ixfr.cache.tests.powerdns.com.' + query = dns.message.make_query(name, 'IXFR', 'IN') + response = dns.message.make_response(query) + soa = dns.rrset.from_text(name, + 60, + dns.rdataclass.IN, + dns.rdatatype.SOA, + 'ns.' + name + ' hostmaster.' + name + ' 1 3600 3600 3600 60') + response.answer.append(soa) + response.answer.append(dns.rrset.from_text(name, + 60, + dns.rdataclass.IN, + dns.rdatatype.A, + '192.0.2.1')) + response.answer.append(soa) + numberOfQueries = 5 + + for _ in range(numberOfQueries): + for method in ("sendUDPQuery", "sendTCPQuery"): + sender = getattr(self, method) + (receivedQuery, receivedResponse) = sender(query, response) + self.assertTrue(receivedQuery) + self.assertTrue(receivedResponse) + receivedQuery.id = query.id + self.assertEqual(query, receivedQuery) + self.assertEqual(receivedResponse, response) + + for key in self._responsesCounter: + value = self._responsesCounter[key] + self.assertEqual(value, numberOfQueries) + def testCacheExpiration(self): """ Cache: Cache expiration