From 133d76c05e25aa69f02be9955feb4ec50b99eb69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Tue, 23 Sep 2025 09:53:41 +0200 Subject: [PATCH 1/6] Move the size of the expired data into expireheader Co-authored-by: Matthijs Mekking --- lib/dns/qpcache.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/lib/dns/qpcache.c b/lib/dns/qpcache.c index d64ba9c880..022e64da69 100644 --- a/lib/dns/qpcache.c +++ b/lib/dns/qpcache.c @@ -452,7 +452,7 @@ cleanup_deadnodes_cb(void *arg); * Cache-eviction routines. */ -static void +static size_t expireheader(dns_slabheader_t *header, isc_rwlocktype_t *nlocktypep, isc_rwlocktype_t *tlocktypep, dns_expire_t reason DNS__DB_FLARG); @@ -506,10 +506,8 @@ expire_lru_headers(qpcache_t *qpdb, uint32_t idx, size_t requested, dns_slabheader_t *header = first_header(top); - expired += rdataset_size(header); - - expireheader(header, nlocktypep, tlocktypep, - dns_expire_lru DNS__DB_FLARG_PASS); + expired += expireheader(header, nlocktypep, tlocktypep, + dns_expire_lru DNS__DB_FLARG_PASS); } while (expired < requested); } @@ -925,9 +923,10 @@ mark_ancient(dns_slabheader_t *header) { /* * Caller must hold the node (write) lock. */ -static void +static size_t expireheader(dns_slabheader_t *header, isc_rwlocktype_t *nlocktypep, isc_rwlocktype_t *tlocktypep, dns_expire_t reason DNS__DB_FLARG) { + size_t expired = rdataset_size(header); mark_ancient(header); if (isc_refcount_current(&HEADERNODE(header)->erefs) == 0) { @@ -944,7 +943,7 @@ expireheader(dns_slabheader_t *header, isc_rwlocktype_t *nlocktypep, tlocktypep DNS__DB_FLARG_PASS); if (qpdb->cachestats == NULL) { - return; + return expired; } switch (reason) { @@ -960,6 +959,8 @@ expireheader(dns_slabheader_t *header, isc_rwlocktype_t *nlocktypep, break; } } + + return expired; } static void @@ -2245,8 +2246,8 @@ qpcnode_expiredata(dns_dbnode_t *node, void *data) { isc_rwlock_t *nlock = &qpdb->buckets[qpnode->locknum].lock; NODE_WRLOCK(nlock, &nlocktype); - expireheader(header, &nlocktype, &tlocktype, - dns_expire_flush DNS__DB_FILELINE); + (void)expireheader(header, &nlocktype, &tlocktype, + dns_expire_flush DNS__DB_FILELINE); NODE_UNLOCK(nlock, &nlocktype); INSIST(tlocktype == isc_rwlocktype_none); } @@ -3795,8 +3796,8 @@ expire_ttl_headers(qpcache_t *qpdb, unsigned int locknum, return; } - expireheader(header, nlocktypep, tlocktypep, - dns_expire_ttl DNS__DB_FLARG_PASS); + (void)expireheader(header, nlocktypep, tlocktypep, + dns_expire_ttl DNS__DB_FLARG_PASS); } } From 6b0e6cb058a02af32f139d71bba549412463a7b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Tue, 23 Sep 2025 09:54:11 +0200 Subject: [PATCH 2/6] Refactor check header There was a pattern where first the header was checked for NULL and then for being stale. In both cases the code path is the same so it makes sense to put them in a separate function. Co-authored-by: Matthijs Mekking --- lib/dns/qpcache.c | 35 ++++++++++------------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/lib/dns/qpcache.c b/lib/dns/qpcache.c index 022e64da69..7537b14449 100644 --- a/lib/dns/qpcache.c +++ b/lib/dns/qpcache.c @@ -1216,6 +1216,11 @@ check_stale_header(dns_slabheader_t *header, qpc_search_t *search) { return true; } +static bool +check_header(dns_slabheader_t *header, qpc_search_t *search) { + return header == NULL || check_stale_header(header, search); +} + /* * Return true if we've found headers for both 'type' and RRSIG('type'), * or (optionally, if 'negtype' is nonzero) if we've found a single @@ -1301,11 +1306,7 @@ check_zonecut(qpcnode_t *node, void *arg DNS__DB_FLARG) { */ DNS_SLABTOP_FOREACH(top, node->data) { dns_slabheader_t *header = first_header(top); - if (header == NULL) { - continue; - } - - if (check_stale_header(header, search)) { + if (check_header(header, search)) { continue; } @@ -1368,11 +1369,7 @@ find_deepest_zonecut(qpc_search_t *search, qpcnode_t *node, */ DNS_SLABTOP_FOREACH(top, node->data) { dns_slabheader_t *header = first_header(top); - if (header == NULL) { - continue; - } - - if (check_stale_header(header, search)) { + if (check_header(header, search)) { continue; } @@ -1476,11 +1473,7 @@ find_coveringnsec(qpc_search_t *search, const dns_name_t *name, NODE_RDLOCK(nlock, &nlocktype); DNS_SLABTOP_FOREACH(top, node->data) { dns_slabheader_t *header = first_header(top); - if (header == NULL) { - continue; - } - - if (check_stale_header(header, search)) { + if (check_header(header, search)) { continue; } @@ -1687,11 +1680,7 @@ qpcache_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version, empty_node = true; DNS_SLABTOP_FOREACH(top, node->data) { dns_slabheader_t *header = first_header(top); - if (header == NULL) { - continue; - } - - if (check_stale_header(header, &search)) { + if (check_header(header, &search)) { continue; } @@ -2130,11 +2119,7 @@ qpcache_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, DNS_SLABTOP_FOREACH(top, qpnode->data) { dns_slabheader_t *header = first_header(top); - if (header == NULL) { - continue; - } - - if (check_stale_header(header, &search)) { + if (check_header(header, &search)) { continue; } From 270f78194e46394ad67012efdb615ea59991855c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Tue, 23 Sep 2025 09:54:30 +0200 Subject: [PATCH 3/6] Refactor find headers Another recurring code pattern that can be moved into a separate function. Co-authored-by: Matthijs Mekking --- lib/dns/qpcache.c | 79 +++++++++++------------------------------------ 1 file changed, 18 insertions(+), 61 deletions(-) diff --git a/lib/dns/qpcache.c b/lib/dns/qpcache.c index 7537b14449..071fbf2de5 100644 --- a/lib/dns/qpcache.c +++ b/lib/dns/qpcache.c @@ -1288,6 +1288,20 @@ both_headers(dns_slabheader_t *header, dns_rdatatype_t type, return done; } +static void +find_headers(qpcnode_t *node, qpc_search_t *search, dns_rdatatype_t type, + dns_slabheader_t **found, dns_slabheader_t **foundsig) { + DNS_SLABTOP_FOREACH(top, node->data) { + dns_slabheader_t *header = first_header(top); + if (check_header(header, search)) { + continue; + } + if (both_headers(header, type, found, foundsig)) { + break; + } + } +} + static isc_result_t check_zonecut(qpcnode_t *node, void *arg DNS__DB_FLARG) { qpc_search_t *search = arg; @@ -1304,18 +1318,7 @@ check_zonecut(qpcnode_t *node, void *arg DNS__DB_FLARG) { /* * Look for a DNAME or RRSIG DNAME rdataset. */ - DNS_SLABTOP_FOREACH(top, node->data) { - dns_slabheader_t *header = first_header(top); - if (check_header(header, search)) { - continue; - } - - if (both_headers(header, dns_rdatatype_dname, &found, - &foundsig)) - { - break; - } - } + find_headers(node, search, dns_rdatatype_dname, &found, &foundsig); if (found != NULL && (!DNS_TRUST_PENDING(atomic_load(&found->trust)) || (search->options & DNS_DBFIND_PENDINGOK) != 0)) @@ -1367,18 +1370,7 @@ find_deepest_zonecut(qpc_search_t *search, qpcnode_t *node, /* * Look for NS and RRSIG NS rdatasets. */ - DNS_SLABTOP_FOREACH(top, node->data) { - dns_slabheader_t *header = first_header(top); - if (check_header(header, search)) { - continue; - } - - if (both_headers(header, dns_rdatatype_ns, &found, - &foundsig)) - { - break; - } - } + find_headers(node, search, dns_rdatatype_ns, &found, &foundsig); if (found != NULL) { /* @@ -1471,17 +1463,8 @@ find_coveringnsec(qpc_search_t *search, const dns_name_t *name, nlock = &search->qpdb->buckets[node->locknum].lock; NODE_RDLOCK(nlock, &nlocktype); - DNS_SLABTOP_FOREACH(top, node->data) { - dns_slabheader_t *header = first_header(top); - if (check_header(header, search)) { - continue; - } - if (both_headers(header, dns_rdatatype_nsec, &found, &foundsig)) - { - break; - } - } + find_headers(node, search, dns_rdatatype_nsec, &found, &foundsig); if (found != NULL) { if (nodep != NULL) { @@ -1944,33 +1927,7 @@ seek_ns_headers(qpc_search_t *search, qpcnode_t *node, dns_dbnode_t **nodep, NODE_RDLOCK(nlock, &nlocktype); - DNS_SLABTOP_FOREACH(top, node->data) { - bool ns = top->typepair == DNS_TYPEPAIR(dns_rdatatype_ns) || - top->typepair == DNS_SIGTYPEPAIR(dns_rdatatype_ns); - dns_slabheader_t *header = first_header(top); - if (header == NULL) { - continue; - } - - if (check_stale_header(header, search)) { - if (ns) { - /* - * We found a cached NS, but was either - * ancient or it was stale and serve-stale - * is disabled, so this node can't be used - * as a zone cut we know about. Instead we - * bail out and call find_deepest_zonecut() - * below. - */ - break; - } - continue; - } - - if (both_headers(header, dns_rdatatype_ns, &found, &foundsig)) { - break; - } - } + find_headers(node, search, dns_rdatatype_ns, &found, &foundsig); if (found == NULL) { isc_result_t result; From 0b317abe4efbc4edfd4956741f57048d84cd0d6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Tue, 23 Sep 2025 09:54:38 +0200 Subject: [PATCH 4/6] Add a circular reference between slabtops for type and RRSIG(type) Previously, the slabtops for "type" and its signature was only loosely coupled. Add a .related member to the slabtop that allows us to optimize the lookups because now both slabtops are looked up at the same time. Co-authored-by: Matthijs Mekking --- lib/dns/include/dns/rdataslab.h | 2 + lib/dns/qpcache.c | 98 +++++++++++++++++---------------- 2 files changed, 53 insertions(+), 47 deletions(-) diff --git a/lib/dns/include/dns/rdataslab.h b/lib/dns/include/dns/rdataslab.h index 36f99c74f0..b160f3494d 100644 --- a/lib/dns/include/dns/rdataslab.h +++ b/lib/dns/include/dns/rdataslab.h @@ -79,6 +79,8 @@ struct dns_slabtop { dns_typepair_t typepair; + dns_slabtop_t *related; + /*% Used for SIEVE-LRU (cache) and changed_list (zone) */ ISC_LINK(struct dns_slabtop) link; /*% Used for SIEVE-LRU */ diff --git a/lib/dns/qpcache.c b/lib/dns/qpcache.c index 071fbf2de5..c3ee91f5a4 100644 --- a/lib/dns/qpcache.c +++ b/lib/dns/qpcache.c @@ -2561,12 +2561,11 @@ add(qpcache_t *qpdb, qpcnode_t *qpnode, dns_slabheader_t *newheader, unsigned int options, dns_rdataset_t *addedrdataset, isc_stdtime_t now, isc_rwlocktype_t nlocktype, isc_rwlocktype_t tlocktype DNS__DB_FLARG) { dns_slabtop_t *priotop = NULL, *expiretop = NULL; - dns_slabheader_t *oldheader = NULL, *oldsigheader = NULL; + dns_slabtop_t *oldtop = NULL, *related = NULL; dns_trust_t trust; uint32_t ntypes = 0; dns_rdatatype_t rdtype = DNS_TYPEPAIR_TYPE(newheader->typepair); dns_rdatatype_t covers = DNS_TYPEPAIR_COVERS(newheader->typepair); - dns_typepair_t sigpair = dns_typepair_none; REQUIRE(rdtype != dns_rdatatype_none); if (dns_rdatatype_issig(rdtype)) { @@ -2585,16 +2584,6 @@ add(qpcache_t *qpdb, qpcnode_t *qpnode, dns_slabheader_t *newheader, trust = newheader->trust; } - if (EXISTS(newheader) && NEGATIVE(newheader) && - !dns_rdatatype_issig(rdtype)) - { - /* - * Look for any RRSIGs of the given type in the main search - * loop so they can be also marked ancient later. - */ - sigpair = DNS_SIGTYPEPAIR(rdtype); - } - DNS_SLABTOP_FOREACH(top, qpnode->data) { dns_slabheader_t *header = first_header(top); if (header == NULL) { @@ -2659,17 +2648,27 @@ add(qpcache_t *qpdb, qpcnode_t *qpnode, dns_slabheader_t *newheader, } if (top->typepair == newheader->typepair) { - INSIST(oldheader == NULL); - oldheader = first_header(top); + INSIST(oldtop == NULL); + oldtop = top; } - if (sigpair != dns_rdatatype_none && top->typepair == sigpair) { - INSIST(oldsigheader == NULL); - oldsigheader = first_header(top); + if (rdtype == dns_rdatatype_rrsig) { + if (DNS_TYPEPAIR_TYPE(top->typepair) == covers) { + INSIST(related == NULL); + related = top; + } + } else { + if (top->typepair == DNS_SIGTYPEPAIR(rdtype)) { + INSIST(related == NULL); + related = top; + } } } - if (oldheader != NULL) { + if (oldtop != NULL) { + dns_slabheader_t *oldheader = first_header(oldtop); + INSIST(oldheader != NULL); + /* * Deleting an already non-existent rdataset has no effect. */ @@ -2696,7 +2695,7 @@ add(qpcache_t *qpdb, qpcnode_t *qpnode, dns_slabheader_t *newheader, (options & DNS_DBADD_EQUALOK) != 0 && dns_rdataslab_equalx( oldheader, newheader, qpdb->common.rdclass, - DNS_TYPEPAIR_TYPE(oldheader->typepair))) + DNS_TYPEPAIR_TYPE(oldtop->typepair))) { /* * Updated by caller to ISC_R_SUCCESS after @@ -2716,13 +2715,13 @@ add(qpcache_t *qpdb, qpcnode_t *qpnode, dns_slabheader_t *newheader, * further down. */ if (ACTIVE(oldheader, now) && - oldheader->typepair == DNS_TYPEPAIR(dns_rdatatype_ns) && + oldtop->typepair == DNS_TYPEPAIR(dns_rdatatype_ns) && EXISTS(oldheader) && EXISTS(newheader) && newheader->trust < oldtrust && oldheader->expire < newheader->expire && - dns_rdataslab_equalx( - oldheader, newheader, qpdb->common.rdclass, - DNS_TYPEPAIR_TYPE(oldheader->typepair))) + dns_rdataslab_equalx(oldheader, newheader, + qpdb->common.rdclass, + DNS_TYPEPAIR_TYPE(oldtop->typepair))) { if (oldheader->noqname == NULL && newheader->noqname != NULL) @@ -2757,7 +2756,7 @@ add(qpcache_t *qpdb, qpcnode_t *qpnode, dns_slabheader_t *newheader, * ensures the delegations that are withdrawn are honoured. */ if (ACTIVE(oldheader, now) && - oldheader->typepair == DNS_TYPEPAIR(dns_rdatatype_ns) && + oldtop->typepair == DNS_TYPEPAIR(dns_rdatatype_ns) && EXISTS(oldheader) && EXISTS(newheader) && newheader->trust > oldtrust) { @@ -2772,11 +2771,10 @@ add(qpcache_t *qpdb, qpcnode_t *qpnode, dns_slabheader_t *newheader, } if (ACTIVE(oldheader, now) && (options & DNS_DBADD_PREFETCH) == 0 && - (oldheader->typepair == DNS_TYPEPAIR(dns_rdatatype_a) || - oldheader->typepair == DNS_TYPEPAIR(dns_rdatatype_aaaa) || - oldheader->typepair == DNS_TYPEPAIR(dns_rdatatype_ds) || - oldheader->typepair == - DNS_SIGTYPEPAIR(dns_rdatatype_ds)) && + (oldtop->typepair == DNS_TYPEPAIR(dns_rdatatype_a) || + oldtop->typepair == DNS_TYPEPAIR(dns_rdatatype_aaaa) || + oldtop->typepair == DNS_TYPEPAIR(dns_rdatatype_ds) || + oldtop->typepair == DNS_SIGTYPEPAIR(dns_rdatatype_ds)) && EXISTS(oldheader) && EXISTS(newheader) && newheader->trust < oldtrust && oldheader->expire < newheader->expire && @@ -2820,8 +2818,15 @@ add(qpcache_t *qpdb, qpcnode_t *qpnode, dns_slabheader_t *newheader, &tlocktype DNS__DB_FLARG_PASS); mark_ancient(oldheader); - if (oldsigheader != NULL) { - mark_ancient(oldsigheader); + + if (EXISTS(newheader) && NEGATIVE(newheader) && + !dns_rdatatype_issig(rdtype)) + { + if (oldtop->related != NULL) { + dns_slabheader_t *oldsigheader = + first_header(oldtop->related); + mark_ancient(oldsigheader); + } } } else if (!EXISTS(newheader)) { /* @@ -2833,12 +2838,6 @@ add(qpcache_t *qpdb, qpcnode_t *qpnode, dns_slabheader_t *newheader, /* No rdatasets of the given type exist at the node. */ dns_slabtop_t *newtop = dns_slabtop_new( ((dns_db_t *)qpdb)->mctx, newheader->typepair); - newheader->top = newtop; - - cds_list_add(&newheader->headers_link, &newtop->headers); - - qpcache_miss(qpdb, newheader, &nlocktype, - &tlocktype DNS__DB_FLARG_PASS); if (prio_header(newtop)) { /* This is a priority type, prepend it */ @@ -2851,6 +2850,18 @@ add(qpcache_t *qpdb, qpcnode_t *qpnode, dns_slabheader_t *newheader, cds_list_add(&newtop->types_link, qpnode->data); } + if (related != NULL) { + INSIST(related->related == NULL); + related->related = newtop; + newtop->related = related; + } + + newheader->top = newtop; + cds_list_add(&newheader->headers_link, &newtop->headers); + + qpcache_miss(qpdb, newheader, &nlocktype, + &tlocktype DNS__DB_FLARG_PASS); + if (overmaxtype(qpdb, ntypes)) { if (expiretop == NULL) { expiretop = newtop; @@ -2864,17 +2875,10 @@ add(qpcache_t *qpdb, qpcnode_t *qpnode, dns_slabheader_t *newheader, expiretop = newtop; } - dns_slabheader_t *expireheader = - first_header(expiretop); - if (expireheader != NULL) { - mark_ancient(expireheader); + mark_ancient(first_header(expiretop)); + if (expiretop->related != NULL) { + mark_ancient(first_header(expiretop->related)); } - /* - * FIXME: In theory, we should mark the RRSIG - * and the header at the same time, but there is - * no direct link between those two headers, so - * we would have to check the whole list again. - */ } } From 0f13d7f2faffc14279cff7362cf545809e31fe4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Tue, 23 Sep 2025 09:54:45 +0200 Subject: [PATCH 5/6] Expire related headers at the same time Previously, the slabtops for "type" and its signature was only loosely coupled and the headers could expire at different time (both TTL and LRU based expiry). This commit expires the headers in both related headers. Co-authored-by: Matthijs Mekking --- lib/dns/qpcache.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/lib/dns/qpcache.c b/lib/dns/qpcache.c index c3ee91f5a4..62ad3e0775 100644 --- a/lib/dns/qpcache.c +++ b/lib/dns/qpcache.c @@ -502,12 +502,22 @@ expire_lru_headers(qpcache_t *qpdb, uint32_t idx, size_t requested, return; } + dns_slabtop_t *related = top->related; + ISC_SIEVE_UNLINK(qpdb->buckets[idx].sieve, top, link); dns_slabheader_t *header = first_header(top); - expired += expireheader(header, nlocktypep, tlocktypep, dns_expire_lru DNS__DB_FLARG_PASS); + + if (related != NULL) { + header = first_header(related); + + expired += + expireheader(header, nlocktypep, tlocktypep, + dns_expire_lru DNS__DB_FLARG_PASS); + } + } while (expired < requested); } @@ -598,6 +608,11 @@ clean_cache_node(qpcache_t *qpdb, qpcnode_t *node) { * If current slabtop is empty, we can clean it up. */ if (header == NULL) { + if (top->related != NULL) { + top->related->related = NULL; + top->related = NULL; + } + cds_list_del(&top->types_link); if (ISC_LINK_LINKED(top, link)) { @@ -927,6 +942,7 @@ static size_t expireheader(dns_slabheader_t *header, isc_rwlocktype_t *nlocktypep, isc_rwlocktype_t *tlocktypep, dns_expire_t reason DNS__DB_FLARG) { size_t expired = rdataset_size(header); + mark_ancient(header); if (isc_refcount_current(&HEADERNODE(header)->erefs) == 0) { @@ -2182,14 +2198,23 @@ qpcnode_expiredata(dns_dbnode_t *node, void *data) { qpcnode_t *qpnode = (qpcnode_t *)node; qpcache_t *qpdb = (qpcache_t *)qpnode->qpdb; + dns_slabtop_t *related = NULL; dns_slabheader_t *header = data; isc_rwlocktype_t nlocktype = isc_rwlocktype_none; isc_rwlocktype_t tlocktype = isc_rwlocktype_none; isc_rwlock_t *nlock = &qpdb->buckets[qpnode->locknum].lock; NODE_WRLOCK(nlock, &nlocktype); + related = header->top->related; + (void)expireheader(header, &nlocktype, &tlocktype, dns_expire_flush DNS__DB_FILELINE); + if (related != NULL) { + header = first_header(related); + (void)expireheader(header, &nlocktype, &tlocktype, + dns_expire_flush DNS__DB_FILELINE); + } + NODE_UNLOCK(nlock, &nlocktype); INSIST(tlocktype == isc_rwlocktype_none); } From 3e05958a42400d6f88674ae3ed4f232e61d2d69a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Tue, 23 Sep 2025 09:54:52 +0200 Subject: [PATCH 6/6] Refactor find headers to make use of related Change the code of finding headers to make use of the related circular reference. Co-authored-by: Matthijs Mekking --- lib/dns/qpcache.c | 295 ++++++++++++++++++++++++++++------------------ 1 file changed, 180 insertions(+), 115 deletions(-) diff --git a/lib/dns/qpcache.c b/lib/dns/qpcache.c index 62ad3e0775..4702ff05d8 100644 --- a/lib/dns/qpcache.c +++ b/lib/dns/qpcache.c @@ -1234,7 +1234,8 @@ check_stale_header(dns_slabheader_t *header, qpc_search_t *search) { static bool check_header(dns_slabheader_t *header, qpc_search_t *search) { - return header == NULL || check_stale_header(header, search); + return header == NULL || check_stale_header(header, search) || + !EXISTS(header) || ANCIENT(header); } /* @@ -1243,78 +1244,121 @@ check_header(dns_slabheader_t *header, qpc_search_t *search) { * negative header covering either 'negtype' or ANY. */ static bool -related_headers(dns_slabheader_t *header, dns_typepair_t typepair, - dns_typepair_t sigpair, dns_slabheader_t **foundp, - dns_slabheader_t **foundsigp, bool *matchp) { - if (!EXISTS(header) || ANCIENT(header)) { - return false; +related_headers(dns_slabheader_t *header, dns_slabheader_t *sigheader, + dns_typepair_t typepair, dns_slabheader_t **foundp, + dns_slabheader_t **foundsigp) { + if (header != NULL) { + REQUIRE(DNS_TYPEPAIR_TYPE(header->typepair) != + dns_rdatatype_rrsig); + REQUIRE(DNS_TYPEPAIR_COVERS(header->typepair) == + dns_rdatatype_none); + } + if (sigheader != NULL) { + REQUIRE(DNS_TYPEPAIR_TYPE(sigheader->typepair) == + dns_rdatatype_rrsig); + REQUIRE(DNS_TYPEPAIR_COVERS(sigheader->typepair) != + dns_rdatatype_none || + NEGATIVE(sigheader)); } - if (header->typepair == typepair && NEGATIVE(header)) { - /* - * In theory, the RRSIG(type) should not exist, but in reality, - * both the LRU and TTL based cleaning can delete one, but not - * the other. The INSIST below should be restored when we add - * a more strict synchronization between the type and its - * signature. - */ - /* INSIST(*foundsigp == NULL); */ - *foundp = header; - SET_IF_NOT_NULL(matchp, true); - return true; - } else if (header->typepair == typepair) { - *foundp = header; - SET_IF_NOT_NULL(matchp, true); - if (*foundsigp != NULL) { - return true; - } - } else if (header->typepair == sigpair) { - INSIST(!NEGATIVE(header)); - *foundsigp = header; - SET_IF_NOT_NULL(matchp, true); - if (*foundp != NULL) { - return true; - } - } else if (header->typepair == dns_typepair_any) { + /* + * Nothing exists if there's a NEGATIVE(dns_typepair_any). + */ + if (header != NULL && header->typepair == dns_typepair_any) { INSIST(NEGATIVE(header)); + INSIST(sigheader == NULL); *foundp = header; *foundsigp = NULL; - SET_IF_NOT_NULL(matchp, true); return true; } + /* + * Use the sigheader if we are looking for RRSIG. + */ + if (DNS_TYPEPAIR_TYPE(typepair) == dns_rdatatype_rrsig) { + if (sigheader == NULL) { + return false; + } + + REQUIRE(EXISTS(sigheader) && !ANCIENT(sigheader)); + if (sigheader->typepair == typepair) { + *foundp = sigheader; + *foundsigp = NULL; + return true; + } + return false; + } else { + if (header == NULL) { + return false; + } + + REQUIRE(EXISTS(header) && !ANCIENT(header)); + REQUIRE(!NEGATIVE(header) || sigheader == NULL); + + if (header->typepair == typepair) { + *foundp = header; + *foundsigp = sigheader; + return true; + } + } + return false; } -/* - * Return true if we've found headers for both 'type' and RRSIG('type'). - */ -static bool -both_headers(dns_slabheader_t *header, dns_rdatatype_t type, - dns_slabheader_t **foundp, dns_slabheader_t **foundsigp) { - dns_typepair_t typepair = DNS_TYPEPAIR_VALUE(type, 0); - dns_typepair_t sigpair = DNS_SIGTYPEPAIR(type); - - bool done = related_headers(header, typepair, sigpair, foundp, - foundsigp, NULL); - if (done && NEGATIVE(*foundp)) { - *foundp = NULL; - } - - return done; -} - static void find_headers(qpcnode_t *node, qpc_search_t *search, dns_rdatatype_t type, - dns_slabheader_t **found, dns_slabheader_t **foundsig) { + dns_slabheader_t **foundp, dns_slabheader_t **foundsigp) { DNS_SLABTOP_FOREACH(top, node->data) { - dns_slabheader_t *header = first_header(top); - if (check_header(header, search)) { + dns_slabheader_t *header = NULL, *sigheader = NULL; + dns_slabtop_t *related = top->related; + + if (top->typepair == dns_typepair_any) { + INSIST(top->related == NULL); + header = first_header(top); + INSIST(NEGATIVE(header)); + if (check_header(header, search)) { + /* + * NEGATIVE(ANY), but it is no longer valid. + */ + header = NULL; + continue; + } + *foundp = NULL; + *foundsigp = NULL; + return; + } + + if (top->typepair == DNS_TYPEPAIR(type)) { + header = first_header(top); + if (related != NULL) { + sigheader = first_header(related); + } + } else if (top->typepair == DNS_SIGTYPEPAIR(type)) { + sigheader = first_header(top); + if (related != NULL) { + header = first_header(related); + } + } else { + /* Not our type; continue with next slabtop */ continue; } - if (both_headers(header, type, found, foundsig)) { - break; + + if (check_header(header, search)) { + header = NULL; } + if (check_header(sigheader, search)) { + sigheader = NULL; + } + + /* + * This function only sets positive headers. + */ + if (header != NULL && !NEGATIVE(header)) { + *foundp = header; + *foundsigp = sigheader; + } + + return; } } @@ -1555,10 +1599,10 @@ qpcache_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version, isc_rwlock_t *nlock = NULL; isc_rwlocktype_t tlocktype = isc_rwlocktype_none; isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - dns_slabheader_t *found = NULL, *nsheader = NULL; - dns_slabheader_t *foundsig = NULL, *nssig = NULL, *cnamesig = NULL; + dns_slabheader_t *found = NULL, *foundsig = NULL; + dns_slabheader_t *nsheader = NULL, *nssig = NULL; dns_slabheader_t *nsecheader = NULL, *nsecsig = NULL; - dns_typepair_t typepair, sigpair; + dns_typepair_t typepair; if (type == dns_rdatatype_none) { /* We can't search negative cache directly */ @@ -1670,20 +1714,35 @@ qpcache_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version, found = NULL; foundsig = NULL; typepair = DNS_TYPEPAIR(type); - sigpair = (type != dns_rdatatype_rrsig) ? DNS_SIGTYPEPAIR(type) : 0; nsheader = NULL; nsecheader = NULL; nssig = NULL; nsecsig = NULL; - cnamesig = NULL; empty_node = true; + DNS_SLABTOP_FOREACH(top, node->data) { - dns_slabheader_t *header = first_header(top); - if (check_header(header, &search)) { - continue; + dns_slabheader_t *header = NULL, *sigheader = NULL; + if (DNS_TYPEPAIR_TYPE(top->typepair) == dns_rdatatype_rrsig) { + sigheader = first_header(top); + if (top->related != NULL) { + header = first_header(top->related); + } + } else { + header = first_header(top); + if (top->related != NULL) { + sigheader = first_header(top->related); + } } - if (!EXISTS(header) || ANCIENT(header)) { + if (check_header(header, &search)) { + header = NULL; + } + + if (check_header(sigheader, &search)) { + sigheader = NULL; + } + + if (header == NULL && sigheader == NULL) { continue; } @@ -1693,20 +1752,22 @@ qpcache_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version, */ empty_node = false; - if (header->noqname != NULL && + if (header != NULL && header->noqname != NULL && atomic_load(&header->trust) == dns_trust_secure) { found_noqname = true; } - if (!NEGATIVE(header)) { + if (header != NULL && !NEGATIVE(header)) { all_negative = false; } - bool match = false; - if (related_headers(header, typepair, sigpair, &found, - &foundsig, &match) && - !missing_answer(found, options)) + if (sigheader != NULL && !NEGATIVE(sigheader)) { + all_negative = false; + } + + if (related_headers(header, sigheader, typepair, &found, + &foundsig)) { /* * We can't exit early until we have an answer with @@ -1714,63 +1775,42 @@ qpcache_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version, * for details - because we might need NS or NSEC * records. */ + if (missing_answer(found, options)) { + continue; + } + + /* We found something, continue with next header */ break; } - if (match) { - /* We found something, continue with next header */ - continue; - } - - if (NEGATIVE(header)) { + if (header == NULL || NEGATIVE(header)) { /* - * FIXME: As of now, we are not interested in - * the negative headers. This could be - * improved and we can bail out early if we've - * seen all the types below (positive or - * negative), but the code is not yet ready - * for this. + * We are not interested in the negative headers for the + * auxiliary types, only for the main type we are + * looking for. */ continue; } switch (top->typepair) { case dns_rdatatype_cname: - if (!cname_ok) { - break; - } - - found = header; - if (cnamesig != NULL) { - /* We already have CNAME signature */ - foundsig = cnamesig; - } else { - /* Look for CNAME signature instead */ - sigpair = DNS_SIGTYPEPAIR(dns_rdatatype_cname); - foundsig = NULL; - } - break; case DNS_SIGTYPEPAIR(dns_rdatatype_cname): - if (!cname_ok) { - break; + if (cname_ok) { + found = header; + foundsig = sigheader; } + break; - cnamesig = header; - break; case dns_rdatatype_ns: - /* Remember the NS rdataset */ - nsheader = header; - break; case DNS_SIGTYPEPAIR(dns_rdatatype_ns): - /* ...and its signature */ - nssig = header; + nsheader = header; + nssig = sigheader; break; case dns_rdatatype_nsec: - nsecheader = header; - break; case DNS_SIGTYPEPAIR(dns_rdatatype_nsec): - nsecsig = header; + nsecheader = header; + nsecsig = sigheader; break; default: @@ -1780,6 +1820,10 @@ qpcache_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version, break; } } + + if (!missing_answer(found, options)) { + break; + } } if (empty_node) { @@ -2091,16 +2135,37 @@ qpcache_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, : dns_typepair_none; DNS_SLABTOP_FOREACH(top, qpnode->data) { - dns_slabheader_t *header = first_header(top); - if (check_header(header, &search)) { + dns_slabheader_t *header = NULL, *sigheader = NULL; + + if (top->typepair != typepair && top->typepair != sigpair && + top->typepair != dns_typepair_any) + { continue; } - if (related_headers(header, typepair, sigpair, &found, - &foundsig, NULL)) - { - break; + if (DNS_TYPEPAIR_TYPE(top->typepair) == dns_rdatatype_rrsig) { + sigheader = first_header(top); + if (top->related != NULL) { + header = first_header(top->related); + } + } else { + header = first_header(top); + if (top->related != NULL) { + sigheader = first_header(top->related); + } } + + if (check_header(header, &search)) { + header = NULL; + } + + if (check_header(sigheader, &search)) { + sigheader = NULL; + } + + (void)related_headers(header, sigheader, typepair, &found, + &foundsig); + break; } if (found != NULL) {