From: Remi Gacogne Date: Tue, 29 Dec 2020 17:16:58 +0000 (+0100) Subject: rec: Aggressive NSEC3 caching as well! X-Git-Tag: dnsdist-1.6.0-alpha2~12^2~31 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=bf720981b4e24c09286343db5f2842af00f74b32;p=thirdparty%2Fpdns.git rec: Aggressive NSEC3 caching as well! --- diff --git a/pdns/recursordist/aggressive_nsec.cc b/pdns/recursordist/aggressive_nsec.cc index b0bba44f34..068ad27cb5 100644 --- a/pdns/recursordist/aggressive_nsec.cc +++ b/pdns/recursordist/aggressive_nsec.cc @@ -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_ptrd_entries) { + cerr<<"- "< "< end of list, looking for the lower bound to "<d_entries.get(); 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"<d_owner<d_owner != name) { + //cerr<<"the lower bound is already the first entry, let's if the end is a wrap"< 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 "<d_owner<d_owner<d_owner<<" "<d_next<d_owner<<" "<d_next<d_ttd <= now) { - cerr<<"not using it"<(zoneEntry->d_entries, it); return false; } entry = *it; + moveCacheItemToBack(zoneEntry->d_entries, it); return true; } -bool AggressiveNSECCache::getDenial(time_t now, const DNSName& name, const QType& type, std::vector&ret, int& res, const ComboAddress& who, const boost::optional& routingTag, bool doDNSSEC) -{ - auto zoneEntry = getBestZone(name); - if (!zoneEntry) { - cerr<<"zone info not found"< soaSet; - std::vector> 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"<& zoneEntry, const DNSName& name, ZoneEntry::CacheEntry& entry) { - if (zoneEntry->d_nsec3) { - cerr<<"nsec 3"< 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 "<d_entries.get(); + auto entries = idx.equal_range(name); - auto content = std::dynamic_pointer_cast(entry.d_record); - if (!content) { - return false; - } + for (auto it = entries.first; it != entries.second; ++it) { - cerr<<"nsecFound "< 1) { - DNSName wc(name); - wc.chopOff(); - wc = g_wildcarddnsname + wc; - - cerr<<"looking for nsec before "<d_owner != name) { + continue; + } - cerr<<"wc nsec found "<d_entries.project(it); + if (it->d_ttd <= now) { + moveCacheItemToFront(zoneEntry->d_entries, firstIndexIterator); + return false; } - } - if (!covered) { - return false; + entry = *it; + moveCacheItemToBack(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& recordSet, std::vector> signatures, const DNSName& owner, bool doDNSSEC, std::vector& 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& content, std::vector> signatures, bool doDNSSEC, std::vector& 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&ret, int& res, vState& state) +bool AggressiveNSECCache::getNSEC3Denial(time_t now, std::shared_ptr& zoneEntry, std::vector& soaSet, std::vector>& soaSignatures, const DNSName& name, const QType& type, std::vector& ret, int& res, bool doDNSSEC) { -#warning FIXME: nsec3 - cerr<<"nsec3, sorry"< g_maxNSEC3Iterations) { - return false; - } + const auto& salt = zoneEntry->d_salt; + const auto iterations = zoneEntry->d_iterations; + const auto& zone = zoneEntry->d_zone; - vector cset; - vector> signatures; - vState cachedState; + auto nameHash = DNSName(toBase32Hex(hashQNameWithSalt(salt, iterations, name))) + zone; - auto qnameHash = toBase32Hex(hashQNameWithSalt(salt, iterations, qname)); + cerr<<"looking for nsec3 "<(exactNSEC3.d_record); + if (!nsec3) { + return false; + } - cerr<<"looking for nsec3 "<<(DNSName(qnameHash) + zone)<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 "<isSet(type.getCode())) { return false; } - return false; - } + if (nsec3->isSet(QType::CNAME)) { + return false; + } + cerr<<"Direct match, done!"<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"<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 "<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"<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"<(wcEntry.d_record); + if (!nsec3) { + return false; + } -bool SyncRes::doAggressiveNSECCacheCheck(const std::string& prefix, const DNSName& qname, const QType& qtype, vector&ret, int& res, vState& state) -{ - if (!g_aggressiveNSECCache) { - cerr<<"no aggressive NSEC"<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"<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!"<& ret, int& res, const ComboAddress& who, const boost::optional& routingTag, bool doDNSSEC) +{ + auto zoneEntry = getBestZone(name); + if (!zoneEntry) { cerr<<"zone info not found"< soaSet; std::vector> 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"< cset; - vector> signatures; - - if (nsec3) { - return doAggressiveNSEC3Cache(prefix, qname, qtype, zone, salt, iterations, ret, res, state); + if (zoneEntry->d_nsec3) { + cerr<<"nsec 3"< wcSet; - std::vector> wcSignatures; + ZoneEntry::CacheEntry entry; + ZoneEntry::CacheEntry wcEntry; + bool covered = false; + bool needWildcard = false; - cerr<<"looking for nsec before "<getNSECBefore(d_now.tv_sec, zone, qname, QType::NSEC, nsecFound, cset, signatures, cachedState)) { - cerr<<"nothing found in aggressive cache either"<(entry.d_record); + if (!content) { return false; } - bool covered = false; - cerr<<"Got "<(nsecRecord); - if (!content) { + cerr<<"nsecFound "<d_next< 1) { - DNSName wc = qname; - wc.chopOff(); - wc = g_wildcarddnsname + wc; - - cerr<<"looking for nsec before "<getNSECBefore(d_now.tv_sec, zone, wc, QType::NSEC, wcNSEC, wcSet, wcSignatures, cachedState)) { - cerr<<"nothing found in aggressive cache for Wildcard"< commonLabelsCount) { + if (!wc.chopOff()) { + break; } + --labelsCount; + } + wc = g_wildcarddnsname + wc; - if (wcSet.empty() || cachedState != vState::Secure) { - cerr<<"nothing usable"<(wcNsecRecord); - if (!wcContent) { + cerr<<"wc nsec found "<(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 "<d_next<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 "<::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 diff --git a/pdns/recursordist/aggressive_nsec.hh b/pdns/recursordist/aggressive_nsec.hh index 5c8d8d1934..e720343c11 100644 --- a/pdns/recursordist/aggressive_nsec.hh +++ b/pdns/recursordist/aggressive_nsec.hh @@ -30,6 +30,7 @@ #include #include +#include "base32.hh" #include "dnsname.hh" #include "dnsrecords.hh" #include "lock.hh" @@ -94,6 +95,8 @@ private: std::shared_ptr getZone(const DNSName& zone); std::shared_ptr getBestZone(const DNSName& zone); bool getNSECBefore(time_t now, std::shared_ptr& zoneEntry, const DNSName& name, ZoneEntry::CacheEntry& entry); + bool getNSEC3(time_t now, std::shared_ptr& zoneEntry, const DNSName& name, ZoneEntry::CacheEntry& entry); + bool getNSEC3Denial(time_t now, std::shared_ptr& zoneEntry, std::vector& soaSet, std::vector>& soaSignatures, const DNSName& name, const QType& type, std::vector& ret, int& res, bool doDNSSEC); SuffixMatchTree> d_zones; ReadWriteLock d_lock; diff --git a/pdns/validate.cc b/pdns/validate.cc index af3621148e..bf8d689d50 100644 --- a/pdns/validate.cc +++ b/pdns/validate.cc @@ -55,7 +55,7 @@ static vector > 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: "<, sharedDNSKeyRecordContentCompare > vState validateWithKeySet(time_t now, const DNSName& name, const sortedRecords_t& records, const vector >& 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& recs); vState getKeysFor(DNSRecordOracle& dro, const DNSName& zone, skeyset_t& keyset);