]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Introduce routingTag, settable from lua and passed to SyncRes if set.
authorOtto Moerbeek <otto.moerbeek@open-xchange.com>
Wed, 12 Feb 2020 10:55:56 +0000 (11:55 +0100)
committerOtto Moerbeek <otto.moerbeek@open-xchange.com>
Mon, 20 Apr 2020 13:19:18 +0000 (15:19 +0200)
SyncRes passes it on to the record cache methods that do not use
it yet.

12 files changed:
pdns/lua-recursor4.cc
pdns/lua-recursor4.hh
pdns/pdns_recursor.cc
pdns/recursor_cache.cc
pdns/recursor_cache.hh
pdns/recursordist/test-recursorcache_cc.cc
pdns/recursordist/test-syncres_cc3.cc
pdns/recursordist/test-syncres_cc8.cc
pdns/recursordist/test-syncres_cc9.cc
pdns/reczones.cc
pdns/syncres.cc
pdns/syncres.hh

index 60a5815cc70e218ca7486f08294131b12e80a8c0..931b3bb4b7f11ff67fc686a24273320c9c8c2579 100644 (file)
@@ -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);
index 5b3dcb50026c51f1d703aeaf01dc602e1a849fde..163958306985e87a1be044d954a34cc0195442d4 100644 (file)
@@ -81,6 +81,7 @@ public:
     std::string requestorId;
     std::string deviceId;
     std::string deviceName;
+    std::string routingTag;
     vState validationState{Indeterminate};
     bool& variable;
     bool& wantsRPZ;
index 2e830e06a46e42537365f1af91b34adf99349fe2..f0ffdfe9717ec86442ae8028414aef64beeede9c 100644 (file)
@@ -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();
       }
index 7c813317be54d90cbed87cebca498bab4293882a..13ecd979759b9c0a6e4db95e06d2b898b0d05254 100644 (file)
@@ -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<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;
   }
 
@@ -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::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";
@@ -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<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);
@@ -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<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);
   }
 
@@ -375,10 +448,15 @@ size_t MemRecursorCache::doWipeCache(const DNSName& name, bool sub, uint16_t qty
     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);
@@ -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<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;
@@ -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<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) {
@@ -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<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());
@@ -542,3 +626,9 @@ void MemRecursorCache::doPrune(size_t keep)
   pruneMutexCollectionsVector<SequencedTag>(*this, d_maps, keep, cacheSize);
 }
 
+namespace boost {
+  size_t hash_value(const MemRecursorCache::OptTag& o)
+  {
+    return o ? hash_value(o.get()) : 0xcafebaaf;
+  }
+}
index 2b0305d82a6bae366b37e26c94ed080c09643611..5ec2a6c933a03f7e119e117c573a4a3888c23e14 100644 (file)
@@ -55,16 +55,18 @@ public:
   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};
 
@@ -72,8 +74,8 @@ private:
 
   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)
     {
     }
 
@@ -88,6 +90,7 @@ private:
     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;
@@ -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<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,
@@ -189,6 +197,8 @@ private:
     >
   > ecsIndex_t;
 
+  typedef std::pair<NameAndRTagOnlyHashedTagIterator_t, NameAndRTagOnlyHashedTagIterator_t> Entries;
+  
   struct MapCombo
   {
     MapCombo() {}
@@ -197,7 +207,8 @@ private:
     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};
@@ -212,9 +223,10 @@ private:
   }
 
   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 {
@@ -250,3 +262,7 @@ public:
     }
   }
 };
+
+namespace boost {
+  size_t hash_value(const MemRecursorCache::OptTag& rtag);
+}
index 9a4b60963c4818b8c270a5a54fbb85203a218120..1c1dea71313f8321d021e3635ac94aa1f582526c 100644 (file)
@@ -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<AAAARecordContent>(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<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
@@ -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<ARecordContent>(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<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);
@@ -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<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
@@ -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<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());
 
@@ -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<ARecordContent>(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<ARecordContent>(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<ARecordContent>(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<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));
 }
@@ -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<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);
@@ -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<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)
@@ -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<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);
@@ -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<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);
@@ -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<AAAARecordContent>(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<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);
@@ -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<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()
index d0847bb4a3999eba4258db6cc61c8f1710fc7608..2fc7e325bfdd1e9038e724003bb8a275c9ae8f66 100644 (file)
@@ -317,7 +317,7 @@ BOOST_AUTO_TEST_CASE(test_answer_no_aa)
   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)
index 9fb52c8899cb665d0cbe54c04d80fc27c24705b6..056fb1ef88a12694441111b2ae057c654c10d0ec 100644 (file)
@@ -817,7 +817,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_rrsig_cache_validity)
   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);
index 8364a872c0f48f05ffab0a1d894cf87fc7aa1ac7..59c1ad5ceb777b7af72b1950283ff23a8d8994eb 100644 (file)
@@ -937,7 +937,7 @@ BOOST_AUTO_TEST_CASE(test_cname_plus_authority_ns_ttl)
   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);
@@ -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);
 }
index c7f48b206afe0dc59158379941dcf3de13c549d7..64621ba7c865fc42c280b77dd6eb05a25dd449ee 100644 (file)
@@ -68,13 +68,13 @@ void primeHints(void)
       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);
@@ -90,11 +90,11 @@ void primeHints(void)
       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));
@@ -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<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
 }
 
 
index 125f337f1b0cc9e2254cbd429060ebfa734099ff..0850b4914b98ba8dd7bef1dfa108a52448313654 100644 (file)
@@ -957,7 +957,7 @@ vector<ComboAddress> 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<AAAARecordContent>(i)) {
@@ -1033,7 +1033,7 @@ void SyncRes::getBestNSFromCache(const DNSName &qname, const QType& qtype, vecto
     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) {
@@ -1043,7 +1043,7 @@ void SyncRes::getBestNSFromCache(const DNSName &qname, const QType& qtype, vecto
           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));
@@ -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<<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);
   }
@@ -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<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()<<": ");
 
@@ -2838,11 +2838,11 @@ RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, LWResult& lwr
         }
       }
       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;
   }
 
index 87cc541c23e024179f1be4c2adcee8cdbba46fe7..9bbadcdb426868112256394c6ca6cefaada403dd 100644 (file)
@@ -782,6 +782,8 @@ public:
   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;