]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
rec: Aggressive NSEC3 caching as well!
authorRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 29 Dec 2020 17:16:58 +0000 (18:16 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Mon, 22 Feb 2021 17:42:04 +0000 (18:42 +0100)
pdns/recursordist/aggressive_nsec.cc
pdns/recursordist/aggressive_nsec.hh
pdns/validate.cc
pdns/validate.hh

index b0bba44f3426ee1aa72b566d48bdb56e162846cc..068ad27cb5a8ef721d2180441fda8061aa5b5b6a 100644 (file)
@@ -21,6 +21,7 @@
  */
 
 #include "aggressive_nsec.hh"
+#include "cachecleaner.hh"
 #include "recursor_cache.hh"
 #include "validate.hh"
 
@@ -91,7 +92,11 @@ void AggressiveNSECCache::insertNSEC(const DNSName& zone, const DNSName& owner,
       if (!content) {
         throw std::runtime_error("Error getting the content from a NSEC3 record");
       }
-      next = DNSName(content->d_nexthash) + zone;
+      next = DNSName(toBase32Hex(content->d_nexthash)) + zone;
+      if (g_maxNSEC3Iterations && content->d_iterations > g_maxNSEC3Iterations) {
+        return;
+      }
+
       entry->d_iterations = content->d_iterations;
       entry->d_salt = content->d_salt;
     }
@@ -108,151 +113,108 @@ bool AggressiveNSECCache::getNSECBefore(time_t now, std::shared_ptr<AggressiveNS
     return false;
   }
 
+#if 0
+  cerr<<"We have:"<<endl;
+  for (const auto& ent : zoneEntry->d_entries) {
+    cerr<<"- "<<ent.d_owner<<" -> "<<ent.d_next<<endl;
+  }
+  cerr<<"=> end of list, looking for the lower bound to "<<name<<endl;
+#endif
   auto& idx = zoneEntry->d_entries.get<ZoneEntry::OrderedTag>();
   auto it = idx.lower_bound(name);
   bool end = false;
+  bool wrapped = false;
 
-  while (!end && (it == idx.end() || (it->d_owner != name && !it->d_owner.canonCompare(name))))
-  {
-    if (it == idx.end()) {
-      cerr<<"GOT END"<<endl;
-    }
-    else {
-      cerr<<"got "<<it->d_owner<<endl;
-    }
+  if (it == idx.begin() && it->d_owner != name) {
+    //cerr<<"the lower bound is already the first entry, let's if the end is a wrap"<<endl;
+    it = idx.end();
+    // we know the map is not empty
+    it--;
+    // might be that owner > name && name < next
+    // can't go further, but perhaps we wrapped?
+    wrapped = true;
+  }
 
+  while (!end && !wrapped && (it == idx.end() || (it->d_owner != name && !it->d_owner.canonCompare(name))))
+  {
     if (it == idx.begin()) {
-      // can't go further, but perhaps we wrapped?
-      it = idx.end();
-      it--;
-      if (!it->d_owner.canonCompare(name) && it->d_next.canonCompare(name)) {
-        break;
-      }
       end = true;
       break;
     }
     else {
       it--;
-       cerr<<"looping with "<<it->d_owner<<endl;
+      // cerr<<"looping with "<<it->d_owner<<endl;
     }
   }
 
   if (end) {
-     cerr<<"nothing left"<<endl;
+    //cerr<<"nothing left"<<endl;
     return false;
   }
 
-  cerr<<"considering "<<it->d_owner<<" "<<it->d_next<<endl;
+  //cerr<<"considering "<<it->d_owner<<" "<<it->d_next<<endl;
 
   if (it->d_ttd <= now) {
-     cerr<<"not using it"<<endl;
+    //cerr<<"not using it"<<endl;
+    moveCacheItemToFront<ZoneEntry::SequencedTag>(zoneEntry->d_entries, it);
     return false;
   }
 
   entry = *it;
+  moveCacheItemToBack<ZoneEntry::SequencedTag>(zoneEntry->d_entries, it);
   return true;
 }
 
-bool AggressiveNSECCache::getDenial(time_t now, const DNSName& name, const QType& type, std::vector<DNSRecord>&ret, int& res, const ComboAddress& who, const boost::optional<std::string>& routingTag, bool doDNSSEC)
-{
-  auto zoneEntry = getBestZone(name);
-  if (!zoneEntry) {
-    cerr<<"zone info not found"<<endl;
-    return false;
-  }
-
-  vState cachedState;
-  std::vector<DNSRecord> soaSet;
-  std::vector<std::shared_ptr<RRSIGRecordContent>> soaSignatures;
-  if (g_recCache->get(now, zoneEntry->d_zone, QType::SOA, true, &soaSet, who, false, routingTag, doDNSSEC ? &soaSignatures : nullptr, nullptr, nullptr, &cachedState) <= 0 || cachedState != vState::Secure) {
-    cerr<<"could not find SOA"<<endl;
-    return false;
-  }
+bool AggressiveNSECCache::getNSEC3(time_t now, std::shared_ptr<AggressiveNSECCache::ZoneEntry>& zoneEntry, const DNSName& name, ZoneEntry::CacheEntry& entry) {
 
-  if (zoneEntry->d_nsec3) {
-    cerr<<"nsec 3"<<endl;
+  std::lock_guard<std::mutex> lock(zoneEntry->d_lock);
+  if (zoneEntry->d_entries.empty()) {
     return false;
-    //return doAggressiveNSEC3Cache(prefix, qname, qtype, zone, salt, iterations, ret, res, state);
   }
 
-  ZoneEntry::CacheEntry entry;
-  ZoneEntry::CacheEntry wcEntry;
-  bool covered = false;
-  bool needWildcard = false;
-
-  cerr<<"looking for nsec before "<<name<<endl;
-  if (!getNSECBefore(now, zoneEntry, name, entry)) {
-    cerr<<"nothing found in aggressive cache either"<<endl;
-    return false;
-  }
+  auto& idx = zoneEntry->d_entries.get<ZoneEntry::HashedTag>();
+  auto entries = idx.equal_range(name);
 
-  auto content = std::dynamic_pointer_cast<NSECRecordContent>(entry.d_record);
-  if (!content) {
-    return false;
-  }
+  for (auto it = entries.first; it != entries.second; ++it) {
 
-  cerr<<"nsecFound "<<entry.d_owner<<endl;
-  auto denial = matchesNSEC(name, type.getCode(), entry.d_owner, content, entry.d_signatures);
-  if (denial == dState::NXQTYPE) {
-    covered = true;
-    res = RCode::NoError;
-  }
-  else if (denial == dState::NXDOMAIN) {
-    if (name.countLabels() > 1) {
-      DNSName wc(name);
-      wc.chopOff();
-      wc = g_wildcarddnsname + wc;
-
-      cerr<<"looking for nsec before "<<wc<<endl;
-      if (!getNSECBefore(now, zoneEntry, wc, wcEntry)) {
-         cerr<<"nothing found in aggressive cache for Wildcard"<<endl;
-        return false;
-      }
+    if (it->d_owner != name) {
+      continue;
+    }
 
-      cerr<<"wc nsec found "<<wcEntry.d_owner<<endl;
-      if (wcEntry.d_owner == entry.d_owner) {
-        covered = true;
-        res = RCode::NXDomain;
-      }
-      else {
-        if (wcEntry.d_owner == wc) {
-#warning FIXME: if the wc does exist but the type does not, it is actually quite simple
-          /* too complicated for now */
-          return false;
-        }
-        else if (isCoveredByNSEC(wc, wcEntry.d_owner, wcEntry.d_next)) {
-          cerr<<"next is "<<wcEntry.d_next<<endl;
-          covered = true;
-          res = RCode::NXDomain;
-          needWildcard = true;
-        }
-      }
+    auto firstIndexIterator = zoneEntry->d_entries.project<ZoneEntry::OrderedTag>(it);
+    if (it->d_ttd <= now) {
+      moveCacheItemToFront<ZoneEntry::SequencedTag>(zoneEntry->d_entries, firstIndexIterator);
+      return false;
     }
-  }
 
-  if (!covered) {
-    return false;
+    entry = *it;
+    moveCacheItemToBack<ZoneEntry::SequencedTag>(zoneEntry->d_entries, firstIndexIterator);
+    return true;
   }
 
-  uint32_t ttl=0;
+  return false;
+}
 
-  ret.reserve(ret.size() + soaSet.size() + soaSignatures.size() + /* NSEC */ 1 + entry.d_signatures.size() + (needWildcard ? (/* NSEC */ 1 + wcEntry.d_signatures.size()) : 0));
+static void addToRRSet(const time_t now, std::vector<DNSRecord>& recordSet, std::vector<std::shared_ptr<RRSIGRecordContent>> signatures, const DNSName& owner, bool doDNSSEC, std::vector<DNSRecord>& ret)
+{
+  uint32_t ttl = 0;
 
-  for (auto& record : soaSet) {
+  for (auto& record : recordSet) {
     if (record.d_class != QClass::IN) {
       continue;
     }
 
     record.d_ttl -= now;
     ttl = record.d_ttl;
+    record.d_place = DNSResourceRecord::AUTHORITY;
     ret.push_back(std::move(record));
   }
 
   if (doDNSSEC) {
-    for (auto& signature : soaSignatures) {
+    for (auto& signature : signatures) {
       DNSRecord dr;
       dr.d_type = QType::RRSIG;
-      dr.d_name = zoneEntry->d_zone;
+      dr.d_name = owner;
       dr.d_ttl = ttl;
       dr.d_content = std::move(signature);
       dr.d_place = DNSResourceRecord::AUTHORITY;
@@ -260,22 +222,24 @@ bool AggressiveNSECCache::getDenial(time_t now, const DNSName& name, const QType
       ret.push_back(std::move(dr));
     }
   }
+}
 
+static void addRecordToRRSet(time_t now, const DNSName& owner, const QType& type, uint32_t ttl, std::shared_ptr<DNSRecordContent>& content, std::vector<std::shared_ptr<RRSIGRecordContent>> signatures, bool doDNSSEC, std::vector<DNSRecord>& ret)
+{
   DNSRecord nsecRec;
-  nsecRec.d_type = QType::NSEC;
-  nsecRec.d_name = entry.d_owner;
-  nsecRec.d_ttl = entry.d_ttd - now;
-  ttl = nsecRec.d_ttl;
-  nsecRec.d_content = std::move(entry.d_record);
+  nsecRec.d_type = type.getCode();
+  nsecRec.d_name = owner;
+  nsecRec.d_ttl = ttl;
+  nsecRec.d_content = std::move(content);
   nsecRec.d_place = DNSResourceRecord::AUTHORITY;
   nsecRec.d_class = QClass::IN;
   ret.push_back(std::move(nsecRec));
 
   if (doDNSSEC) {
-    for (auto& signature : entry.d_signatures) {
+    for (auto& signature : signatures) {
       DNSRecord dr;
       dr.d_type = QType::RRSIG;
-      dr.d_name = entry.d_owner;
+      dr.d_name = owner;
       dr.d_ttl = ttl;
       dr.d_content = std::move(signature);
       dr.d_place = DNSResourceRecord::AUTHORITY;
@@ -283,72 +247,50 @@ bool AggressiveNSECCache::getDenial(time_t now, const DNSName& name, const QType
       ret.push_back(std::move(dr));
     }
   }
-
-  if (needWildcard) {
-    DNSRecord wcNsecRec;
-    wcNsecRec.d_type = QType::NSEC;
-    wcNsecRec.d_name = wcEntry.d_owner;
-    wcNsecRec.d_ttl = wcEntry.d_ttd - now;
-    ttl = wcNsecRec.d_ttl;
-    wcNsecRec.d_content = std::move(wcEntry.d_record);
-    wcNsecRec.d_place = DNSResourceRecord::AUTHORITY;
-    wcNsecRec.d_class = QClass::IN;
-    ret.push_back(std::move(wcNsecRec));
-
-    if (doDNSSEC) {
-      for (auto& signature : wcEntry.d_signatures) {
-        DNSRecord dr;
-        dr.d_type = QType::RRSIG;
-        dr.d_name = wcEntry.d_owner;
-        dr.d_ttl = ttl;
-        dr.d_content = std::move(signature);
-        dr.d_place = DNSResourceRecord::AUTHORITY;
-        dr.d_class = QClass::IN;
-        ret.push_back(std::move(dr));
-      }
-    }
-  }
-
-  return true;
 }
 
-#if 0
-
-bool SyncRes::doAggressiveNSEC3Cache(const std::string& prefix, const DNSName& qname, const QType& qtype, const DNSName& zone, const std::string& salt, uint16_t iterations, vector<DNSRecord>&ret, int& res, vState& state)
+bool AggressiveNSECCache::getNSEC3Denial(time_t now, std::shared_ptr<AggressiveNSECCache::ZoneEntry>& zoneEntry, std::vector<DNSRecord>& soaSet, std::vector<std::shared_ptr<RRSIGRecordContent>>& soaSignatures, const DNSName& name, const QType& type, std::vector<DNSRecord>& ret, int& res, bool doDNSSEC)
 {
-#warning FIXME: nsec3
-  cerr<<"nsec3, sorry"<<endl;
-  if (g_maxNSEC3Iterations && iterations > g_maxNSEC3Iterations) {
-    return false;
-  }
+  const auto& salt = zoneEntry->d_salt;
+  const auto iterations = zoneEntry->d_iterations;
+  const auto& zone = zoneEntry->d_zone;
 
-  vector<DNSRecord> cset;
-  vector<std::shared_ptr<RRSIGRecordContent>> signatures;
-  vState cachedState;
+  auto nameHash = DNSName(toBase32Hex(hashQNameWithSalt(salt, iterations, name))) + zone;
 
-  auto qnameHash = toBase32Hex(hashQNameWithSalt(salt, iterations, qname));
+  cerr<<"looking for nsec3 "<<nameHash<<endl;
+  ZoneEntry::CacheEntry exactNSEC3;
+  if (getNSEC3(now, zoneEntry, nameHash, exactNSEC3)) {
+    cerr<<"found direct match "<<nameHash<<endl;
+    auto nsec3 = std::dynamic_pointer_cast<NSEC3RecordContent>(exactNSEC3.d_record);
+    if (!nsec3) {
+      return false;
+    }
 
-  cerr<<"looking for nsec3 "<<(DNSName(qnameHash) + zone)<<endl;
-  DNSName nsec3Found;
-  if (g_recCache->get(d_now.tv_sec, DNSName(qnameHash) + zone, QType::NSEC3, true, &cset, d_cacheRemote, false, d_routingTag, d_doDNSSEC ? &signatures : nullptr, nullptr, nullptr, &cachedState, nullptr, &zone) > 0) {
-    cerr<<"found direct match "<<qnameHash<<endl;
-    if (cachedState == vState::Secure) {
-      cerr<<"but not secure"<<endl;
+    if (nsec3->isSet(type.getCode())) {
       return false;
     }
 
-    return false;
-  }
+    if (nsec3->isSet(QType::CNAME)) {
+      return false;
+    }
 
+    cerr<<"Direct match, done!"<<endl;
+    addToRRSet(now, soaSet, soaSignatures, zoneEntry->d_zone, doDNSSEC, ret);
+    addRecordToRRSet(now, exactNSEC3.d_owner, QType::NSEC, exactNSEC3.d_ttd - now, exactNSEC3.d_record, exactNSEC3.d_signatures, doDNSSEC, ret);
+    return true;
+  }
+#warning FIXME opt-out / ENT
+#warning FIXME ancestor delegation
   cerr<<"no direct match, looking for closest encloser"<<endl;
-  DNSName closestEncloser(qname);
+  DNSName closestEncloser(name);
   bool found = false;
+  ZoneEntry::CacheEntry closestNSEC3;
   while (!found && closestEncloser.chopOff()) {
-    auto closestHash = toBase32Hex(hashQNameWithSalt(salt, iterations, closestEncloser));
-    cerr<<"looking for nsec3 "<<(DNSName(closestHash) + zone)<<endl;
+    auto closestHash = DNSName(toBase32Hex(hashQNameWithSalt(salt, iterations, closestEncloser))) + zone;
+    cerr<<"looking for nsec3 "<<closestHash<<endl;
 
-    if (g_recCache->get(d_now.tv_sec, DNSName(closestHash) + zone, QType::NSEC3, true, &cset, d_cacheRemote, false, d_routingTag, d_doDNSSEC ? &signatures : nullptr, nullptr, nullptr, &cachedState, nullptr, &zone) > 0) {
-      cerr<<"found direct match for closest encloser "<<closestHash<<endl;
+    if (getNSEC3(now, zoneEntry, closestHash, closestNSEC3)) {
+      cerr<<"found next closest encloser at "<<closestEncloser<<endl;
       found = true;
       break;
     }
@@ -359,46 +301,72 @@ bool SyncRes::doAggressiveNSEC3Cache(const std::string& prefix, const DNSName& q
     return false;
   }
 
-  unsigned int labelIdx = qname.countLabels() - closestEncloser.countLabels();
+  unsigned int labelIdx = name.countLabels() - closestEncloser.countLabels();
   if (labelIdx < 1) {
     return false;
   }
 
   DNSName nsecFound;
   DNSName nextCloser(closestEncloser);
-  nextCloser.prependRawLabel(qname.getRawLabel(labelIdx - 1));
+  nextCloser.prependRawLabel(name.getRawLabel(labelIdx - 1));
   auto nextCloserHash = toBase32Hex(hashQNameWithSalt(salt, iterations, nextCloser));
   cerr<<"looking for a NSEC3 covering the next closer "<<nextCloser<<": "<<nextCloserHash<<endl;
 
-  if (!g_recCache->getNSECBefore(d_now.tv_sec, zone, DNSName(nextCloserHash) + zone, QType::NSEC3, nsecFound, cset, signatures, cachedState)) {
+  ZoneEntry::CacheEntry nextCloserEntry;
+  if (!getNSECBefore(now, zoneEntry, DNSName(nextCloserHash) + zone, nextCloserEntry)) {
     cerr<<"nothing found for the next closer in aggressive cache"<<endl;
     return false;
   }
 
+  if (!isCoveredByNSEC3Hash(DNSName(nextCloserHash) + zone, nextCloserEntry.d_owner, nextCloserEntry.d_next)) {
+    cerr<<"no covering record found for the next closer in aggressive cache"<<endl;
+    return false;
+  }
+
   DNSName wildcard(g_wildcarddnsname + closestEncloser);
   auto wcHash = toBase32Hex(hashQNameWithSalt(salt, iterations, wildcard));
   cerr<<"looking for a NSEC3 covering the wildcard "<<wildcard<<": "<<wcHash<<endl;
 
-  if (!g_recCache->getNSECBefore(d_now.tv_sec, zone, DNSName(wcHash) + zone, QType::NSEC3, nsecFound, cset, signatures, cachedState)) {
+  ZoneEntry::CacheEntry wcEntry;
+  if (!getNSECBefore(now, zoneEntry, DNSName(wcHash) + zone, wcEntry)) {
     cerr<<"nothing found for the wildcard in aggressive cache"<<endl;
     return false;
   }
 
-  return false;
-}
+  if ((DNSName(wcHash) + zone) == wcEntry.d_owner) {
+    auto nsec3 = std::dynamic_pointer_cast<NSEC3RecordContent>(wcEntry.d_record);
+    if (!nsec3) {
+      return false;
+    }
 
-bool SyncRes::doAggressiveNSECCacheCheck(const std::string& prefix, const DNSName& qname, const QType& qtype, vector<DNSRecord>&ret, int& res, vState& state)
-{
-  if (!g_aggressiveNSECCache) {
-    cerr<<"no aggressive NSEC"<<endl;
-    return false;
+    if (nsec3->isSet(type.getCode())) {
+      return false;
+    }
+
+    if (nsec3->isSet(QType::CNAME)) {
+      return false;
+    }
+  }
+  else {
+    if (!isCoveredByNSEC3Hash(DNSName(wcHash) + zone, wcEntry.d_owner, wcEntry.d_next)) {
+      cerr<<"no covering record found for the wildcard in aggressive cache"<<endl;
+      return false;
+    }
   }
 
-  DNSName zone(qname);
-  std::string salt;
-  uint16_t iterations = 0;
-  bool nsec3 = false;
-  if (!g_aggressiveNSECCache->getBestZoneInfo(zone, nsec3, salt, iterations)) {
+  addToRRSet(now, soaSet, soaSignatures, zoneEntry->d_zone, doDNSSEC, ret);
+  addRecordToRRSet(now, closestNSEC3.d_owner, QType::NSEC3, closestNSEC3.d_ttd - now, closestNSEC3.d_record, closestNSEC3.d_signatures, doDNSSEC, ret);
+  addRecordToRRSet(now, nextCloserEntry.d_owner, QType::NSEC3, nextCloserEntry.d_ttd - now, nextCloserEntry.d_record, nextCloserEntry.d_signatures, doDNSSEC, ret);
+  addRecordToRRSet(now, wcEntry.d_owner, QType::NSEC3, wcEntry.d_ttd - now, wcEntry.d_record, wcEntry.d_signatures, doDNSSEC, ret);
+
+  cerr<<"Done!"<<endl;
+  return true;
+}
+
+bool AggressiveNSECCache::getDenial(time_t now, const DNSName& name, const QType& type, std::vector<DNSRecord>& ret, int& res, const ComboAddress& who, const boost::optional<std::string>& routingTag, bool doDNSSEC)
+{
+  auto zoneEntry = getBestZone(name);
+  if (!zoneEntry) {
     cerr<<"zone info not found"<<endl;
     return false;
   }
@@ -406,88 +374,90 @@ bool SyncRes::doAggressiveNSECCacheCheck(const std::string& prefix, const DNSNam
   vState cachedState;
   std::vector<DNSRecord> soaSet;
   std::vector<std::shared_ptr<RRSIGRecordContent>> soaSignatures;
-  if (g_recCache->get(d_now.tv_sec, zone, QType::SOA, true, &soaSet, d_cacheRemote, false, d_routingTag, d_doDNSSEC ? &soaSignatures : nullptr, nullptr, nullptr, &cachedState) <= 0 || cachedState != vState::Secure) {
+  if (g_recCache->get(now, zoneEntry->d_zone, QType::SOA, true, &soaSet, who, false, routingTag, doDNSSEC ? &soaSignatures : nullptr, nullptr, nullptr, &cachedState) <= 0 || cachedState != vState::Secure) {
     cerr<<"could not find SOA"<<endl;
     return false;
   }
 
-  vector<DNSRecord> cset;
-  vector<std::shared_ptr<RRSIGRecordContent>> signatures;
-
-  if (nsec3) {
-    return doAggressiveNSEC3Cache(prefix, qname, qtype, zone, salt, iterations, ret, res, state);
+  if (zoneEntry->d_nsec3) {
+    cerr<<"nsec 3"<<endl;
+    return getNSEC3Denial(now, zoneEntry, soaSet, soaSignatures, name, type, ret, res, doDNSSEC);
   }
 
-  DNSName nsecFound;
-  std::vector<DNSRecord> wcSet;
-  std::vector<std::shared_ptr<RRSIGRecordContent>> wcSignatures;
+  ZoneEntry::CacheEntry entry;
+  ZoneEntry::CacheEntry wcEntry;
+  bool covered = false;
+  bool needWildcard = false;
 
-   cerr<<"looking for nsec before "<<qname<<endl;
-   if (!g_recCache->getNSECBefore(d_now.tv_sec, zone, qname, QType::NSEC, nsecFound, cset, signatures, cachedState)) {
-     cerr<<"nothing found in aggressive cache either"<<endl;
+  cerr<<"looking for nsec before "<<name<<endl;
+  if (!getNSECBefore(now, zoneEntry, name, entry)) {
+    cerr<<"nothing found in aggressive cache either"<<endl;
     return false;
   }
 
-   cerr<<"nsecFound "<<nsecFound<<endl;
-  if (cset.empty() || cachedState != vState::Secure) {
+  auto content = std::dynamic_pointer_cast<NSECRecordContent>(entry.d_record);
+  if (!content) {
     return false;
   }
 
-  bool covered = false;
-   cerr<<"Got "<<cset.size()<<" records"<<endl;
-  const auto& nsecRecord = cset.at(0);
-  auto content = getRR<NSECRecordContent>(nsecRecord);
-  if (!content) {
+  cerr<<"nsecFound "<<entry.d_owner<<endl;
+  auto denial = matchesNSEC(name, type.getCode(), entry.d_owner, content, entry.d_signatures);
+  if (denial == dState::NODENIAL) {
+    cerr<<"no dice"<<endl;
     return false;
   }
-
-   cerr<<"next is "<<content->d_next<<endl;
-  auto denial = matchesNSEC(qname, qtype.getCode(), nsecRecord, signatures);
-  if (denial == dState::NXQTYPE) {
+  else if (denial == dState::NXQTYPE) {
     covered = true;
+    cerr<<"nx qtype"<<endl;
     res = RCode::NoError;
   }
   else if (denial == dState::NXDOMAIN) {
-    if (qname.countLabels() > 1) {
-      DNSName wc = qname;
-      wc.chopOff();
-      wc = g_wildcarddnsname + wc;
-
-       cerr<<"looking for nsec before "<<wc<<endl;
-      DNSName wcNSEC;
-      if (!g_recCache->getNSECBefore(d_now.tv_sec, zone, wc, QType::NSEC, wcNSEC, wcSet, wcSignatures, cachedState)) {
-         cerr<<"nothing found in aggressive cache for Wildcard"<<endl;
-        return false;
+    const DNSName commonLabels = entry.d_owner.getCommonLabels(entry.d_next);
+    DNSName wc(name);
+    auto labelsCount = wc.countLabels();
+    auto commonLabelsCount = commonLabels.countLabels();
+    while (labelsCount > commonLabelsCount) {
+      if (!wc.chopOff()) {
+        break;
       }
+      --labelsCount;
+    }
+    wc = g_wildcarddnsname + wc;
 
-      if (wcSet.empty() || cachedState != vState::Secure) {
-        cerr<<"nothing usable"<<endl;
-        return false;
-      }
+    cerr<<"looking for nsec before "<<wc<<endl;
+    if (!getNSECBefore(now, zoneEntry, wc, wcEntry)) {
+      cerr<<"nothing found in aggressive cache for Wildcard"<<endl;
+      return false;
+    }
 
-      cerr<<"wc nsec found "<<wcNSEC<<endl;
-      if (wcNSEC == nsecFound) {
-        wcSet.clear();
-        wcSignatures.clear();
-        covered = true;
-        res = RCode::NXDomain;
-      }
-      else {
-        const auto& wcNsecRecord = wcSet.at(0);
-        auto wcContent = getRR<NSECRecordContent>(wcNsecRecord);
-        if (!wcContent) {
+    cerr<<"wc nsec found "<<wcEntry.d_owner<<endl;
+    if (wcEntry.d_owner == entry.d_owner) {
+      covered = true;
+      res = RCode::NXDomain;
+    }
+    else {
+      if (wcEntry.d_owner == wc) {
+        auto nsecContent = std::dynamic_pointer_cast<NSECRecordContent>(wcEntry.d_record);
+        if (!nsecContent) {
           return false;
         }
-
-        if (wcNSEC == wc) {
+        if (nsecContent->isSet(type.getCode())) {
           /* too complicated for now */
           return false;
         }
-        else if (isCoveredByNSEC(wc, wcNsecRecord.d_name, wcContent->d_next)) {
-          cerr<<"next is "<<wcContent->d_next<<endl;
-          covered = true;
-          res = RCode::NXDomain;
+        if (nsecContent->isSet(QType::CNAME)) {
+          /* too complicated for now */
+          return false;
         }
+
+        covered = true;
+        res = RCode::NoError;
+      }
+      else if (isCoveredByNSEC(wc, wcEntry.d_owner, wcEntry.d_next)) {
+        cerr<<"next is "<<wcEntry.d_next<<endl;
+        covered = true;
+        res = RCode::NXDomain;
+        needWildcard = true;
       }
     }
   }
@@ -496,83 +466,14 @@ bool SyncRes::doAggressiveNSECCacheCheck(const std::string& prefix, const DNSNam
     return false;
   }
 
-  LOG(prefix<<qname<<": Found aggressive NSEC cache hit for "<<qtype.getName()<<endl);
-
-  uint32_t ttl=0;
-  uint32_t capTTL = std::numeric_limits<uint32_t>::max();
-
-  ret.reserve(ret.size() + soaSet.size() + soaSignatures.size() + cset.size() + signatures.size() + wcSet.size() + wcSignatures.size());
-
-  for (auto& record : soaSet) {
-    if (record.d_class != QClass::IN) {
-      continue;
-    }
-
-    record.d_ttl -= d_now.tv_sec;
-    record.d_ttl = std::min(record.d_ttl, capTTL);
-    ttl = record.d_ttl;
-    ret.push_back(std::move(record));
-  }
-
-  for (auto& signature : soaSignatures) {
-    DNSRecord dr;
-    dr.d_type = QType::RRSIG;
-    dr.d_name = zone;
-    dr.d_ttl = ttl;
-    dr.d_content = std::move(signature);
-    dr.d_place = DNSResourceRecord::ANSWER;
-    dr.d_class = QClass::IN;
-    ret.push_back(std::move(dr));
-  }
-
-  for (auto& record : cset) {
-    if (record.d_class != QClass::IN) {
-      continue;
-    }
-
-    record.d_ttl -= d_now.tv_sec;
-    record.d_ttl = std::min(record.d_ttl, capTTL);
-    ttl = record.d_ttl;
-    ret.push_back(std::move(record));
-  }
-
-  for (auto& signature : signatures) {
-    DNSRecord dr;
-    dr.d_type = QType::RRSIG;
-    dr.d_name = qname;
-    dr.d_ttl = ttl;
-    dr.d_content = std::move(signature);
-    dr.d_place = DNSResourceRecord::ANSWER;
-    dr.d_class = QClass::IN;
-    ret.push_back(std::move(dr));
-  }
-
-  for (auto& record : wcSet) {
-    if (record.d_class != QClass::IN) {
-      continue;
-    }
+  ret.reserve(ret.size() + soaSet.size() + soaSignatures.size() + /* NSEC */ 1 + entry.d_signatures.size() + (needWildcard ? (/* NSEC */ 1 + wcEntry.d_signatures.size()) : 0));
 
-    record.d_ttl -= d_now.tv_sec;
-    record.d_ttl = std::min(record.d_ttl, capTTL);
-    ttl = record.d_ttl;
-    ret.push_back(std::move(record));
-  }
+  addToRRSet(now, soaSet, soaSignatures, zoneEntry->d_zone, doDNSSEC, ret);
+  addRecordToRRSet(now, entry.d_owner, QType::NSEC, entry.d_ttd - now, entry.d_record, entry.d_signatures, doDNSSEC, ret);
 
-  for (auto& signature : wcSignatures) {
-    DNSRecord dr;
-    dr.d_type = QType::RRSIG;
-    dr.d_name = qname;
-    dr.d_ttl = ttl;
-    dr.d_content = std::move(signature);
-    dr.d_place = DNSResourceRecord::ANSWER;
-    dr.d_class = QClass::IN;
-    ret.push_back(std::move(dr));
+  if (needWildcard) {
+    addRecordToRRSet(now, wcEntry.d_owner, QType::NSEC, wcEntry.d_ttd - now, wcEntry.d_record, wcEntry.d_signatures, doDNSSEC, ret);
   }
 
-  /* we would have given up otherwise */
-  state = vState::Secure;
-
   return true;
 }
-
-#endif
index 5c8d8d193437f8146a2bab4a37ab9aefa60a05da..e720343c11c8b96f46ef43b59ce2612332324969 100644 (file)
@@ -30,6 +30,7 @@
 #include <boost/multi_index/key_extractors.hpp>
 #include <boost/multi_index/sequenced_index.hpp>
 
+#include "base32.hh"
 #include "dnsname.hh"
 #include "dnsrecords.hh"
 #include "lock.hh"
@@ -94,6 +95,8 @@ private:
   std::shared_ptr<ZoneEntry> getZone(const DNSName& zone);
   std::shared_ptr<ZoneEntry> getBestZone(const DNSName& zone);
   bool getNSECBefore(time_t now, std::shared_ptr<ZoneEntry>& zoneEntry, const DNSName& name, ZoneEntry::CacheEntry& entry);
+  bool getNSEC3(time_t now, std::shared_ptr<ZoneEntry>& zoneEntry, const DNSName& name, ZoneEntry::CacheEntry& entry);
+  bool getNSEC3Denial(time_t now, std::shared_ptr<ZoneEntry>& zoneEntry, std::vector<DNSRecord>& soaSet, std::vector<std::shared_ptr<RRSIGRecordContent>>& soaSignatures, const DNSName& name, const QType& type, std::vector<DNSRecord>& ret, int& res, bool doDNSSEC);
 
   SuffixMatchTree<std::shared_ptr<ZoneEntry>> d_zones;
   ReadWriteLock d_lock;
index af3621148efdb933cd692758c5afd20058499ad3..bf8d689d504c8323ff411efe59852fdaee1e5eb9 100644 (file)
@@ -55,7 +55,7 @@ static vector<shared_ptr<DNSKEYRecordContent > > getByTag(const skeyset_t& keys,
   return ret;
 }
 
-static bool isCoveredByNSEC3Hash(const std::string& h, const std::string& beginHash, const std::string& nextHash)
+bool isCoveredByNSEC3Hash(const std::string& h, const std::string& beginHash, const std::string& nextHash)
 {
   return ((beginHash < h && h < nextHash) ||          // no wrap          BEGINNING --- HASH -- END
           (nextHash > h  && beginHash > nextHash) ||  // wrap             HASH --- END --- BEGINNING
@@ -63,6 +63,14 @@ static bool isCoveredByNSEC3Hash(const std::string& h, const std::string& beginH
           (beginHash == nextHash && h != beginHash));   // "we have only 1 NSEC3 record, LOL!"
 }
 
+bool isCoveredByNSEC3Hash(const DNSName& h, const DNSName& beginHash, const DNSName& nextHash)
+{
+  return ((beginHash.canonCompare(h) && h.canonCompare(nextHash)) ||          // no wrap          BEGINNING --- HASH -- END
+          (h.canonCompare(nextHash) && nextHash.canonCompare(beginHash)) ||  // wrap             HASH --- END --- BEGINNING
+          (nextHash.canonCompare(beginHash) && beginHash.canonCompare(h)) || // wrap other case  END --- BEGINNING --- HASH
+          (beginHash == nextHash && h != beginHash));   // "we have only 1 NSEC3 record, LOL!"
+}
+
 bool isCoveredByNSEC(const DNSName& name, const DNSName& begin, const DNSName& next)
 {
   return ((begin.canonCompare(name) && name.canonCompare(next)) ||  // no wrap          BEGINNING --- NAME --- NEXT
@@ -304,6 +312,7 @@ static bool provesNoWildCard(const DNSName& qname, const uint16_t qtype, const c
         unsigned int wildcardLabelsCount = wildcard.countLabels();
         while (wildcard.chopOff() && wildcardLabelsCount >= commonLabelsCount) {
           DNSName target = g_wildcarddnsname + wildcard;
+          #warning BUG?? should we decerement wildcardLabelsCount??
 
           LOG("Comparing owner: "<<owner<<" with target: "<<target<<endl);
 
index 0493096bc66f8def0811e95ca1990b2699cf9fd6..7f7df41c6a867a7cf6e2206ffa0b79edb67828fe 100644 (file)
@@ -75,6 +75,8 @@ typedef set<shared_ptr<DNSKEYRecordContent>, sharedDNSKeyRecordContentCompare >
 
 vState validateWithKeySet(time_t now, const DNSName& name, const sortedRecords_t& records, const vector<shared_ptr<RRSIGRecordContent> >& signatures, const skeyset_t& keys, bool validateAllSigs=true);
 bool isCoveredByNSEC(const DNSName& name, const DNSName& begin, const DNSName& next);
+bool isCoveredByNSEC3Hash(const std::string& h, const std::string& beginHash, const std::string& nextHash);
+bool isCoveredByNSEC3Hash(const DNSName& h, const DNSName& beginHash, const DNSName& nextHash);
 void validateWithKeySet(const cspmap_t& rrsets, cspmap_t& validated, const skeyset_t& keys);
 cspmap_t harvestCSPFromRecs(const vector<DNSRecord>& recs);
 vState getKeysFor(DNSRecordOracle& dro, const DNSName& zone, skeyset_t& keyset);