From 113dfd51b560a2228d41024aa6fd33a991f1e990 Mon Sep 17 00:00:00 2001 From: Miod Vallat Date: Fri, 11 Jul 2025 09:50:23 +0200 Subject: [PATCH] Correctly remove NSEC3 records when removing ENT records. Signed-off-by: Miod Vallat --- modules/lmdbbackend/lmdbbackend.cc | 44 ++++++++++++++++++++++++------ modules/lmdbbackend/lmdbbackend.hh | 2 +- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/modules/lmdbbackend/lmdbbackend.cc b/modules/lmdbbackend/lmdbbackend.cc index 8dc749ed48..2f78de9f86 100644 --- a/modules/lmdbbackend/lmdbbackend.cc +++ b/modules/lmdbbackend/lmdbbackend.cc @@ -1107,7 +1107,7 @@ static std::shared_ptr deserializeContentZR(uint16_t qtype, co #define StringView string #endif -void LMDBBackend::deleteDomainRecords(RecordsRWTransaction& txn, uint16_t qtype, const std::string& match) +void LMDBBackend::deleteDomainRecords(RecordsRWTransaction& txn, const std::string& match) { auto cursor = txn.txn->getCursor(txn.db->dbi); MDBOutVal key{}; @@ -1115,9 +1115,7 @@ void LMDBBackend::deleteDomainRecords(RecordsRWTransaction& txn, uint16_t qtype, if (cursor.prefix(match, key, val) == 0) { do { - if (qtype == QType::ANY || compoundOrdername::getQType(key.getNoStripHeader()) == qtype) { - cursor.del(key); - } + cursor.del(key); } while (cursor.next(key, val) == 0); } } @@ -1157,7 +1155,7 @@ bool LMDBBackend::startTransaction(const ZoneName& domain, domainid_t domain_id) if (domain_id != UnknownDomainID) { compoundOrdername order; string match = order(domain_id); - LMDBBackend::deleteDomainRecords(*d_rwtxn, QType::ANY, match); + LMDBBackend::deleteDomainRecords(*d_rwtxn, match); } return true; @@ -1370,7 +1368,7 @@ bool LMDBBackend::replaceRRSet(domainid_t domain_id, const DNSName& qname, const // remove the back chain record. deleteNSEC3RecordPair(txn, domain_id, relative); match = co(domain_id, relative); - deleteDomainRecords(*txn, QType::ANY, match); + deleteDomainRecords(*txn, match); // Update key if insertions are to follow if (!rrset.empty()) { match = co(domain_id, relative, rrset.front().qtype.getCode()); @@ -2850,13 +2848,43 @@ bool LMDBBackend::updateEmptyNonTerminals(domainid_t domain_id, set& in compoundOrdername order; if (remove) { string match = order(domain_id); - LMDBBackend::deleteDomainRecords(*txn, QType::ENT, match); + // We can not simply blindly delete all ENT records the way + // deleteDomainRecords() would do, as we also need to remove + // NSEC3 records for these ENT, if any. + { + auto cursor = txn->txn->getCursor(txn->db->dbi); + MDBOutVal key{}; + MDBOutVal val{}; + std::vector names; + + if (cursor.prefix(match, key, val) == 0) { + do { + if (compoundOrdername::getQType(key.getNoStripHeader()) == QType::ENT) { + // We need to remember the name of the records we're deleting, so + // as to remove the matching NSEC3 records, if any. + // (we can't invoke deleteNSEC3RecordPair here as doing this + // could make our cursor invalid) + DNSName qname = compoundOrdername::getQName(key.getNoStripHeader()); + names.emplace_back(qname); + cursor.del(key); + } + } while (cursor.next(key, val) == 0); + } + for (const auto& qname : names) { + deleteNSEC3RecordPair(txn, domain_id, qname); + } + } } else { for (auto name : erase) { // cout <<" -"<txn->del(txn->db->dbi, order(domain_id, name, QType::ENT)); + std::string match = order(domain_id, name, QType::ENT); + MDBOutVal val{}; + if (txn->txn->get(txn->db->dbi, match, val) == 0) { + txn->txn->del(txn->db->dbi, match); + deleteNSEC3RecordPair(txn, domain_id, name); + } } } for (const auto& name : insert) { diff --git a/modules/lmdbbackend/lmdbbackend.hh b/modules/lmdbbackend/lmdbbackend.hh index 86b1cc9670..c3997e9dfb 100644 --- a/modules/lmdbbackend/lmdbbackend.hh +++ b/modules/lmdbbackend/lmdbbackend.hh @@ -334,7 +334,7 @@ private: std::shared_ptr getRecordsROTransaction(domainid_t id, const std::shared_ptr& rwtxn = nullptr); int genChangeDomain(const ZoneName& domain, const std::function& func); int genChangeDomain(domainid_t id, const std::function& func); - static void deleteDomainRecords(RecordsRWTransaction& txn, uint16_t qtype, const std::string& match); + static void deleteDomainRecords(RecordsRWTransaction& txn, const std::string& match); void getAllDomainsFiltered(vector* domains, const std::function& allow); -- 2.47.2