From d6fd3cb887bf165b6a1b0e398e20e57ed90691bd Mon Sep 17 00:00:00 2001 From: Otto Moerbeek Date: Wed, 12 Feb 2020 11:55:56 +0100 Subject: [PATCH] Introduce routingTag, settable from lua and passed to SyncRes if set. SyncRes passes it on to the record cache methods that do not use it yet. --- pdns/lua-recursor4.cc | 1 + pdns/lua-recursor4.hh | 1 + pdns/pdns_recursor.cc | 5 + pdns/recursor_cache.cc | 184 ++++++++++++---- pdns/recursor_cache.hh | 42 ++-- pdns/recursordist/test-recursorcache_cc.cc | 240 ++++++++++++++++++--- pdns/recursordist/test-syncres_cc3.cc | 2 +- pdns/recursordist/test-syncres_cc8.cc | 2 +- pdns/recursordist/test-syncres_cc9.cc | 4 +- pdns/reczones.cc | 10 +- pdns/syncres.cc | 20 +- pdns/syncres.hh | 2 + 12 files changed, 399 insertions(+), 114 deletions(-) diff --git a/pdns/lua-recursor4.cc b/pdns/lua-recursor4.cc index 60a5815cc7..931b3bb4b7 100644 --- a/pdns/lua-recursor4.cc +++ b/pdns/lua-recursor4.cc @@ -169,6 +169,7 @@ void RecursorLua4::postPrepareContext() d_lw->registerMember("requestorId", &DNSQuestion::requestorId); d_lw->registerMember("deviceId", &DNSQuestion::deviceId); d_lw->registerMember("deviceName", &DNSQuestion::deviceName); + d_lw->registerMember("routingTag", &DNSQuestion::routingTag); d_lw->registerMember("followupFunction", &DNSQuestion::followupFunction); d_lw->registerMember("followupPrefix", &DNSQuestion::followupPrefix); d_lw->registerMember("followupName", &DNSQuestion::followupName); diff --git a/pdns/lua-recursor4.hh b/pdns/lua-recursor4.hh index 5b3dcb5002..1639583069 100644 --- a/pdns/lua-recursor4.hh +++ b/pdns/lua-recursor4.hh @@ -81,6 +81,7 @@ public: std::string requestorId; std::string deviceId; std::string deviceName; + std::string routingTag; vState validationState{Indeterminate}; bool& variable; bool& wantsRPZ; diff --git a/pdns/pdns_recursor.cc b/pdns/pdns_recursor.cc index 2e830e06a4..f0ffdfe971 100644 --- a/pdns/pdns_recursor.cc +++ b/pdns/pdns_recursor.cc @@ -1498,6 +1498,11 @@ static void startDoResolve(void *p) try { sr.d_appliedPolicy = appliedPolicy; sr.d_policyTags = std::move(dc->d_policyTags); + + if (!dq.routingTag.empty()) { + sr.d_routingTag = dq.routingTag; + } + res = sr.beginResolve(dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_mdp.d_qclass, ret); shouldNotValidate = sr.wasOutOfBand(); } diff --git a/pdns/recursor_cache.cc b/pdns/recursor_cache.cc index 7c813317be..13ecd97975 100644 --- a/pdns/recursor_cache.cc +++ b/pdns/recursor_cache.cc @@ -79,12 +79,12 @@ size_t MemRecursorCache::bytes() return ret; } -int32_t MemRecursorCache::handleHit(MapCombo& map, MemRecursorCache::OrderedTagIterator_t& entry, const DNSName& qname, const ComboAddress& who, vector* res, vector>* signatures, std::vector>* authorityRecs, bool* variable, vState* state, bool* wasAuth) +int32_t MemRecursorCache::handleHit(MapCombo& map, MemRecursorCache::OrderedTagIterator_t& entry, const DNSName& qname, vector* res, vector>* signatures, std::vector>* authorityRecs, bool* variable, vState* state, bool* wasAuth) { // MUTEX SHOULD BE ACQUIRED int32_t ttd = entry->d_ttd; - if(variable && !entry->d_netmask.empty()) { + if (variable && (!entry->d_netmask.empty() || entry->d_rtag)) { *variable = true; } @@ -138,7 +138,7 @@ MemRecursorCache::cache_t::const_iterator MemRecursorCache::getEntryUsingECSInde /* we have nothing more specific for you */ break; } - auto key = boost::make_tuple(qname, qtype, best); + auto key = boost::make_tuple(qname, qtype, boost::none, best); auto entry = map.d_map.find(key); if (entry == map.d_map.end()) { /* ecsIndex is not up-to-date */ @@ -170,7 +170,7 @@ MemRecursorCache::cache_t::const_iterator MemRecursorCache::getEntryUsingECSInde } /* we have nothing specific, let's see if we have a generic one */ - auto key = boost::make_tuple(qname, qtype, Netmask()); + auto key = boost::make_tuple(qname, qtype, boost::none, Netmask()); auto entry = map.d_map.find(key); if (entry != map.d_map.end()) { if (entry->d_ttd > now) { @@ -187,31 +187,49 @@ MemRecursorCache::cache_t::const_iterator MemRecursorCache::getEntryUsingECSInde return map.d_map.end(); } -std::pair MemRecursorCache::getEntries(MapCombo& map, const DNSName &qname, const QType& qt) +MemRecursorCache::Entries MemRecursorCache::getEntries(MapCombo& map, const DNSName &qname, const QType& qt, const OptTag& rtag ) { // MUTEX SHOULD BE ACQUIRED - if (!map.d_cachecachevalid || map.d_cachedqname != qname) { + if (!map.d_cachecachevalid || map.d_cachedqname != qname || map.d_cachedrtag != rtag) { map.d_cachedqname = qname; - const auto& idx = map.d_map.get(); - map.d_cachecache = idx.equal_range(qname); + map.d_cachedrtag = rtag; + const auto& idx = map.d_map.get(); + map.d_cachecache = idx.equal_range(tie(qname, rtag)); map.d_cachecachevalid = true; } return map.d_cachecache; } +#include + + bool MemRecursorCache::entryMatches(MemRecursorCache::OrderedTagIterator_t& entry, uint16_t qt, bool requireAuth, const ComboAddress& who) { // MUTEX SHOULD BE ACQUIRED if (requireAuth && !entry->d_auth) return false; - return ((entry->d_qtype == qt || qt == QType::ANY || - (qt == QType::ADDR && (entry->d_qtype == QType::A || entry->d_qtype == QType::AAAA))) - && (entry->d_netmask.empty() || entry->d_netmask.match(who))); + bool match = (entry->d_qtype == qt || qt == QType::ANY || + (qt == QType::ADDR && (entry->d_qtype == QType::A || entry->d_qtype == QType::AAAA))) + && (entry->d_netmask.empty() || entry->d_netmask.match(who)); + //cerr << match << "N " << qt << ':' << entry->d_qtype << ' ' << entry->d_netmask.toString() << ':' << who.toString() << endl; + return match; +} + +bool MemRecursorCache::entryMatches(MemRecursorCache::OrderedTagIterator_t& entry, uint16_t qt, bool requireAuth) +{ + // MUTEX SHOULD BE ACQUIRED + if (requireAuth && !entry->d_auth) + return false; + + bool match = (entry->d_qtype == qt || qt == QType::ANY || + (qt == QType::ADDR && (entry->d_qtype == QType::A || entry->d_qtype == QType::AAAA))); + //cerr << match << "T " << qt << ':' << entry->d_qtype << ' ' << entry->d_rtag << endl; + return match; } // returns -1 for no hits -int32_t MemRecursorCache::get(time_t now, const DNSName &qname, const QType& qt, bool requireAuth, vector* res, const ComboAddress& who, vector>* signatures, std::vector>* authorityRecs, bool* variable, vState* state, bool* wasAuth) +int32_t MemRecursorCache::get(time_t now, const DNSName &qname, const QType& qt, bool requireAuth, vector* res, const ComboAddress& who, const OptTag& routingTag, vector>* signatures, std::vector>* authorityRecs, bool* variable, vState* state, bool* wasAuth) { time_t ttd=0; // cerr<<"looking up "<< qname<<"|"+qt.getName()<<"\n"; @@ -222,21 +240,20 @@ int32_t MemRecursorCache::get(time_t now, const DNSName &qname, const QType& qt, auto& map = getMap(qname); const lock l(map); - + /* If we don't have any netmask-specific entries at all, let's just skip this to be able to use the nice d_cachecache hack. */ - if (qtype != QType::ANY && !map.d_ecsIndex.empty()) { - + if (qtype != QType::ANY && !map.d_ecsIndex.empty() && !routingTag) { if (qtype == QType::ADDR) { int32_t ret = -1; auto entryA = getEntryUsingECSIndex(map, now, qname, QType::A, requireAuth, who); if (entryA != map.d_map.end()) { - ret = handleHit(map, entryA, qname, who, res, signatures, authorityRecs, variable, state, wasAuth); + ret = handleHit(map, entryA, qname, res, signatures, authorityRecs, variable, state, wasAuth); } auto entryAAAA = getEntryUsingECSIndex(map, now, qname, QType::AAAA, requireAuth, who); if (entryAAAA != map.d_map.end()) { - int32_t ttdAAAA = handleHit(map, entryAAAA, qname, who, res, signatures, authorityRecs, variable, state, wasAuth); + int32_t ttdAAAA = handleHit(map, entryAAAA, qname, res, signatures, authorityRecs, variable, state, wasAuth); if (ret > 0) { ret = std::min(ret, ttdAAAA); } else { @@ -248,39 +265,92 @@ int32_t MemRecursorCache::get(time_t now, const DNSName &qname, const QType& qt, else { auto entry = getEntryUsingECSIndex(map, now, qname, qtype, requireAuth, who); if (entry != map.d_map.end()) { - return static_cast(handleHit(map, entry, qname, who, res, signatures, authorityRecs, variable, state, wasAuth) - now); + return static_cast(handleHit(map, entry, qname, res, signatures, authorityRecs, variable, state, wasAuth) - now); } return -1; } } - auto entries = getEntries(map, qname, qt); + if (!routingTag) { + auto entries = getEntries(map, qname, qt, boost::none); - if(entries.first!=entries.second) { - for(auto i=entries.first; i != entries.second; ++i) { + if (entries.first != entries.second) { + for (auto i=entries.first; i != entries.second; ++i) { - auto firstIndexIterator = map.d_map.project(i); - if (i->d_ttd <= now) { - moveCacheItemToFront(map.d_map, firstIndexIterator); - continue; + auto firstIndexIterator = map.d_map.project(i); + if (i->d_ttd <= now) { + moveCacheItemToFront(map.d_map, firstIndexIterator); + continue; + } + + if (!entryMatches(firstIndexIterator, qtype, requireAuth, who)) { + continue; + } + + ttd = handleHit(map, firstIndexIterator, qname, res, signatures, authorityRecs, variable, state, wasAuth); + + if (qt.getCode() != QType::ANY && qt.getCode() != QType::ADDR) { // normally if we have a hit, we are done + break; + } } - if (!entryMatches(firstIndexIterator, qtype, requireAuth, who)) - continue; + // cerr<<"time left : "<size() : 0) <<"\n"; + return static_cast(ttd-now); + } + } + else { + auto entries = getEntries(map, qname, qt, routingTag); - ttd = handleHit(map, firstIndexIterator, qname, who, res, signatures, authorityRecs, variable, state, wasAuth); + if (entries.first != entries.second) { + for (auto i=entries.first; i != entries.second; ++i) { - if(qt.getCode()!=QType::ANY && qt.getCode()!=QType::ADDR) // normally if we have a hit, we are done - break; + auto firstIndexIterator = map.d_map.project(i); + if (i->d_ttd <= now) { + moveCacheItemToFront(map.d_map, firstIndexIterator); + continue; + } + + if (!entryMatches(firstIndexIterator, qtype, requireAuth)) { + continue; + } + + ttd = handleHit(map, firstIndexIterator, qname, res, signatures, authorityRecs, variable, state, wasAuth); + + if (qt.getCode() != QType::ANY && qt.getCode() != QType::ADDR) { // normally if we have a hit, we are done + break; + } + } + return static_cast(ttd-now); } + // Try again without tag + entries = getEntries(map, qname, qt, boost::none); - // cerr<<"time left : "<size() : 0) <<"\n"; - return static_cast(ttd-now); + if (entries.first != entries.second) { + for (auto i=entries.first; i != entries.second; ++i) { + + auto firstIndexIterator = map.d_map.project(i); + if (i->d_ttd <= now) { + moveCacheItemToFront(map.d_map, firstIndexIterator); + continue; + } + + if (!entryMatches(firstIndexIterator, qtype, requireAuth)) { + continue; + } + + ttd = handleHit(map, firstIndexIterator, qname, res, signatures, authorityRecs, variable, state, wasAuth); + + if (qt.getCode() != QType::ANY && qt.getCode() != QType::ADDR) { // normally if we have a hit, we are done + break; + } + } + return static_cast(ttd-now); + } } return -1; } -void MemRecursorCache::replace(time_t now, const DNSName &qname, const QType& qt, const vector& content, const vector>& signatures, const std::vector>& authorityRecs, bool auth, boost::optional ednsmask, vState state) +void MemRecursorCache::replace(time_t now, const DNSName &qname, const QType& qt, const vector& content, const vector>& signatures, const std::vector>& authorityRecs, bool auth, boost::optional ednsmask, const OptTag& routingTag, vState state) { auto& map = getMap(qname); const lock l(map); @@ -290,7 +360,10 @@ void MemRecursorCache::replace(time_t now, const DNSName &qname, const QType& qt if (ednsmask) { ednsmask = ednsmask->getNormalized(); } - auto key = boost::make_tuple(qname, qt.getCode(), ednsmask ? *ednsmask : Netmask()); + + // We only store with a tag if we have an ednsmask and the tag is available + // We only store an ednsmask if we do not have a tag and we do have a mask. + auto key = boost::make_tuple(qname, qt.getCode(), ednsmask ? routingTag : boost::none, (ednsmask && !routingTag) ? *ednsmask : Netmask()); bool isNew = false; cache_t::iterator stored = map.d_map.find(key); if (stored == map.d_map.end()) { @@ -306,7 +379,7 @@ void MemRecursorCache::replace(time_t now, const DNSName &qname, const QType& qt */ if (isNew || stored->d_ttd <= now) { /* don't bother building an ecsIndex if we don't have any netmask-specific entries */ - if (ednsmask && !ednsmask->empty()) { + if (!routingTag && ednsmask && !ednsmask->empty()) { auto ecsIndexKey = boost::make_tuple(qname, qt.getCode()); auto ecsIndex = map.d_ecsIndex.find(ecsIndexKey); if (ecsIndex == map.d_ecsIndex.end()) { @@ -329,7 +402,7 @@ void MemRecursorCache::replace(time_t now, const DNSName &qname, const QType& qt if(!auth && ce.d_auth) { // unauth data came in, we have some auth data, but is it fresh? if(ce.d_ttd > now) { // we still have valid data, ignore unauth data - // cerr<<"\tStill hold valid auth data, and the new data is unauth, return\n"; + // cerr<<"\tStill hold valid auth data, and the new data is unauth, return\n"; return; } else { @@ -357,7 +430,7 @@ void MemRecursorCache::replace(time_t now, const DNSName &qname, const QType& qt /* Yes, we have altered the d_ttl value by adding time(nullptr) to it prior to calling this function, so the TTL actually holds a TTD. */ ce.d_ttd=min(maxTTD, static_cast(i.d_ttl)); // XXX this does weird things if TTLs differ in the set - // cerr<<"To store: "<getZoneRepresentation()<<" with ttl/ttd "<getZoneRepresentation()<<" with ttl/ttd "<(); - size_t n = idx.erase(name); - count += n; - map.d_entriesCount -= n; + auto& idx = map.d_map.get(); + auto i = idx.lower_bound(name); + auto upper_bound = idx.upper_bound(name); + while (i != upper_bound) { + i = idx.erase(i); + count++; + map.d_entriesCount--; + } + if (qtype == 0xffff) { auto& ecsIdx = map.d_ecsIndex.get(); auto ecsIndexRange = ecsIdx.equal_range(name); @@ -450,14 +528,14 @@ bool MemRecursorCache::doAgeCache(time_t now, const DNSName& name, uint16_t qtyp return false; } -bool MemRecursorCache::updateValidationStatus(time_t now, const DNSName &qname, const QType& qt, const ComboAddress& who, bool requireAuth, vState newState, boost::optional capTTD) +bool MemRecursorCache::updateValidationStatus(time_t now, const DNSName &qname, const QType& qt, const ComboAddress& who, const OptTag& routingTag, bool requireAuth, vState newState, boost::optional capTTD) { auto& map = getMap(qname); const lock l(map); bool updated = false; uint16_t qtype = qt.getCode(); - if (qtype != QType::ANY && qtype != QType::ADDR && !map.d_ecsIndex.empty()) { + if (qtype != QType::ANY && qtype != QType::ADDR && !map.d_ecsIndex.empty() && !routingTag) { auto entry = getEntryUsingECSIndex(map, now, qname, qtype, requireAuth, who); if (entry == map.d_map.end()) { return false; @@ -470,13 +548,19 @@ bool MemRecursorCache::updateValidationStatus(time_t now, const DNSName &qname, return true; } - auto entries = getEntries(map, qname, qt); + auto entries = getEntries(map, qname, qt, routingTag); for(auto i = entries.first; i != entries.second; ++i) { auto firstIndexIterator = map.d_map.project(i); - if (!entryMatches(firstIndexIterator, qtype, requireAuth, who)) - continue; + if (routingTag) { + if (!entryMatches(firstIndexIterator, qtype, requireAuth)) { + continue; + } + } else { + if (!entryMatches(firstIndexIterator, qtype, requireAuth, who)) + continue; + } i->d_state = newState; if (capTTD) { @@ -515,7 +599,7 @@ uint64_t MemRecursorCache::doDump(int fd) for (const auto& j : i.d_records) { count++; try { - fprintf(fp.get(), "%s %" PRId64 " IN %s %s ; (%s) auth=%i %s\n", i.d_qname.toString().c_str(), static_cast(i.d_ttd - now), DNSRecordContent::NumberToType(i.d_qtype).c_str(), j->getZoneRepresentation().c_str(), vStates[i.d_state], i.d_auth, i.d_netmask.empty() ? "" : i.d_netmask.toString().c_str()); + fprintf(fp.get(), "%s %" PRId64 " IN %s %s ; (%s) auth=%i %s %s\n", i.d_qname.toString().c_str(), static_cast(i.d_ttd - now), DNSRecordContent::NumberToType(i.d_qtype).c_str(), j->getZoneRepresentation().c_str(), vStates[i.d_state], i.d_auth, i.d_netmask.empty() ? "" : i.d_netmask.toString().c_str(), !i.d_rtag ? "" : i.d_rtag.get().c_str()); } catch(...) { fprintf(fp.get(), "; error printing '%s'\n", i.d_qname.empty() ? "EMPTY" : i.d_qname.toString().c_str()); @@ -542,3 +626,9 @@ void MemRecursorCache::doPrune(size_t keep) pruneMutexCollectionsVector(*this, d_maps, keep, cacheSize); } +namespace boost { + size_t hash_value(const MemRecursorCache::OptTag& o) + { + return o ? hash_value(o.get()) : 0xcafebaaf; + } +} diff --git a/pdns/recursor_cache.hh b/pdns/recursor_cache.hh index 2b0305d82a..5ec2a6c933 100644 --- a/pdns/recursor_cache.hh +++ b/pdns/recursor_cache.hh @@ -55,16 +55,18 @@ public: pair stats(); size_t ecsIndexSize(); - 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); + typedef boost::optional OptTag; - 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); + int32_t get(time_t, const DNSName &qname, const QType& qt, bool requireAuth, vector* res, const ComboAddress& who, const OptTag& routingTag = boost::none, 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, const OptTag& routingTag = boost::none, vState state=Indeterminate); void doPrune(size_t keep); uint64_t doDump(int fd); size_t doWipeCache(const DNSName& name, bool sub, uint16_t qtype=0xffff); bool doAgeCache(time_t now, const DNSName& name, uint16_t qtype, uint32_t newTTL); - bool updateValidationStatus(time_t now, const DNSName &qname, const QType& qt, const ComboAddress& who, bool requireAuth, vState newState, boost::optional capTTD); + bool updateValidationStatus(time_t now, const DNSName &qname, const QType& qt, const ComboAddress& who, const OptTag& routingTag, bool requireAuth, vState newState, boost::optional capTTD); std::atomic cacheHits{0}, cacheMisses{0}; @@ -72,8 +74,8 @@ private: struct CacheEntry { - CacheEntry(const boost::tuple& key, bool auth): - d_qname(key.get<0>()), d_netmask(key.get<2>().getNormalized()), d_state(Indeterminate), d_ttd(0), d_qtype(key.get<1>()), d_auth(auth) + CacheEntry(const boost::tuple& key, bool auth): + d_qname(key.get<0>()), d_netmask(key.get<3>().getNormalized()), d_rtag(key.get<2>()), d_state(Indeterminate), d_ttd(0), d_qtype(key.get<1>()), d_auth(auth) { } @@ -88,6 +90,7 @@ private: std::vector> d_authorityRecs; DNSName d_qname; Netmask d_netmask; + OptTag d_rtag; mutable vState d_state; mutable time_t d_ttd; uint16_t d_qtype; @@ -143,7 +146,7 @@ private: struct HashedTag {}; struct SequencedTag {}; - struct NameOnlyHashedTag {}; + struct NameAndRTagOnlyHashedTag {}; struct OrderedTag {}; typedef multi_index_container< @@ -154,19 +157,24 @@ private: CacheEntry, member, member, + member, member >, - composite_key_compare, std::less > + composite_key_compare, std::less, std::less > >, sequenced >, - hashed_non_unique, - member + hashed_non_unique, + composite_key< + CacheEntry, + member, + member + > > > > cache_t; typedef MemRecursorCache::cache_t::index::type::iterator OrderedTagIterator_t; - typedef MemRecursorCache::cache_t::index::type::iterator NameOnlyHashedTagIterator_t; + typedef MemRecursorCache::cache_t::index::type::iterator NameAndRTagOnlyHashedTagIterator_t; typedef multi_index_container< ECSIndexEntry, @@ -189,6 +197,8 @@ private: > > ecsIndex_t; + typedef std::pair Entries; + struct MapCombo { MapCombo() {} @@ -197,7 +207,8 @@ private: cache_t d_map; ecsIndex_t d_ecsIndex; DNSName d_cachedqname; - std::pair d_cachecache; + OptTag d_cachedrtag; + Entries d_cachecache; std::mutex mutex; bool d_cachecachevalid{false}; std::atomic d_entriesCount{0}; @@ -212,9 +223,10 @@ private: } bool entryMatches(OrderedTagIterator_t& entry, uint16_t qt, bool requireAuth, const ComboAddress& who); - std::pair getEntries(MapCombo& map, const DNSName &qname, const QType& qt); + bool entryMatches(OrderedTagIterator_t& entry, uint16_t qt, bool requireAuth); + Entries getEntries(MapCombo& map, const DNSName &qname, const QType& qt, const OptTag& rtag); cache_t::const_iterator getEntryUsingECSIndex(MapCombo& map, time_t now, const DNSName &qname, uint16_t qtype, bool requireAuth, const ComboAddress& who); - int32_t handleHit(MapCombo& map, OrderedTagIterator_t& entry, const DNSName& qname, const ComboAddress& who, vector* res, vector>* signatures, std::vector>* authorityRecs, bool* variable, vState* state, bool* wasAuth); + int32_t handleHit(MapCombo& map, OrderedTagIterator_t& entry, const DNSName& qname, vector* res, vector>* signatures, std::vector>* authorityRecs, bool* variable, vState* state, bool* wasAuth); public: struct lock { @@ -250,3 +262,7 @@ public: } } }; + +namespace boost { + size_t hash_value(const MemRecursorCache::OptTag& rtag); +} diff --git a/pdns/recursordist/test-recursorcache_cc.cc b/pdns/recursordist/test-recursorcache_cc.cc index 9a4b60963c..1c1dea7131 100644 --- a/pdns/recursordist/test-recursorcache_cc.cc +++ b/pdns/recursordist/test-recursorcache_cc.cc @@ -69,7 +69,7 @@ BOOST_AUTO_TEST_CASE(test_RecursorCacheSimple) int64_t expected = counter - delcounter; for (; delcounter < counter; ++delcounter) { - if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(delcounter)), QType(QType::A), false, &retrieved, who, nullptr) > 0) { + if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(delcounter)), QType(QType::A), false, &retrieved, who) > 0) { matches++; BOOST_REQUIRE_EQUAL(retrieved.size(), records.size()); BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr0Content.toString()); @@ -106,12 +106,12 @@ BOOST_AUTO_TEST_CASE(test_RecursorCacheSimple) BOOST_CHECK_EQUAL(MRC.size(), 1U); // subnet specific should be returned for a matching subnet - BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::AAAA), false, &retrieved, ComboAddress("192.0.2.2"), nullptr), (ttd - now)); + BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::AAAA), false, &retrieved, ComboAddress("192.0.2.2")), (ttd - now)); BOOST_REQUIRE_EQUAL(retrieved.size(), 1U); BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr1Content.toString()); // subnet specific should not be returned for a different subnet - BOOST_CHECK_LT(MRC.get(now, power, QType(QType::AAAA), false, &retrieved, ComboAddress("127.0.0.1"), nullptr), 0); + BOOST_CHECK_LT(MRC.get(now, power, QType(QType::AAAA), false, &retrieved, ComboAddress("127.0.0.1")), 0); BOOST_CHECK_EQUAL(retrieved.size(), 0U); // remove everything @@ -125,7 +125,7 @@ BOOST_AUTO_TEST_CASE(test_RecursorCacheSimple) BOOST_CHECK_EQUAL(MRC.size(), 1U); // NON-subnet specific should always be returned - BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1"), nullptr), (ttd - now)); + BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1")), (ttd - now)); BOOST_REQUIRE_EQUAL(retrieved.size(), 1U); BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr2Content.toString()); @@ -144,21 +144,21 @@ BOOST_AUTO_TEST_CASE(test_RecursorCacheSimple) BOOST_CHECK_EQUAL(MRC.size(), 3U); // we should still get the NON-subnet specific entry - BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1"), nullptr), (ttd - now)); + BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1")), (ttd - now)); BOOST_REQUIRE_EQUAL(retrieved.size(), 1U); BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr2Content.toString()); // we should get the subnet specific entry if we are from the right subnet - BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::AAAA), false, &retrieved, ComboAddress("192.0.2.3"), nullptr), (ttd - now)); + BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::AAAA), false, &retrieved, ComboAddress("192.0.2.3")), (ttd - now)); BOOST_REQUIRE_EQUAL(retrieved.size(), 1U); BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr1Content.toString()); // but nothing from a different subnet - BOOST_CHECK_LT(MRC.get(now, power, QType(QType::AAAA), false, &retrieved, ComboAddress("127.0.0.1"), nullptr), 0); + BOOST_CHECK_LT(MRC.get(now, power, QType(QType::AAAA), false, &retrieved, ComboAddress("127.0.0.1")), 0); BOOST_CHECK_EQUAL(retrieved.size(), 0U); // QType::ANY should return any qtype, so from the right subnet we should get all of them - BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::ANY), false, &retrieved, ComboAddress("192.0.2.3"), nullptr), (ttd - now)); + BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::ANY), false, &retrieved, ComboAddress("192.0.2.3")), (ttd - now)); BOOST_CHECK_EQUAL(retrieved.size(), 3U); for (const auto& rec : retrieved) { BOOST_CHECK(rec.d_type == QType::A || rec.d_type == QType::AAAA || rec.d_type == QType::TXT); @@ -169,14 +169,14 @@ BOOST_AUTO_TEST_CASE(test_RecursorCacheSimple) } // but only the non-subnet specific from the another subnet - BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::ANY), false, &retrieved, ComboAddress("127.0.0.1"), nullptr), (ttd - now)); + BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::ANY), false, &retrieved, ComboAddress("127.0.0.1")), (ttd - now)); BOOST_CHECK_EQUAL(retrieved.size(), 2U); for (const auto& rec : retrieved) { BOOST_CHECK(rec.d_type == QType::A || rec.d_type == QType::TXT); } // QType::ADDR should return both A and AAAA but no TXT, so two entries from the right subnet - BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::ADDR), false, &retrieved, ComboAddress("192.0.2.3"), nullptr), (ttd - now)); + BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::ADDR), false, &retrieved, ComboAddress("192.0.2.3")), (ttd - now)); BOOST_CHECK_EQUAL(retrieved.size(), 2U); bool gotA = false; bool gotAAAA = false; @@ -193,12 +193,12 @@ BOOST_AUTO_TEST_CASE(test_RecursorCacheSimple) BOOST_CHECK(gotAAAA); // but only the non-subnet specific one from the another subnet - BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::ADDR), false, &retrieved, ComboAddress("127.0.0.1"), nullptr), (ttd - now)); + BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::ADDR), false, &retrieved, ComboAddress("127.0.0.1")), (ttd - now)); BOOST_REQUIRE_EQUAL(retrieved.size(), 1U); BOOST_CHECK(retrieved.at(0).d_type == QType::A); // entries are only valid until ttd, we should not get anything after that because they are expired - BOOST_CHECK_LT(MRC.get(ttd + 5, power, QType(QType::ADDR), false, &retrieved, ComboAddress("127.0.0.1"), nullptr), 0); + BOOST_CHECK_LT(MRC.get(ttd + 5, power, QType(QType::ADDR), false, &retrieved, ComboAddress("127.0.0.1")), 0); BOOST_CHECK_EQUAL(retrieved.size(), 0U); // let's age the records for our existing QType::TXT entry so they are now only valid for 5s @@ -206,14 +206,14 @@ BOOST_AUTO_TEST_CASE(test_RecursorCacheSimple) BOOST_CHECK_EQUAL(MRC.doAgeCache(now, power, QType::TXT, newTTL), true); // we should still be able to retrieve it - BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::TXT), false, &retrieved, ComboAddress("127.0.0.1"), nullptr), static_cast(newTTL)); + BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::TXT), false, &retrieved, ComboAddress("127.0.0.1")), static_cast(newTTL)); BOOST_CHECK_EQUAL(retrieved.size(), 1U); BOOST_CHECK(retrieved.at(0).d_type == QType::TXT); // please note that this is still a TTD at this point BOOST_CHECK_EQUAL(retrieved.at(0).d_ttl, now + newTTL); // but 10s later it should be gone - BOOST_CHECK_LT(MRC.get(now + 10, power, QType(QType::TXT), false, &retrieved, ComboAddress("127.0.0.1"), nullptr), 0); + BOOST_CHECK_LT(MRC.get(now + 10, power, QType(QType::TXT), false, &retrieved, ComboAddress("127.0.0.1")), 0); BOOST_CHECK_EQUAL(retrieved.size(), 0U); // wipe everything @@ -225,7 +225,7 @@ BOOST_AUTO_TEST_CASE(test_RecursorCacheSimple) records.push_back(dr2); MRC.replace(now, power, QType(QType::A), records, signatures, authRecords, true, boost::none); BOOST_CHECK_EQUAL(MRC.size(), 1U); - BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1"), nullptr), (ttd - now)); + BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1")), (ttd - now)); BOOST_CHECK_EQUAL(retrieved.size(), 1U); DNSRecord dr3; @@ -247,14 +247,14 @@ BOOST_AUTO_TEST_CASE(test_RecursorCacheSimple) // non-auth should not replace valid auth MRC.replace(now, power, QType(QType::A), records, signatures, authRecords, false, boost::none); BOOST_CHECK_EQUAL(MRC.size(), 1U); - BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1"), nullptr), (ttd - now)); + BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1")), (ttd - now)); BOOST_REQUIRE_EQUAL(retrieved.size(), 1U); BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr2Content.toString()); // but non-auth _should_ replace expired auth MRC.replace(ttd + 1, power, QType(QType::A), records, signatures, authRecords, false, boost::none); BOOST_CHECK_EQUAL(MRC.size(), 1U); - BOOST_CHECK_EQUAL(MRC.get(ttd + 1, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1"), nullptr), (dr3.d_ttl - (ttd + 1))); + BOOST_CHECK_EQUAL(MRC.get(ttd + 1, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1")), (dr3.d_ttl - (ttd + 1))); BOOST_REQUIRE_EQUAL(retrieved.size(), 1U); BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr3Content.toString()); @@ -264,8 +264,8 @@ BOOST_AUTO_TEST_CASE(test_RecursorCacheSimple) MRC.replace(now, power, QType(QType::A), records, signatures, authRecords, false, boost::none); BOOST_CHECK_EQUAL(MRC.size(), 1U); // let's first check that non-auth is not returned when we need authoritative data - BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), true, &retrieved, ComboAddress("127.0.0.1"), nullptr), -now); - BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1"), nullptr), (ttd - now)); + BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), true, &retrieved, ComboAddress("127.0.0.1")), -now); + BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1")), (ttd - now)); BOOST_REQUIRE_EQUAL(retrieved.size(), 1U); BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr2Content.toString()); @@ -307,7 +307,7 @@ BOOST_AUTO_TEST_CASE(test_RecursorCacheSimple) BOOST_CHECK_EQUAL(MRC.size(), 3U); // we should get the most specific entry for 192.168.0.1, so the second one - BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.168.0.1"), nullptr), (ttd - now)); + BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.168.0.1")), (ttd - now)); BOOST_REQUIRE_EQUAL(retrieved.size(), 1U); BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr4Content.toString()); @@ -323,11 +323,11 @@ BOOST_AUTO_TEST_CASE(test_RecursorCacheSimple) BOOST_CHECK_EQUAL(MRC.size(), 1U); // we should not get it when we need authoritative data - BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), true, &retrieved, ComboAddress("192.168.0.1"), nullptr), -1); + BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), true, &retrieved, ComboAddress("192.168.0.1")), -1); BOOST_REQUIRE_EQUAL(retrieved.size(), 0U); // but we should when we are OK with non-auth - BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.168.0.1"), nullptr), (ttd - now)); + BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.168.0.1")), (ttd - now)); BOOST_REQUIRE_EQUAL(retrieved.size(), 1U); BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr2Content.toString()); } @@ -373,7 +373,7 @@ BOOST_AUTO_TEST_CASE(test_RecursorCacheGhost) /* the TTL should not have been raisd */ std::vector retrieved; - BOOST_CHECK_EQUAL(MRC.get(now, ghost, QType(QType::NS), false, &retrieved, ComboAddress("192.0.2.2"), nullptr), (ttd - now)); + BOOST_CHECK_EQUAL(MRC.get(now, ghost, QType(QType::NS), false, &retrieved, ComboAddress("192.0.2.2")), (ttd - now)); BOOST_REQUIRE_EQUAL(retrieved.size(), 1U); BOOST_CHECK_EQUAL(retrieved.at(0).d_ttl, static_cast(ttd)); } @@ -430,11 +430,11 @@ BOOST_AUTO_TEST_CASE(test_RecursorCache_ExpungingExpiredEntries) /* the remaining entry should be power2, but to get it we need to go back in the past a bit */ - BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power2, QType(dr2.d_type), false, &retrieved, who, nullptr), 1); + BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power2, QType(dr2.d_type), false, &retrieved, who, boost::none, nullptr), 1); BOOST_REQUIRE_EQUAL(retrieved.size(), 1U); BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr2Content.toString()); /* check that power1 is gone */ - BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power1, QType(dr1.d_type), false, &retrieved, who, nullptr), -1); + BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power1, QType(dr1.d_type), false, &retrieved, who, boost::none, nullptr), -1); /* clear everything up */ MRC.doWipeCache(DNSName("."), true); @@ -451,7 +451,7 @@ BOOST_AUTO_TEST_CASE(test_RecursorCache_ExpungingExpiredEntries) BOOST_CHECK_EQUAL(MRC.size(), 2U); /* trigger a miss (expired) for power2 */ - BOOST_CHECK_EQUAL(MRC.get(now, power2, QType(dr2.d_type), false, &retrieved, who, nullptr), -now); + BOOST_CHECK_EQUAL(MRC.get(now, power2, QType(dr2.d_type), false, &retrieved, who, boost::none, nullptr), -now); /* power2 should have been moved to the front of the expunge queue, and should this time be removed first */ @@ -461,11 +461,11 @@ BOOST_AUTO_TEST_CASE(test_RecursorCache_ExpungingExpiredEntries) /* the remaining entry should be power1, but to get it we need to go back in the past a bit */ - BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power1, QType(dr1.d_type), false, &retrieved, who, nullptr), 1); + BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power1, QType(dr1.d_type), false, &retrieved, who, boost::none, nullptr), 1); BOOST_REQUIRE_EQUAL(retrieved.size(), 1U); BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr1Content.toString()); /* check that power2 is gone */ - BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power2, QType(dr2.d_type), false, &retrieved, who, nullptr), -1); + BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power2, QType(dr2.d_type), false, &retrieved, who, boost::none, nullptr), -1); } BOOST_AUTO_TEST_CASE(test_RecursorCache_ExpungingValidEntries) @@ -519,11 +519,11 @@ BOOST_AUTO_TEST_CASE(test_RecursorCache_ExpungingValidEntries) BOOST_CHECK_EQUAL(MRC.size(), 1U); /* the remaining entry should be power2 */ - BOOST_CHECK_EQUAL(MRC.get(now, power2, QType(dr2.d_type), false, &retrieved, who, nullptr), ttd - now); + BOOST_CHECK_EQUAL(MRC.get(now, power2, QType(dr2.d_type), false, &retrieved, who, boost::none, nullptr), ttd - now); BOOST_REQUIRE_EQUAL(retrieved.size(), 1U); BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr2Content.toString()); /* check that power1 is gone */ - BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, nullptr), -1); + BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, boost::none, nullptr), -1); /* clear everything up */ MRC.doWipeCache(DNSName("."), true); @@ -553,11 +553,11 @@ BOOST_AUTO_TEST_CASE(test_RecursorCache_ExpungingValidEntries) BOOST_CHECK_EQUAL(MRC.size(), 1U); /* the remaining entry should be power1 */ - BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, nullptr), ttd - now); + BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, boost::none, nullptr), ttd - now); BOOST_REQUIRE_EQUAL(retrieved.size(), 1U); 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); + BOOST_CHECK_EQUAL(MRC.get(now, power2, QType(dr2.d_type), false, &retrieved, who, boost::none, nullptr), -1); /* clear everything up */ MRC.doWipeCache(DNSName("."), true); @@ -574,7 +574,7 @@ BOOST_AUTO_TEST_CASE(test_RecursorCache_ExpungingValidEntries) BOOST_CHECK_EQUAL(MRC.size(), 2U); /* get a hit for power1 */ - BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, nullptr), ttd - now); + BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, boost::none, nullptr), ttd - now); BOOST_REQUIRE_EQUAL(retrieved.size(), 1U); BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr1Content.toString()); @@ -585,11 +585,11 @@ BOOST_AUTO_TEST_CASE(test_RecursorCache_ExpungingValidEntries) BOOST_CHECK_EQUAL(MRC.size(), 1U); /* the remaining entry should be power1 */ - BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, nullptr), ttd - now); + BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, boost::none, nullptr), ttd - now); BOOST_REQUIRE_EQUAL(retrieved.size(), 1U); 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); + BOOST_CHECK_EQUAL(MRC.get(now, power2, QType(dr2.d_type), false, &retrieved, who, boost::none, nullptr), -1); MRC.doPrune(0); BOOST_CHECK_EQUAL(MRC.size(), 0U); @@ -892,4 +892,174 @@ BOOST_AUTO_TEST_CASE(test_RecursorCache_Wipe) BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 0U); } +BOOST_AUTO_TEST_CASE(test_RecursorCacheTagged) +{ + MemRecursorCache MRC; + + std::vector records; + std::vector> authRecords; + std::vector> signatures; + time_t now = time(nullptr); + + BOOST_CHECK_EQUAL(MRC.size(), 0U); + // An entry withoug edns subnet gets stored without tag as well + MRC.replace(now, DNSName("hello"), QType(QType::A), records, signatures, authRecords, true, boost::none, boost::none); + MRC.replace(now, DNSName("hello"), QType(QType::A), records, signatures, authRecords, true, boost::none, string("mytag")); + BOOST_CHECK_EQUAL(MRC.size(), 1U); + BOOST_CHECK_EQUAL(MRC.doWipeCache(DNSName("hello"), false, QType::A), 1U); + BOOST_CHECK_EQUAL(MRC.size(), 0U); + BOOST_CHECK_EQUAL(MRC.bytes(), 0U); + + uint64_t counter = 0; + try { + for (counter = 0; counter < 100; ++counter) { + DNSName a = DNSName("hello ") + DNSName(std::to_string(counter)); + BOOST_CHECK_EQUAL(DNSName(a.toString()), a); + + MRC.replace(now, a, QType(QType::A), records, signatures, authRecords, true, boost::none, string("mytagA")); + if (!MRC.doWipeCache(a, false)) + BOOST_FAIL("Could not remove entry we just added to the cache!"); + MRC.replace(now, a, QType(QType::A), records, signatures, authRecords, true, boost::none, string("mytagB")); + } + + BOOST_CHECK_EQUAL(MRC.size(), counter); + + uint64_t delcounter = 0; + for (delcounter = 0; delcounter < counter / 100; ++delcounter) { + DNSName a = DNSName("hello ") + DNSName(std::to_string(delcounter)); + BOOST_CHECK_EQUAL(MRC.doWipeCache(a, false, QType::A), 1U); + } + + BOOST_CHECK_EQUAL(MRC.size(), counter - delcounter); + + std::vector retrieved; + ComboAddress who("192.0.2.1"); + int64_t matches = 0; + int64_t expected = counter - delcounter; + int64_t delstart = delcounter; + + for (; delcounter < counter; ++delcounter) { + if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(delcounter)), QType(QType::A), false, &retrieved, who, boost::none)) { + matches++; + } + } + BOOST_CHECK_EQUAL(matches, expected); + BOOST_CHECK_EQUAL(retrieved.size(), records.size()); + + matches = 0; + retrieved.clear(); + for (delcounter = delstart; delcounter < counter; ++delcounter) { + if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(delcounter)), QType(QType::A), false, &retrieved, who, string("mytagB"))) { + matches++; + } + } + BOOST_CHECK_EQUAL(matches, expected); + BOOST_CHECK_EQUAL(retrieved.size(), records.size()); + + matches = 0; + for (delcounter = delstart; delcounter < counter; ++delcounter) { + if (MRC.get(now, DNSName("hello ") + DNSName(std::to_string(delcounter)), QType(QType::A), false, &retrieved, who, string("mytagX"))) { + matches++; + } + } + BOOST_CHECK_EQUAL(matches, expected); + BOOST_CHECK_EQUAL(retrieved.size(), records.size()); + + MRC.doWipeCache(DNSName("."), true); + BOOST_CHECK_EQUAL(MRC.size(), 0U); + + time_t ttd = now + 30; + DNSName power("powerdns.com."); + DNSRecord dr1; + ComboAddress dr1Content("192.0.2.41"); + 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; + std::vector rset1; + rset1.push_back(dr1); + + DNSRecord dr2; + ComboAddress dr2Content("192.0.2.42"); + 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(ttd); + dr2.d_place = DNSResourceRecord::ANSWER; + std::vector rset2; + rset2.push_back(dr2); + + DNSRecord dr3; + ComboAddress dr3Content("192.0.2.43"); + dr3.d_name = power; + dr3.d_type = QType::A; + dr3.d_class = QClass::IN; + dr3.d_content = std::make_shared(dr3Content); + dr3.d_ttl = static_cast(ttd); + dr3.d_place = DNSResourceRecord::ANSWER; + std::vector rset3; + rset3.push_back(dr3); + + // insert a tagged entry + records.push_back(dr1); + MRC.replace(now, power, QType(QType::A), rset1, signatures, authRecords, true, boost::optional("192.0.2.0/24"), string("mytag")); + BOOST_CHECK_EQUAL(MRC.size(), 1U); + + // tagged specific should be returned for a matching tag + BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), string("mytag")), (ttd - now)); + BOOST_REQUIRE_EQUAL(retrieved.size(), 1U); + BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr1Content.toString()); + + // tag specific should not be returned for a different tag + BOOST_CHECK_LT(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), string("othertag")), 0); + BOOST_CHECK_EQUAL(retrieved.size(), 0U); + + // insert a new entry without tag + MRC.replace(now, power, QType(QType::A), rset2, signatures, authRecords, true, boost::optional("192.0.3.0/24"), boost::none); + BOOST_CHECK_EQUAL(MRC.size(), 2U); + + // tagged specific should be returned for a matching tag + BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), string("mytag")), (ttd - now)); + BOOST_REQUIRE_EQUAL(retrieved.size(), 1U); + BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr1Content.toString()); + + // if no tag given nothing should be retrieved if address doesn't match + BOOST_CHECK_LT(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1"), boost::none), 0); + BOOST_REQUIRE_EQUAL(retrieved.size(), 0U); + + // if no tag given and no-non-tagged entries are available nothing should be retrieved even if address matches + BOOST_CHECK_LT(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), boost::none), 0); + BOOST_REQUIRE_EQUAL(retrieved.size(), 0U); + + // Insert untagged entry with no netmask + MRC.replace(now, power, QType(QType::A), rset3, signatures, authRecords, true, boost::none, boost::none); + BOOST_CHECK_EQUAL(MRC.size(), 3U); + + // Retrieval with no address and no tag should get that one + BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress(), boost::none), (ttd - now)); + BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr3Content.toString()); + + // If no tag given match non-tagged entry + BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), boost::none), (ttd - now)); + BOOST_REQUIRE_EQUAL(retrieved.size(), 1U); + BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr3Content.toString()); + + // tagged specific should still be returned for a matching tag, address is not used + BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), string("mytag")), (ttd - now)); + BOOST_REQUIRE_EQUAL(retrieved.size(), 1U); + BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr1Content.toString()); + + // remove everything + MRC.doWipeCache(DNSName("."), true); + BOOST_CHECK_EQUAL(MRC.size(), 0U); + } + catch (const PDNSException& e) { + cerr << "Had error: " << e.reason << endl; + throw; + } +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/pdns/recursordist/test-syncres_cc3.cc b/pdns/recursordist/test-syncres_cc3.cc index d0847bb4a3..2fc7e325bf 100644 --- a/pdns/recursordist/test-syncres_cc3.cc +++ b/pdns/recursordist/test-syncres_cc3.cc @@ -317,7 +317,7 @@ BOOST_AUTO_TEST_CASE(test_answer_no_aa) const ComboAddress who; vector cached; vector> signatures; - BOOST_REQUIRE_EQUAL(s_RC->get(now, target, QType(QType::A), false, &cached, who, &signatures), -1); + BOOST_REQUIRE_EQUAL(s_RC->get(now, target, QType(QType::A), false, &cached, who, boost::none, &signatures), -1); } BOOST_AUTO_TEST_CASE(test_special_types) diff --git a/pdns/recursordist/test-syncres_cc8.cc b/pdns/recursordist/test-syncres_cc8.cc index 9fb52c8899..056fb1ef88 100644 --- a/pdns/recursordist/test-syncres_cc8.cc +++ b/pdns/recursordist/test-syncres_cc8.cc @@ -817,7 +817,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_rrsig_cache_validity) const ComboAddress who; vector cached; vector> signatures; - BOOST_REQUIRE_EQUAL(s_RC->get(tnow, target, QType(QType::A), true, &cached, who, &signatures), 1); + BOOST_REQUIRE_EQUAL(s_RC->get(tnow, target, QType(QType::A), true, &cached, who, boost::none, &signatures), 1); BOOST_REQUIRE_EQUAL(cached.size(), 1U); BOOST_REQUIRE_EQUAL(signatures.size(), 1U); BOOST_CHECK_EQUAL((cached[0].d_ttl - tnow), 1); diff --git a/pdns/recursordist/test-syncres_cc9.cc b/pdns/recursordist/test-syncres_cc9.cc index 8364a872c0..59c1ad5ceb 100644 --- a/pdns/recursordist/test-syncres_cc9.cc +++ b/pdns/recursordist/test-syncres_cc9.cc @@ -937,7 +937,7 @@ BOOST_AUTO_TEST_CASE(test_cname_plus_authority_ns_ttl) vector cached; bool wasAuth = false; - auto ttl = s_RC->get(now, DNSName("powerdns.com."), QType(QType::NS), false, &cached, who, nullptr, nullptr, nullptr, nullptr, &wasAuth); + auto ttl = s_RC->get(now, DNSName("powerdns.com."), QType(QType::NS), false, &cached, who, boost::none, nullptr, nullptr, nullptr, nullptr, &wasAuth); BOOST_REQUIRE_GE(ttl, 1); BOOST_REQUIRE_LE(ttl, 42); BOOST_CHECK_EQUAL(cached.size(), 1U); @@ -946,7 +946,7 @@ BOOST_AUTO_TEST_CASE(test_cname_plus_authority_ns_ttl) cached.clear(); /* Also check that the the part in additional is still not auth */ - BOOST_REQUIRE_GE(s_RC->get(now, DNSName("a.gtld-servers.net."), QType(QType::A), false, &cached, who, nullptr, nullptr, nullptr, nullptr, &wasAuth), -1); + BOOST_REQUIRE_GE(s_RC->get(now, DNSName("a.gtld-servers.net."), QType(QType::A), false, &cached, who, boost::none, nullptr, nullptr, nullptr, nullptr, &wasAuth), -1); BOOST_CHECK_EQUAL(cached.size(), 1U); BOOST_CHECK_EQUAL(wasAuth, false); } diff --git a/pdns/reczones.cc b/pdns/reczones.cc index c7f48b206a..64621ba7c8 100644 --- a/pdns/reczones.cc +++ b/pdns/reczones.cc @@ -68,13 +68,13 @@ void primeHints(void) arr.d_content=std::make_shared(ComboAddress(rootIps4[c-'a'])); vector aset; aset.push_back(arr); - s_RC->replace(time(0), DNSName(templ), QType(QType::A), aset, vector>(), vector>(), true, boost::none, validationState); // auth, nuke it all + s_RC->replace(time(0), DNSName(templ), QType(QType::A), aset, vector>(), vector>(), true, boost::none, boost::none, validationState); // auth, nuke it all if (rootIps6[c-'a'] != NULL) { aaaarr.d_content=std::make_shared(ComboAddress(rootIps6[c-'a'])); vector aaaaset; aaaaset.push_back(aaaarr); - s_RC->replace(time(0), DNSName(templ), QType(QType::AAAA), aaaaset, vector>(), vector>(), true, boost::none, validationState); + s_RC->replace(time(0), DNSName(templ), QType(QType::AAAA), aaaaset, vector>(), vector>(), true, boost::none, boost::none, validationState); } nsset.push_back(nsrr); @@ -90,11 +90,11 @@ void primeHints(void) if(rr.qtype.getCode()==QType::A) { vector aset; aset.push_back(DNSRecord(rr)); - s_RC->replace(time(0), rr.qname, QType(QType::A), aset, vector>(), vector>(), true, boost::none, validationState); // auth, etc see above + s_RC->replace(time(0), rr.qname, QType(QType::A), aset, vector>(), vector>(), true, boost::none, boost::none, validationState); // auth, etc see above } else if(rr.qtype.getCode()==QType::AAAA) { vector aaaaset; aaaaset.push_back(DNSRecord(rr)); - s_RC->replace(time(0), rr.qname, QType(QType::AAAA), aaaaset, vector>(), vector>(), true, boost::none, validationState); + s_RC->replace(time(0), rr.qname, QType(QType::AAAA), aaaaset, vector>(), vector>(), true, boost::none, boost::none, validationState); } else if(rr.qtype.getCode()==QType::NS) { rr.content=toLower(rr.content); nsset.push_back(DNSRecord(rr)); @@ -103,7 +103,7 @@ void primeHints(void) } } s_RC->doWipeCache(g_rootdnsname, false, QType::NS); - s_RC->replace(time(0), g_rootdnsname, QType(QType::NS), nsset, vector>(), vector>(), false, boost::none, validationState); // and stuff in the cache + s_RC->replace(time(0), g_rootdnsname, QType(QType::NS), nsset, vector>(), vector>(), false, boost::none, boost::none, validationState); // and stuff in the cache } diff --git a/pdns/syncres.cc b/pdns/syncres.cc index 125f337f1b..0850b4914b 100644 --- a/pdns/syncres.cc +++ b/pdns/syncres.cc @@ -957,7 +957,7 @@ vector SyncRes::getAddrs(const DNSName &qname, unsigned int depth, // We have some IPv4 records, don't bother with going out to get IPv6, but do consult the cache // Once IPv6 adoption matters, this needs to be revisited res_t cset; - if (s_RC->get(d_now.tv_sec, qname, QType(QType::AAAA), false, &cset, d_cacheRemote) > 0) { + if (s_RC->get(d_now.tv_sec, qname, QType(QType::AAAA), false, &cset, d_cacheRemote, d_routingTag) > 0) { for (const auto &i : cset) { if (i.d_ttl > (unsigned int)d_now.tv_sec ) { if (auto rec = getRR(i)) { @@ -1033,7 +1033,7 @@ void SyncRes::getBestNSFromCache(const DNSName &qname, const QType& qtype, vecto vector ns; *flawedNSSet = false; - if(s_RC->get(d_now.tv_sec, subdomain, QType(QType::NS), false, &ns, d_cacheRemote) > 0) { + if(s_RC->get(d_now.tv_sec, subdomain, QType(QType::NS), false, &ns, d_cacheRemote, d_routingTag) > 0) { bestns.reserve(ns.size()); for(auto k=ns.cbegin();k!=ns.cend(); ++k) { @@ -1043,7 +1043,7 @@ void SyncRes::getBestNSFromCache(const DNSName &qname, const QType& qtype, vecto const DNSRecord& dr=*k; auto nrr = getRR(dr); if(nrr && (!nrr->getNS().isPartOf(subdomain) || s_RC->get(d_now.tv_sec, nrr->getNS(), s_doIPv6 ? QType(QType::ADDR) : QType(QType::A), - false, doLog() ? &aset : 0, d_cacheRemote) > 5)) { + false, doLog() ? &aset : 0, d_cacheRemote, d_routingTag) > 5)) { bestns.push_back(dr); LOG(prefix< '"<getNS()<<"'"<getNS().isPartOf(subdomain)); @@ -1158,10 +1158,10 @@ DNSName SyncRes::getBestNSNamesFromCache(const DNSName &qname, const QType& qtyp void SyncRes::updateValidationStatusInCache(const DNSName &qname, const QType& qt, bool aa, vState newState) const { if (newState == Bogus) { - s_RC->updateValidationStatus(d_now.tv_sec, qname, qt, d_cacheRemote, aa, newState, s_maxbogusttl + d_now.tv_sec); + s_RC->updateValidationStatus(d_now.tv_sec, qname, qt, d_cacheRemote, d_routingTag, aa, newState, s_maxbogusttl + d_now.tv_sec); } else { - s_RC->updateValidationStatus(d_now.tv_sec, qname, qt, d_cacheRemote, aa, newState, boost::none); + s_RC->updateValidationStatus(d_now.tv_sec, qname, qt, d_cacheRemote, d_routingTag, aa, newState, boost::none); } } @@ -1189,7 +1189,7 @@ bool SyncRes::doCNAMECacheCheck(const DNSName &qname, const QType &qtype, vector LOG(prefix<get(d_now.tv_sec, qname, QType(QType::CNAME), !wasForwardRecurse && d_requireAuthData, &cset, d_cacheRemote, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &state, &wasAuth) > 0) { + if (s_RC->get(d_now.tv_sec, qname, QType(QType::CNAME), !wasForwardRecurse && d_requireAuthData, &cset, d_cacheRemote, d_routingTag, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &state, &wasAuth) > 0) { foundName = qname; foundQT = QType(QType::CNAME); } @@ -1206,7 +1206,7 @@ bool SyncRes::doCNAMECacheCheck(const DNSName &qname, const QType &qtype, vector if (dnameName == qname && qtype != QType::DNAME) { // The client does not want a DNAME, but we've reached the QNAME already. So there is no match break; } - if (s_RC->get(d_now.tv_sec, dnameName, QType(QType::DNAME), !wasForwardRecurse && d_requireAuthData, &cset, d_cacheRemote, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &state, &wasAuth) > 0) { + if (s_RC->get(d_now.tv_sec, dnameName, QType(QType::DNAME), !wasForwardRecurse && d_requireAuthData, &cset, d_cacheRemote, d_routingTag, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &state, &wasAuth) > 0) { foundName = dnameName; foundQT = QType(QType::DNAME); break; @@ -1542,7 +1542,7 @@ bool SyncRes::doCacheCheck(const DNSName &qname, const DNSName& authname, bool w uint32_t ttl=0; uint32_t capTTL = std::numeric_limits::max(); bool wasCachedAuth; - if(s_RC->get(d_now.tv_sec, sqname, sqt, !wasForwardRecurse && d_requireAuthData, &cset, d_cacheRemote, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &cachedState, &wasCachedAuth) > 0) { + if(s_RC->get(d_now.tv_sec, sqname, sqt, !wasForwardRecurse && d_requireAuthData, &cset, d_cacheRemote, d_routingTag, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &cachedState, &wasCachedAuth) > 0) { LOG(prefix<replace(d_now.tv_sec, i->first.name, QType(i->first.type), i->second.records, i->second.signatures, authorityRecs, i->first.type == QType::DS ? true : isAA, i->first.place == DNSResourceRecord::ANSWER ? ednsmask : boost::none, recordState); + s_RC->replace(d_now.tv_sec, i->first.name, QType(i->first.type), i->second.records, i->second.signatures, authorityRecs, i->first.type == QType::DS ? true : isAA, i->first.place == DNSResourceRecord::ANSWER ? ednsmask : boost::none, d_routingTag, recordState); } } - if(i->first.place == DNSResourceRecord::ANSWER && ednsmask) + if(i->first.place == DNSResourceRecord::ANSWER && (ednsmask || d_routingTag)) d_wasVariable=true; } diff --git a/pdns/syncres.hh b/pdns/syncres.hh index 87cc541c23..9bbadcdb42 100644 --- a/pdns/syncres.hh +++ b/pdns/syncres.hh @@ -782,6 +782,8 @@ public: std::unordered_map d_discardedPolicies; DNSFilterEngine::Policy d_appliedPolicy; std::unordered_set d_policyTags; + boost::optional d_routingTag; + unsigned int d_authzonequeries; unsigned int d_outqueries; unsigned int d_tcpoutqueries; -- 2.47.2