From 8a64a8215bbbe5d2a88fc25d360c6fdeeb2ff2dc Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Thu, 4 Mar 2021 16:01:02 +0100 Subject: [PATCH] rec: Fix aggressive NSEC unit and regression tests We don't get the SOA and NSEC(3) since we don't ask for the DS before-hand, so we need to do a specific query to get it. --- pdns/recursordist/test-aggressive_nsec_cc.cc | 90 ++++++++++++++----- .../test_AggressiveNSECCache.py | 10 ++- 2 files changed, 79 insertions(+), 21 deletions(-) diff --git a/pdns/recursordist/test-aggressive_nsec_cc.cc b/pdns/recursordist/test-aggressive_nsec_cc.cc index b91fa8eff5..ab56e6a916 100644 --- a/pdns/recursordist/test-aggressive_nsec_cc.cc +++ b/pdns/recursordist/test-aggressive_nsec_cc.cc @@ -290,7 +290,8 @@ BOOST_AUTO_TEST_CASE(test_aggressive_nsec_wildcard_synthesis) setDNSSECValidation(sr, DNSSECMode::ValidateAll); primeHints(); - /* we first ask a.powerdns.com. | A, get an answer synthesized from the wildcard, + /* we first ask a.powerdns.com. | A, get an answer synthesized from the wildcard. + We can use it yet because we need the SOA, so let's request a non-existing type then check that the aggressive NSEC cache will use the wildcard to synthesize an answer for b.powerdns.com */ const DNSName target("a.powerdns.com."); @@ -342,13 +343,24 @@ BOOST_AUTO_TEST_CASE(test_aggressive_nsec_wildcard_synthesis) return LWResult::Result::Success; } else if (ip == ComboAddress("192.0.2.1:53")) { - setLWResult(res, RCode::NoError, true, false, true); - addRecordToLW(res, domain, QType::A, "192.0.2.1"); - addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300, false, boost::none, DNSName("*.powerdns.com")); - /* the name does not exist, a wildcard applies and has the requested type */ - addNSECRecordToLW(DNSName("*.powerdns.com."), DNSName("z.powerdns.com."), {QType::A, QType::RRSIG}, 600, res->d_records); - addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300, false, boost::none, DNSName("*.powerdns.com")); - return LWResult::Result::Success; + if (type == QType::A) { + setLWResult(res, RCode::NoError, true, false, true); + addRecordToLW(res, domain, QType::A, "192.0.2.1"); + addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300, false, boost::none, DNSName("*.powerdns.com")); + /* the name does not exist, a wildcard applies and has the requested type */ + addNSECRecordToLW(DNSName("*.powerdns.com."), DNSName("z.powerdns.com."), {QType::A, QType::RRSIG}, 600, res->d_records); + addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300, false, boost::none, DNSName("*.powerdns.com")); + return LWResult::Result::Success; + } + else if (type == QType::TXT) { + setLWResult(res, RCode::NoError, true, false, true); + /* the name does not exist, a wildcard applies but does not have the requested type */ + addRecordToLW(res, DNSName("powerdns.com."), QType::SOA, "powerdns.com. powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY); + addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300); + addNSECRecordToLW(DNSName("*.powerdns.com."), DNSName("z.powerdns.com."), {QType::A, QType::RRSIG}, 600, res->d_records); + addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300, false, boost::none, DNSName("*.powerdns.com")); + return LWResult::Result::Success; + } } } @@ -364,6 +376,16 @@ BOOST_AUTO_TEST_CASE(test_aggressive_nsec_wildcard_synthesis) BOOST_CHECK_EQUAL(ret.at(0).d_type, QType(QType::A).getCode()); BOOST_CHECK_EQUAL(queriesCount, 4U); + /* request the TXT to get the SOA */ + ret.clear(); + res = sr->beginResolve(target, QType(QType::TXT), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, RCode::NoError); + BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure); + BOOST_REQUIRE_EQUAL(ret.size(), 4U); + BOOST_CHECK_EQUAL(ret.at(0).d_name, DNSName("powerdns.com.")); + BOOST_CHECK_EQUAL(ret.at(0).d_type, QType(QType::SOA).getCode()); + BOOST_CHECK_EQUAL(queriesCount, 5U); + ret.clear(); res = sr->beginResolve(DNSName("b.powerdns.com."), QType(QType::A), QClass::IN, ret); BOOST_CHECK_EQUAL(res, RCode::NoError); @@ -371,7 +393,7 @@ BOOST_AUTO_TEST_CASE(test_aggressive_nsec_wildcard_synthesis) BOOST_REQUIRE_EQUAL(ret.size(), 4U); BOOST_CHECK_EQUAL(ret.at(0).d_name, DNSName("b.powerdns.com.")); BOOST_CHECK_EQUAL(ret.at(0).d_type, QType(QType::A).getCode()); - BOOST_CHECK_EQUAL(queriesCount, 4U); + BOOST_CHECK_EQUAL(queriesCount, 5U); } BOOST_AUTO_TEST_CASE(test_aggressive_nsec3_nxdomain) @@ -678,7 +700,8 @@ BOOST_AUTO_TEST_CASE(test_aggressive_nsec3_wildcard_synthesis) setDNSSECValidation(sr, DNSSECMode::ValidateAll); primeHints(); - /* we first ask a.powerdns.com. | A, get an answer synthesized from the wildcard, + /* we first ask a.powerdns.com. | A, get an answer synthesized from the wildcard. + We can't use it right away because we don't have the SOA, so let's do a TXT query to get it, then check that the aggressive NSEC cache will use the wildcard to synthesize an answer for b.powerdns.com */ const DNSName target("a.powerdns.com."); @@ -736,15 +759,33 @@ BOOST_AUTO_TEST_CASE(test_aggressive_nsec3_wildcard_synthesis) return LWResult::Result::Success; } else if (ip == ComboAddress("192.0.2.1:53")) { - setLWResult(res, RCode::NoError, true, false, true); - addRecordToLW(res, domain, QType::A, "192.0.2.1"); - addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300, false, boost::none, DNSName("*.powerdns.com")); - /* no need for the closest encloser since we have a positive answer expanded from a wildcard */ - /* the next closer */ - addNSEC3UnhashedRecordToLW(DNSName("+.powerdns.com."), DNSName("powerdns.com."), "v", {QType::RRSIG}, 600, res->d_records); - addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300); - /* and of course we don't deny the wildcard itself */ - return LWResult::Result::Success; + if (type == QType::A) { + setLWResult(res, RCode::NoError, true, false, true); + addRecordToLW(res, domain, QType::A, "192.0.2.1"); + addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300, false, boost::none, DNSName("*.powerdns.com")); + /* no need for the closest encloser since we have a positive answer expanded from a wildcard */ + /* the next closer */ + addNSEC3UnhashedRecordToLW(DNSName("+.powerdns.com."), DNSName("powerdns.com."), "v", {QType::RRSIG}, 600, res->d_records); + addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300); + /* and of course we don't deny the wildcard itself */ + return LWResult::Result::Success; + } + else if (type == QType::TXT) { + setLWResult(res, RCode::NoError, true, false, true); + /* the name does not exist, a wildcard applies but does not have the requested type */ + addRecordToLW(res, DNSName("powerdns.com."), QType::SOA, "powerdns.com. powerdns.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY); + addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300); + /* the closest encloser */ + addNSEC3UnhashedRecordToLW(DNSName("powerdns.com."), DNSName("powerdns.com."), "v", {QType::SOA, QType::NS, QType::NSEC3, QType::DNSKEY, QType::RRSIG}, 600, res->d_records); + addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300); + /* the next closer */ + addNSEC3UnhashedRecordToLW(DNSName("+.powerdns.com."), DNSName("powerdns.com."), "v", {QType::RRSIG}, 600, res->d_records); + addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300); + /* and the wildcard expanded unto itself */ + addNSEC3UnhashedRecordToLW(DNSName("*.powerdns.com."), DNSName("powerdns.com."), "v", {QType::A}, 600, res->d_records); + addRRSIG(keys, res->d_records, DNSName("powerdns.com."), 300); + return LWResult::Result::Success; + } } } @@ -760,6 +801,15 @@ BOOST_AUTO_TEST_CASE(test_aggressive_nsec3_wildcard_synthesis) BOOST_CHECK_EQUAL(ret.at(0).d_type, QType(QType::A).getCode()); BOOST_CHECK_EQUAL(queriesCount, 4U); + ret.clear(); + res = sr->beginResolve(target, QType(QType::TXT), QClass::IN, ret); + BOOST_CHECK_EQUAL(res, RCode::NoError); + BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure); + BOOST_REQUIRE_EQUAL(ret.size(), 8U); + BOOST_CHECK_EQUAL(ret.at(0).d_name, DNSName("powerdns.com.")); + BOOST_CHECK_EQUAL(ret.at(0).d_type, QType(QType::SOA).getCode()); + BOOST_CHECK_EQUAL(queriesCount, 5U); + ret.clear(); res = sr->beginResolve(DNSName("b.powerdns.com."), QType(QType::A), QClass::IN, ret); BOOST_CHECK_EQUAL(res, RCode::NoError); @@ -767,7 +817,7 @@ BOOST_AUTO_TEST_CASE(test_aggressive_nsec3_wildcard_synthesis) BOOST_REQUIRE_EQUAL(ret.size(), 4U); BOOST_CHECK_EQUAL(ret.at(0).d_name, DNSName("b.powerdns.com.")); BOOST_CHECK_EQUAL(ret.at(0).d_type, QType(QType::A).getCode()); - BOOST_CHECK_EQUAL(queriesCount, 4U); + BOOST_CHECK_EQUAL(queriesCount, 5U); } BOOST_AUTO_TEST_CASE(test_aggressive_nsec_wiping) diff --git a/regression-tests.recursor-dnssec/test_AggressiveNSECCache.py b/regression-tests.recursor-dnssec/test_AggressiveNSECCache.py index c114c9e4d3..342fad551c 100644 --- a/regression-tests.recursor-dnssec/test_AggressiveNSECCache.py +++ b/regression-tests.recursor-dnssec/test_AggressiveNSECCache.py @@ -239,7 +239,15 @@ class AggressiveNSECCacheNSEC3(AggressiveNSECCacheBase): def testWildcard(self): - # first we query a non-existent name, but for which a wildcard matches, + # first let's get the SOA and wildcard NSEC in our cache by asking a name that matches the wildcard + # but a type that does not exist + res = self.sendQuery('test1.wildcard.secure.example.', 'AAAA') + self.assertRcodeEqual(res, dns.rcode.NOERROR) + self.assertAnswerEmpty(res) + self.assertAuthorityHasSOA(res) + self.assertMessageIsAuthenticated(res) + + # we query a non-existent name, but for which a wildcard matches, # to get the NSEC3 in our cache res = self.sendQuery('test5.wildcard.secure.example.', 'A') expected = dns.rrset.from_text('test5.wildcard.secure.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.10'.format(prefix=self._PREFIX)) -- 2.47.3