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);
std::string requestorId;
std::string deviceId;
std::string deviceName;
+ std::string routingTag;
vState validationState{Indeterminate};
bool& variable;
bool& wantsRPZ;
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();
}
return ret;
}
-int32_t MemRecursorCache::handleHit(MapCombo& map, MemRecursorCache::OrderedTagIterator_t& entry, const DNSName& qname, const ComboAddress& who, vector<DNSRecord>* res, vector<std::shared_ptr<RRSIGRecordContent>>* signatures, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs, bool* variable, vState* state, bool* wasAuth)
+int32_t MemRecursorCache::handleHit(MapCombo& map, MemRecursorCache::OrderedTagIterator_t& entry, const DNSName& qname, vector<DNSRecord>* res, vector<std::shared_ptr<RRSIGRecordContent>>* signatures, std::vector<std::shared_ptr<DNSRecord>>* 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;
}
/* 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 */
}
/* 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) {
return map.d_map.end();
}
-std::pair<MemRecursorCache::NameOnlyHashedTagIterator_t, MemRecursorCache::NameOnlyHashedTagIterator_t> 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<NameOnlyHashedTag>();
- map.d_cachecache = idx.equal_range(qname);
+ map.d_cachedrtag = rtag;
+ const auto& idx = map.d_map.get<NameAndRTagOnlyHashedTag>();
+ map.d_cachecache = idx.equal_range(tie(qname, rtag));
map.d_cachecachevalid = true;
}
return map.d_cachecache;
}
+#include <boost/optional/optional_io.hpp>
+
+
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<DNSRecord>* res, const ComboAddress& who, vector<std::shared_ptr<RRSIGRecordContent>>* signatures, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs, bool* variable, vState* state, bool* wasAuth)
+int32_t MemRecursorCache::get(time_t now, const DNSName &qname, const QType& qt, bool requireAuth, vector<DNSRecord>* res, const ComboAddress& who, const OptTag& routingTag, vector<std::shared_ptr<RRSIGRecordContent>>* signatures, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs, bool* variable, vState* state, bool* wasAuth)
{
time_t ttd=0;
// cerr<<"looking up "<< qname<<"|"+qt.getName()<<"\n";
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 {
else {
auto entry = getEntryUsingECSIndex(map, now, qname, qtype, requireAuth, who);
if (entry != map.d_map.end()) {
- return static_cast<int32_t>(handleHit(map, entry, qname, who, res, signatures, authorityRecs, variable, state, wasAuth) - now);
+ return static_cast<int32_t>(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<OrderedTag>(i);
- if (i->d_ttd <= now) {
- moveCacheItemToFront<SequencedTag>(map.d_map, firstIndexIterator);
- continue;
+ auto firstIndexIterator = map.d_map.project<OrderedTag>(i);
+ if (i->d_ttd <= now) {
+ moveCacheItemToFront<SequencedTag>(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 : "<<ttd - now<<", "<< (res ? res->size() : 0) <<"\n";
+ return static_cast<int32_t>(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<OrderedTag>(i);
+ if (i->d_ttd <= now) {
+ moveCacheItemToFront<SequencedTag>(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<int32_t>(ttd-now);
}
+ // Try again without tag
+ entries = getEntries(map, qname, qt, boost::none);
- // cerr<<"time left : "<<ttd - now<<", "<< (res ? res->size() : 0) <<"\n";
- return static_cast<int32_t>(ttd-now);
+ if (entries.first != entries.second) {
+ for (auto i=entries.first; i != entries.second; ++i) {
+
+ auto firstIndexIterator = map.d_map.project<OrderedTag>(i);
+ if (i->d_ttd <= now) {
+ moveCacheItemToFront<SequencedTag>(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<int32_t>(ttd-now);
+ }
}
return -1;
}
-void MemRecursorCache::replace(time_t now, const DNSName &qname, const QType& qt, const vector<DNSRecord>& content, const vector<shared_ptr<RRSIGRecordContent>>& signatures, const std::vector<std::shared_ptr<DNSRecord>>& authorityRecs, bool auth, boost::optional<Netmask> ednsmask, vState state)
+void MemRecursorCache::replace(time_t now, const DNSName &qname, const QType& qt, const vector<DNSRecord>& content, const vector<shared_ptr<RRSIGRecordContent>>& signatures, const std::vector<std::shared_ptr<DNSRecord>>& authorityRecs, bool auth, boost::optional<Netmask> ednsmask, const OptTag& routingTag, vState state)
{
auto& map = getMap(qname);
const lock l(map);
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()) {
*/
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()) {
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 {
/* 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<time_t>(i.d_ttl)); // XXX this does weird things if TTLs differ in the set
- // cerr<<"To store: "<<i.d_content->getZoneRepresentation()<<" with ttl/ttd "<<i.d_ttl<<", capped at: "<<maxTTD<<endl;
+ //cerr<<"To store: "<<i.d_content->getZoneRepresentation()<<" with ttl/ttd "<<i.d_ttl<<", capped at: "<<maxTTD<<endl;
ce.d_records.push_back(i.d_content);
}
auto& map = getMap(name);
const lock l(map);
map.d_cachecachevalid = false;
- auto& idx = map.d_map.get<NameOnlyHashedTag>();
- size_t n = idx.erase(name);
- count += n;
- map.d_entriesCount -= n;
+ auto& idx = map.d_map.get<OrderedTag>();
+ 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<OrderedTag>();
auto ecsIndexRange = ecsIdx.equal_range(name);
return false;
}
-bool MemRecursorCache::updateValidationStatus(time_t now, const DNSName &qname, const QType& qt, const ComboAddress& who, bool requireAuth, vState newState, boost::optional<time_t> 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<time_t> 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;
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<OrderedTag>(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) {
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<int64_t>(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<int64_t>(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());
pruneMutexCollectionsVector<SequencedTag>(*this, d_maps, keep, cacheSize);
}
+namespace boost {
+ size_t hash_value(const MemRecursorCache::OptTag& o)
+ {
+ return o ? hash_value(o.get()) : 0xcafebaaf;
+ }
+}
pair<uint64_t,uint64_t> stats();
size_t ecsIndexSize();
- int32_t get(time_t, const DNSName &qname, const QType& qt, bool requireAuth, vector<DNSRecord>* res, const ComboAddress& who, vector<std::shared_ptr<RRSIGRecordContent>>* signatures=nullptr, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs=nullptr, bool* variable=nullptr, vState* state=nullptr, bool* wasAuth=nullptr);
+ typedef boost::optional<std::string> OptTag;
- void replace(time_t, const DNSName &qname, const QType& qt, const vector<DNSRecord>& content, const vector<shared_ptr<RRSIGRecordContent>>& signatures, const std::vector<std::shared_ptr<DNSRecord>>& authorityRecs, bool auth, boost::optional<Netmask> ednsmask=boost::none, vState state=Indeterminate);
+ int32_t get(time_t, const DNSName &qname, const QType& qt, bool requireAuth, vector<DNSRecord>* res, const ComboAddress& who, const OptTag& routingTag = boost::none, vector<std::shared_ptr<RRSIGRecordContent>>* signatures=nullptr, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs=nullptr, bool* variable=nullptr, vState* state=nullptr, bool* wasAuth=nullptr);
+
+ void replace(time_t, const DNSName &qname, const QType& qt, const vector<DNSRecord>& content, const vector<shared_ptr<RRSIGRecordContent>>& signatures, const std::vector<std::shared_ptr<DNSRecord>>& authorityRecs, bool auth, boost::optional<Netmask> 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<time_t> capTTD);
+ bool updateValidationStatus(time_t now, const DNSName &qname, const QType& qt, const ComboAddress& who, const OptTag& routingTag, bool requireAuth, vState newState, boost::optional<time_t> capTTD);
std::atomic<uint64_t> cacheHits{0}, cacheMisses{0};
struct CacheEntry
{
- CacheEntry(const boost::tuple<DNSName, uint16_t, Netmask>& 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<DNSName, uint16_t, OptTag, Netmask>& 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)
{
}
std::vector<std::shared_ptr<DNSRecord>> d_authorityRecs;
DNSName d_qname;
Netmask d_netmask;
+ OptTag d_rtag;
mutable vState d_state;
mutable time_t d_ttd;
uint16_t d_qtype;
struct HashedTag {};
struct SequencedTag {};
- struct NameOnlyHashedTag {};
+ struct NameAndRTagOnlyHashedTag {};
struct OrderedTag {};
typedef multi_index_container<
CacheEntry,
member<CacheEntry,DNSName,&CacheEntry::d_qname>,
member<CacheEntry,uint16_t,&CacheEntry::d_qtype>,
+ member<CacheEntry,OptTag,&CacheEntry::d_rtag>,
member<CacheEntry,Netmask,&CacheEntry::d_netmask>
>,
- composite_key_compare<CanonDNSNameCompare, std::less<uint16_t>, std::less<Netmask> >
+ composite_key_compare<CanonDNSNameCompare, std::less<uint16_t>, std::less<OptTag>, std::less<Netmask> >
>,
sequenced<tag<SequencedTag> >,
- hashed_non_unique<tag<NameOnlyHashedTag>,
- member<CacheEntry,DNSName,&CacheEntry::d_qname>
+ hashed_non_unique<tag<NameAndRTagOnlyHashedTag>,
+ composite_key<
+ CacheEntry,
+ member<CacheEntry,DNSName,&CacheEntry::d_qname>,
+ member<CacheEntry,OptTag,&CacheEntry::d_rtag>
+ >
>
>
> cache_t;
typedef MemRecursorCache::cache_t::index<MemRecursorCache::OrderedTag>::type::iterator OrderedTagIterator_t;
- typedef MemRecursorCache::cache_t::index<MemRecursorCache::NameOnlyHashedTag>::type::iterator NameOnlyHashedTagIterator_t;
+ typedef MemRecursorCache::cache_t::index<MemRecursorCache::NameAndRTagOnlyHashedTag>::type::iterator NameAndRTagOnlyHashedTagIterator_t;
typedef multi_index_container<
ECSIndexEntry,
>
> ecsIndex_t;
+ typedef std::pair<NameAndRTagOnlyHashedTagIterator_t, NameAndRTagOnlyHashedTagIterator_t> Entries;
+
struct MapCombo
{
MapCombo() {}
cache_t d_map;
ecsIndex_t d_ecsIndex;
DNSName d_cachedqname;
- std::pair<MemRecursorCache::NameOnlyHashedTagIterator_t, MemRecursorCache::NameOnlyHashedTagIterator_t> d_cachecache;
+ OptTag d_cachedrtag;
+ Entries d_cachecache;
std::mutex mutex;
bool d_cachecachevalid{false};
std::atomic<uint64_t> d_entriesCount{0};
}
bool entryMatches(OrderedTagIterator_t& entry, uint16_t qt, bool requireAuth, const ComboAddress& who);
- std::pair<NameOnlyHashedTagIterator_t, NameOnlyHashedTagIterator_t> 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<DNSRecord>* res, vector<std::shared_ptr<RRSIGRecordContent>>* signatures, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs, bool* variable, vState* state, bool* wasAuth);
+ int32_t handleHit(MapCombo& map, OrderedTagIterator_t& entry, const DNSName& qname, vector<DNSRecord>* res, vector<std::shared_ptr<RRSIGRecordContent>>* signatures, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs, bool* variable, vState* state, bool* wasAuth);
public:
struct lock {
}
}
};
+
+namespace boost {
+ size_t hash_value(const MemRecursorCache::OptTag& rtag);
+}
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<AAAARecordContent>(retrieved.at(0))->getCA().toString(), dr0Content.toString());
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<AAAARecordContent>(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
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<ARecordContent>(retrieved.at(0))->getCA().toString(), dr2Content.toString());
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<ARecordContent>(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<AAAARecordContent>(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);
}
// 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;
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
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<int32_t>(newTTL));
+ BOOST_CHECK_EQUAL(MRC.get(now, power, QType(QType::TXT), false, &retrieved, ComboAddress("127.0.0.1")), static_cast<int32_t>(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
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;
// 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<ARecordContent>(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<ARecordContent>(retrieved.at(0))->getCA().toString(), dr3Content.toString());
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<ARecordContent>(retrieved.at(0))->getCA().toString(), dr2Content.toString());
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<ARecordContent>(retrieved.at(0))->getCA().toString(), dr4Content.toString());
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<ARecordContent>(retrieved.at(0))->getCA().toString(), dr2Content.toString());
}
/* the TTL should not have been raisd */
std::vector<DNSRecord> 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<uint32_t>(ttd));
}
/* 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<AAAARecordContent>(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);
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 */
/* 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<AAAARecordContent>(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)
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<AAAARecordContent>(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);
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<AAAARecordContent>(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);
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<AAAARecordContent>(retrieved.at(0))->getCA().toString(), dr1Content.toString());
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<AAAARecordContent>(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);
BOOST_CHECK_EQUAL(MRC.ecsIndexSize(), 0U);
}
+BOOST_AUTO_TEST_CASE(test_RecursorCacheTagged)
+{
+ MemRecursorCache MRC;
+
+ std::vector<DNSRecord> records;
+ std::vector<std::shared_ptr<DNSRecord>> authRecords;
+ std::vector<std::shared_ptr<RRSIGRecordContent>> 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<DNSRecord> 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<ARecordContent>(dr1Content);
+ dr1.d_ttl = static_cast<uint32_t>(ttd);
+ dr1.d_place = DNSResourceRecord::ANSWER;
+ std::vector<DNSRecord> 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<ARecordContent>(dr2Content);
+ dr2.d_ttl = static_cast<uint32_t>(ttd);
+ dr2.d_place = DNSResourceRecord::ANSWER;
+ std::vector<DNSRecord> 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<ARecordContent>(dr3Content);
+ dr3.d_ttl = static_cast<uint32_t>(ttd);
+ dr3.d_place = DNSResourceRecord::ANSWER;
+ std::vector<DNSRecord> 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<Netmask>("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<ARecordContent>(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<Netmask>("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<ARecordContent>(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<ARecordContent>(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<ARecordContent>(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<ARecordContent>(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()
const ComboAddress who;
vector<DNSRecord> cached;
vector<std::shared_ptr<RRSIGRecordContent>> 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)
const ComboAddress who;
vector<DNSRecord> cached;
vector<std::shared_ptr<RRSIGRecordContent>> 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);
vector<DNSRecord> 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);
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);
}
arr.d_content=std::make_shared<ARecordContent>(ComboAddress(rootIps4[c-'a']));
vector<DNSRecord> aset;
aset.push_back(arr);
- s_RC->replace(time(0), DNSName(templ), QType(QType::A), aset, vector<std::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), true, boost::none, validationState); // auth, nuke it all
+ s_RC->replace(time(0), DNSName(templ), QType(QType::A), aset, vector<std::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), true, boost::none, boost::none, validationState); // auth, nuke it all
if (rootIps6[c-'a'] != NULL) {
aaaarr.d_content=std::make_shared<AAAARecordContent>(ComboAddress(rootIps6[c-'a']));
vector<DNSRecord> aaaaset;
aaaaset.push_back(aaaarr);
- s_RC->replace(time(0), DNSName(templ), QType(QType::AAAA), aaaaset, vector<std::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), true, boost::none, validationState);
+ s_RC->replace(time(0), DNSName(templ), QType(QType::AAAA), aaaaset, vector<std::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), true, boost::none, boost::none, validationState);
}
nsset.push_back(nsrr);
if(rr.qtype.getCode()==QType::A) {
vector<DNSRecord> aset;
aset.push_back(DNSRecord(rr));
- s_RC->replace(time(0), rr.qname, QType(QType::A), aset, vector<std::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), true, boost::none, validationState); // auth, etc see above
+ s_RC->replace(time(0), rr.qname, QType(QType::A), aset, vector<std::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), true, boost::none, boost::none, validationState); // auth, etc see above
} else if(rr.qtype.getCode()==QType::AAAA) {
vector<DNSRecord> aaaaset;
aaaaset.push_back(DNSRecord(rr));
- s_RC->replace(time(0), rr.qname, QType(QType::AAAA), aaaaset, vector<std::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), true, boost::none, validationState);
+ s_RC->replace(time(0), rr.qname, QType(QType::AAAA), aaaaset, vector<std::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), true, boost::none, boost::none, validationState);
} else if(rr.qtype.getCode()==QType::NS) {
rr.content=toLower(rr.content);
nsset.push_back(DNSRecord(rr));
}
}
s_RC->doWipeCache(g_rootdnsname, false, QType::NS);
- s_RC->replace(time(0), g_rootdnsname, QType(QType::NS), nsset, vector<std::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), false, boost::none, validationState); // and stuff in the cache
+ s_RC->replace(time(0), g_rootdnsname, QType(QType::NS), nsset, vector<std::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), false, boost::none, boost::none, validationState); // and stuff in the cache
}
// 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<AAAARecordContent>(i)) {
vector<DNSRecord> 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) {
const DNSRecord& dr=*k;
auto nrr = getRR<NSRecordContent>(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<<qname<<": NS (with ip, or non-glue) in cache for '"<<subdomain<<"' -> '"<<nrr->getNS()<<"'"<<endl);
LOG(prefix<<qname<<": within bailiwick: "<< nrr->getNS().isPartOf(subdomain));
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);
}
}
LOG(prefix<<qname<<": Looking for CNAME cache hit of '"<<qname<<"|CNAME"<<"'"<<endl);
/* we don't require auth data for forward-recurse lookups */
- if (s_RC->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);
}
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;
uint32_t ttl=0;
uint32_t capTTL = std::numeric_limits<uint32_t>::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<<sqname<<": Found cache hit for "<<sqt.getName()<<": ");
}
}
if (doCache) {
- 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, 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;
}
std::unordered_map<std::string,bool> d_discardedPolicies;
DNSFilterEngine::Policy d_appliedPolicy;
std::unordered_set<std::string> d_policyTags;
+ boost::optional<string> d_routingTag;
+
unsigned int d_authzonequeries;
unsigned int d_outqueries;
unsigned int d_tcpoutqueries;