Skip to content

Commit e888cc4

Browse files
authored
Merge pull request #16891 from omoerbeek/rec-aggr-cache-wrap
rec: handle NSEC3 records where hash(owner) > hash(next) in aggressive cache decision
2 parents 4937057 + a9b811c commit e888cc4

4 files changed

Lines changed: 34 additions & 12 deletions

File tree

pdns/recursordist/aggressive_nsec.cc

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <climits>
2424

2525
#include "aggressive_nsec.hh"
26+
#include "base64.hh"
2627
#include "cachecleaner.hh"
2728
#include "recursor_cache.hh"
2829
#include "logger.hh"
@@ -231,16 +232,19 @@ static bool commonPrefixIsLong(const string& one, const string& two, size_t boun
231232
const auto minLength = std::min(one.length(), two.length());
232233

233234
for (size_t i = 0; i < minLength; i++) {
234-
const auto byte1 = one.at(i);
235-
const auto byte2 = two.at(i);
235+
const uint8_t byte1 = one.at(i);
236+
const uint8_t byte2 = two.at(i);
236237
// shortcut
237238
if (byte1 == byte2) {
238239
length += CHAR_BIT;
239-
if (length > bound) {
240-
return true;
241-
}
242240
continue;
243241
}
242+
if (byte1 > byte2) { // order is reversed, implies large number of hashes covered
243+
return false;
244+
}
245+
if (length > bound) {
246+
return true;
247+
}
244248
// bytes differ, let's look at the bits
245249
for (ssize_t j = CHAR_BIT - 1; j >= 0; j--) {
246250
const auto bit1 = byte1 & (1 << j);
@@ -322,11 +326,6 @@ void AggressiveNSECCache::insertNSEC(const DNSName& zone, const DNSName& owner,
322326
return;
323327
}
324328

325-
if (isSmallCoveringNSEC3(owner, content->d_nexthash)) {
326-
/* not accepting small covering answers since they only deny a small subset */
327-
return;
328-
}
329-
330329
// XXX: Ponder storing everything in raw form, without the zone instead. It still needs to be a DNSName for NSEC, though,
331330
// but doing the conversion on cache hits only might be faster
332331
next = DNSName(toBase32Hex(content->d_nexthash)) + zone;

pdns/recursordist/aggressive_nsec.hh

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ public:
9292
return d_nsec3WildcardHits;
9393
}
9494

95-
// exported for unit test purposes
9695
static bool isSmallCoveringNSEC3(const DNSName& owner, const std::string& nextHash);
9796

9897
void prune(time_t now);

pdns/recursordist/syncres.cc

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4689,6 +4689,8 @@ RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, const string&
46894689
}
46904690

46914691
bool seenBogusRRSet = false;
4692+
std::vector<tcache_t::value_type> aggrCacheRecords;
4693+
bool insertIntoAggressiveCache = false;
46924694
for (auto tCacheEntry = tcache.begin(); tCacheEntry != tcache.end(); ++tCacheEntry) {
46934695

46944696
if (tCacheEntry->second.records.empty()) { // this happens when we did store signatures, but passed on the records themselves
@@ -4893,14 +4895,35 @@ RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, const string&
48934895

48944896
if (g_aggressiveNSECCache && (tCacheEntry->first.type == QType::NSEC || tCacheEntry->first.type == QType::NSEC3) && recordState == vState::Secure && !seenAuth.empty()) {
48954897
// Good candidate for NSEC{,3} caching
4896-
g_aggressiveNSECCache->insertNSEC(seenAuth, tCacheEntry->first.name, tCacheEntry->second.records.at(0), tCacheEntry->second.signatures, tCacheEntry->first.type == QType::NSEC3, qname, qtype);
4898+
aggrCacheRecords.emplace_back(*tCacheEntry);
4899+
if (!insertIntoAggressiveCache) {
4900+
if (tCacheEntry->first.type == QType::NSEC) {
4901+
// NSECs have no additonal condition, just take them
4902+
insertIntoAggressiveCache = true;
4903+
}
4904+
else if (tCacheEntry->first.type == QType::NSEC3 && !AggressiveNSECCache::nsec3Disabled()) {
4905+
// If at least one of the NSEC3s covers quite some names, we will take all of them, as likely all of them are needed for a denial proof.
4906+
if (auto content = getRR<NSEC3RecordContent>(tCacheEntry->second.records.at(0)); content != nullptr) {
4907+
if (!AggressiveNSECCache::isSmallCoveringNSEC3(tCacheEntry->first.name, content->d_nexthash)) {
4908+
insertIntoAggressiveCache = true;
4909+
}
4910+
}
4911+
}
4912+
}
48974913
}
48984914

48994915
if (tCacheEntry->first.place == DNSResourceRecord::ANSWER && ednsmask) {
49004916
d_wasVariable = true;
49014917
}
49024918
}
49034919

4920+
// The primary loop determined if we want to take the NSEC(3) records
4921+
if (insertIntoAggressiveCache) {
4922+
for (const auto& entry : aggrCacheRecords) {
4923+
g_aggressiveNSECCache->insertNSEC(seenAuth, entry.first.name, entry.second.records.at(0), entry.second.signatures, entry.first.type == QType::NSEC3, qname, qtype);
4924+
}
4925+
}
4926+
49044927
if (gatherWildcardProof) {
49054928
if (auto wcIt = wildcardCandidates.find(qname); wcIt != wildcardCandidates.end() && !wcIt->second) {
49064929
// the queried name was not expanded from a wildcard, a record in the CNAME chain was, so we don't need to gather wildcard proof now: we will do that when looking up the CNAME chain

pdns/recursordist/test-aggressive_nsec_cc.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ BOOST_AUTO_TEST_CASE(test_small_covering_nsec3)
2424
{"8ujhshp2lhmnpoo9qde4blg4gq3hgl99", "8ujhshp2lhmnpoo9qde4blg4gq3hgl99", 0, false},
2525
{"8ujhshp2lhmnpoo9qde4blg4gq3hgl99", "8ujhshp2lhmnpoo9qde4blg4gq3hgl99", 1, false},
2626
{"8ujhshp2lhmnpoo9qde4blg4gq3hgl99", "8ujhshp2lhmnpoo9qde4blg4gq3hgl99", 157, false},
27+
{"e731tdlrdip60smlihgnprqdspq0idlp", "e731tcnkl5al8t4r64b292blt4c37j3h", 1, false}, // note order: 1st > 2nd
2728
};
2829

2930
for (const auto& [owner, next, boundary, result] : table) {

0 commit comments

Comments
 (0)