From: Remi Gacogne Date: Thu, 20 Jul 2017 15:20:37 +0000 (+0200) Subject: rec: Add more tests of ECS-specific entries removal and expiration X-Git-Tag: dnsdist-1.2.0~48^2 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=1ffea92c7c22ec6dde69b37262baf612e55b7a66;p=pdns rec: Add more tests of ECS-specific entries removal and expiration --- diff --git a/pdns/recursor_cache.cc b/pdns/recursor_cache.cc index 475e7a1a7..0b9e32dca 100644 --- a/pdns/recursor_cache.cc +++ b/pdns/recursor_cache.cc @@ -14,13 +14,18 @@ #include "cachecleaner.hh" #include "namespaces.hh" -unsigned int MemRecursorCache::size() +unsigned int MemRecursorCache::size() const { return (unsigned int)d_cache.size(); } +size_t MemRecursorCache::ecsIndexSize() const +{ + return d_ecsIndex.size(); +} + // this function is too slow to poll! -unsigned int MemRecursorCache::bytes() +unsigned int MemRecursorCache::bytes() const { unsigned int ret=0; diff --git a/pdns/recursor_cache.hh b/pdns/recursor_cache.hh index e928d91c2..ca9cf4a98 100644 --- a/pdns/recursor_cache.hh +++ b/pdns/recursor_cache.hh @@ -53,8 +53,10 @@ public: { cacheHits = cacheMisses = 0; } - unsigned int size(); - unsigned int bytes(); + unsigned int size() const; + unsigned int bytes() const; + size_t ecsIndexSize() const; + int32_t get(time_t, const DNSName &qname, const QType& qt, bool requireAuth, vector* res, const ComboAddress& who, vector>* signatures=nullptr, std::vector>* authorityRecs=nullptr, bool* variable=nullptr, vState* state=nullptr, bool* wasAuth=nullptr); void replace(time_t, const DNSName &qname, const QType& qt, const vector& content, const vector>& signatures, const std::vector>& authorityRecs, bool auth, boost::optional ednsmask=boost::none, vState state=Indeterminate); diff --git a/pdns/recursordist/test-recursorcache_cc.cc b/pdns/recursordist/test-recursorcache_cc.cc index 162df7610..a1c658581 100644 --- a/pdns/recursordist/test-recursorcache_cc.cc +++ b/pdns/recursordist/test-recursorcache_cc.cc @@ -515,6 +515,216 @@ BOOST_AUTO_TEST_CASE(test_RecursorCache_ExpungingValidEntries) { BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr1Content.toString()); /* check that power2 is gone */ BOOST_CHECK_EQUAL(MRC.get(now, power2, QType(dr2.d_type), false, &retrieved, who, nullptr), -1); + + MRC.doPrune(0); + BOOST_CHECK_EQUAL(MRC.size(), 0); + + /* add a lot of netmask-specific entries */ + for (size_t i = 0; i <= 255; i++) { + records.clear(); + + DNSRecord r1; + ComboAddress r1Content("192.0.2." + std::to_string(i)); + r1.d_name = power1; + r1.d_type = QType::A; + r1.d_class = QClass::IN; + r1.d_content = std::make_shared(r1Content); + r1.d_ttl = static_cast(ttd); + r1.d_place = DNSResourceRecord::ANSWER; + records.push_back(r1); + + MRC.replace(now, power1, QType(QType::A), records, signatures, authRecs, true, Netmask(r1Content, 32)); + } + + BOOST_CHECK_EQUAL(MRC.size(), 256); + BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 1); + + /* remove a bit less than half of them */ + size_t keep = 129; + MRC.doPrune(keep); + BOOST_CHECK_EQUAL(MRC.size(), keep); + BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 1); + + /* check that we can still retrieve the remaining ones */ + size_t found = 0; + for (size_t i = 0; i <= 255; i++) { + retrieved.clear(); + ComboAddress who("192.0.2." + std::to_string(i)); + + auto ret = MRC.get(now, power1, QType(QType::A), false, &retrieved, who); + if (ret > 0) { + BOOST_REQUIRE_EQUAL(retrieved.size(), 1); + BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), who.toString()); + found++; + } + else { + BOOST_REQUIRE_EQUAL(ret, -1); + BOOST_REQUIRE_EQUAL(retrieved.size(), 0); + } + } + + BOOST_CHECK_EQUAL(found, keep); + + /* remove the rest */ + MRC.doPrune(0); + BOOST_CHECK_EQUAL(MRC.size(), 0); + BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 0); +} + +BOOST_AUTO_TEST_CASE(test_RecursorCacheECSIndex) { + MemRecursorCache MRC; + + const DNSName power("powerdns.com."); + std::vector records; + std::vector> authRecords; + std::vector> signatures; + time_t now = time(nullptr); + std::vector retrieved; + ComboAddress who("192.0.2.1"); + + time_t ttl = 10; + time_t ttd = now + ttl; + DNSRecord dr1; + ComboAddress dr1Content("192.0.2.255"); + dr1.d_name = power; + dr1.d_type = QType::A; + dr1.d_class = QClass::IN; + dr1.d_content = std::make_shared(dr1Content); + dr1.d_ttl = static_cast(ttd); + dr1.d_place = DNSResourceRecord::ANSWER; + + DNSRecord dr2; + ComboAddress dr2Content("192.0.2.127"); + dr2.d_name = power; + dr2.d_type = QType::A; + dr2.d_class = QClass::IN; + dr2.d_content = std::make_shared(dr2Content); + dr2.d_ttl = static_cast(now + 5); + dr2.d_place = DNSResourceRecord::ANSWER; + + BOOST_CHECK_EQUAL(MRC.size(), 0); + BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 0); + + /* no entry in the ECS index, no non-specific entry either */ + retrieved.clear(); + BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, who), -1); + + /* insert a non-specific entry */ + records.push_back(dr1); + MRC.replace(now, power, QType(QType::A), records, signatures, authRecords, true, boost::none); + + BOOST_CHECK_EQUAL(MRC.size(), 1); + BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 0); + + /* retrieve the non-specific entry */ + retrieved.clear(); + BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, who), ttd - now); + BOOST_REQUIRE_EQUAL(retrieved.size(), 1); + BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr1Content.toString()); + + /* wipe everything */ + MRC.doPrune(0); + BOOST_CHECK_EQUAL(MRC.size(), 0); + BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 0); + + /* insert a specific entry */ + MRC.replace(now, power, QType(QType::A), records, signatures, authRecords, true, Netmask("192.0.2.0/31")); + + BOOST_CHECK_EQUAL(MRC.size(), 1); + BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 1); + + /* there is an ECS index for that entry but no match, and no non-specific entry */ + retrieved.clear(); + BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.4")), -1); + BOOST_REQUIRE_EQUAL(retrieved.size(), 0); + + /* there is an ECS index for that entry and we get a match */ + retrieved.clear(); + BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.1")), ttd - now); + BOOST_REQUIRE_EQUAL(retrieved.size(), 1); + BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr1Content.toString()); + + /* there is an ECS index for that entry and we get a match, + but it has expired. No other match, no non-specific entry */ + retrieved.clear(); + BOOST_CHECK_EQUAL(MRC.get(now + ttl + 1, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.1")), -1); + BOOST_REQUIRE_EQUAL(retrieved.size(), 0); + + /* The ECS index should now be empty, but the cache entry has not been expunged yet */ + BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 0); + BOOST_CHECK_EQUAL(MRC.size(), 1); + + /* wipe everything */ + MRC.doPrune(0); + BOOST_CHECK_EQUAL(MRC.size(), 0); + BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 0); + + /* insert a specific entry */ + MRC.replace(now, power, QType(QType::A), records, signatures, authRecords, true, Netmask("192.0.2.0/24")); + + BOOST_CHECK_EQUAL(MRC.size(), 1); + BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 1); + + /* insert a slightly more specific one, but expiring sooner */ + records.clear(); + records.push_back(dr2); + MRC.replace(now, power, QType(QType::A), records, signatures, authRecords, true, Netmask("192.0.2.0/26")); + + BOOST_CHECK_EQUAL(MRC.size(), 2); + BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 1); + + /* check that we get the most specific one as long as it's still valid */ + retrieved.clear(); + BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.1")), 5); + BOOST_REQUIRE_EQUAL(retrieved.size(), 1); + BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr2Content.toString()); + + /* there is an ECS index for that entry and we get a match, + but it has expired. + The second ECS is a match too, and is valid. */ + retrieved.clear(); + BOOST_CHECK_EQUAL(MRC.get(now + 5 + 1, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.1")), (ttd - (now +5 + 1))); + BOOST_REQUIRE_EQUAL(retrieved.size(), 1); + BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr1Content.toString()); + + /* The ECS index should not be empty */ + BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 1); + BOOST_CHECK_EQUAL(MRC.size(), 2); + + /* wipe everything */ + MRC.doPrune(0); + BOOST_CHECK_EQUAL(MRC.size(), 0); + BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 0); + + /* insert a non-specific entry */ + records.clear(); + records.push_back(dr1); + MRC.replace(now, power, QType(QType::A), records, signatures, authRecords, true, boost::none); + + BOOST_CHECK_EQUAL(MRC.size(), 1); + BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 0); + + /* insert a subnet-specific entry */ + records.clear(); + records.push_back(dr2); + MRC.replace(now, power, QType(QType::A), records, signatures, authRecords, true, Netmask("192.0.2.42/32")); + + BOOST_CHECK_EQUAL(MRC.size(), 2); + BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 1); + + /* there is an ECS index for that entry and it doesn't match. No other match, but we have a non-specific entry */ + retrieved.clear(); + BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.255")), ttd - now); + BOOST_REQUIRE_EQUAL(retrieved.size(), 1); + BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr1Content.toString()); + + BOOST_CHECK_EQUAL(MRC.size(), 2); + BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 1); + + /* wipe everything */ + MRC.doPrune(0); + BOOST_CHECK_EQUAL(MRC.size(), 0); + BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 0); } BOOST_AUTO_TEST_SUITE_END()