From 47239b45e688d98f1363fcacdc49ff60be05e0d3 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Wed, 16 Nov 2022 11:40:33 +1100 Subject: [PATCH] Add dns_db_allrdatasets options 'DNS_DB_STALEOK' returns stale rdatasets as well as current rdatasets. 'DNS_DB_EXPIREDOK' returns expired rdatasets as well as current rdatasets. This option is currently only set when DNS_DB_STALEOK is also set. (cherry picked from commit 85048ddeeeba8414ababfe73ba2651183b056180) --- lib/dns/cache.c | 4 +- lib/dns/include/dns/db.h | 6 +++ lib/dns/masterdump.c | 16 ++++-- lib/dns/rbtdb.c | 110 ++++++++++++++++++++++++++++----------- 4 files changed, 100 insertions(+), 36 deletions(-) diff --git a/lib/dns/cache.c b/lib/dns/cache.c index 09012d1238..7d64df6722 100644 --- a/lib/dns/cache.c +++ b/lib/dns/cache.c @@ -1014,8 +1014,8 @@ clearnode(dns_db_t *db, dns_dbnode_t *node) { isc_result_t result; dns_rdatasetiter_t *iter = NULL; - result = dns_db_allrdatasets(db, node, NULL, 0, (isc_stdtime_t)0, - &iter); + result = dns_db_allrdatasets(db, node, NULL, DNS_DB_STALEOK, + (isc_stdtime_t)0, &iter); if (result != ISC_R_SUCCESS) { return (result); } diff --git a/lib/dns/include/dns/db.h b/lib/dns/include/dns/db.h index e01ab520f7..9b53f0435f 100644 --- a/lib/dns/include/dns/db.h +++ b/lib/dns/include/dns/db.h @@ -302,6 +302,9 @@ struct dns_dbonupdatelistener { #define DNS_DB_NONSEC3 0x4 /*@}*/ +#define DNS_DB_STALEOK 0x01 +#define DNS_DB_EXPIREDOK 0x02 + /***** ***** Methods *****/ @@ -1168,6 +1171,9 @@ dns_db_allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, * * \li 'options' controls which rdatasets are selected when interating over * the node. + * 'DNS_DB_STALEOK' return stale rdatasets as well as current rdatasets. + * 'DNS_DB_EXPIREDOK' return expired rdatasets as well as current + * rdatasets. * * \li The 'now' field is ignored if 'db' is a zone database. If 'db' is a * cache database, an rdataset will not be found unless it expires after diff --git a/lib/dns/masterdump.c b/lib/dns/masterdump.c index 7e43016334..4946d0d237 100644 --- a/lib/dns/masterdump.c +++ b/lib/dns/masterdump.c @@ -1714,6 +1714,11 @@ dumptostream(dns_dumpctx_t *dctx) { char *bufmem; dns_name_t *name; dns_fixedname_t fixname; + unsigned int options = DNS_DB_STALEOK; + + if ((dctx->tctx.style.flags & DNS_STYLEFLAG_EXPIRED) != 0) { + options |= DNS_DB_EXPIREDOK; + } bufmem = isc_mem_get(dctx->mctx, initial_buffer_length); @@ -1752,8 +1757,8 @@ dumptostream(dns_dumpctx_t *dctx) { result = dns_dbiterator_pause(dctx->dbiter); RUNTIME_CHECK(result == ISC_R_SUCCESS); - result = dns_db_allrdatasets(dctx->db, node, dctx->version, 0, - dctx->now, &rdsiter); + result = dns_db_allrdatasets(dctx->db, node, dctx->version, + options, dctx->now, &rdsiter); if (result != ISC_R_SUCCESS) { dns_db_detachnode(dctx->db, &node); goto cleanup; @@ -1974,6 +1979,11 @@ dns_master_dumpnodetostream(isc_mem_t *mctx, dns_db_t *db, isc_stdtime_t now; dns_totext_ctx_t ctx; dns_rdatasetiter_t *rdsiter = NULL; + unsigned int options = DNS_DB_STALEOK; + + if ((style->flags & DNS_STYLEFLAG_EXPIRED) != 0) { + options |= DNS_DB_EXPIREDOK; + } result = totext_ctx_init(style, NULL, &ctx); if (result != ISC_R_SUCCESS) { @@ -1987,7 +1997,7 @@ dns_master_dumpnodetostream(isc_mem_t *mctx, dns_db_t *db, isc_buffer_init(&buffer, bufmem, initial_buffer_length); - result = dns_db_allrdatasets(db, node, version, 0, now, &rdsiter); + result = dns_db_allrdatasets(db, node, version, options, now, &rdsiter); if (result != ISC_R_SUCCESS) { goto failure; } diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index 5be9648910..fbf1fd971b 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -325,6 +325,12 @@ hash_32(uint32_t val, unsigned int bits) { return (val * GOLDEN_RATIO_32 >> (32 - bits)); } +#define EXPIREDOK(rbtiterator) \ + (((rbtiterator)->common.options & DNS_DB_EXPIREDOK) != 0) + +#define STALEOK(rbtiterator) \ + (((rbtiterator)->common.options & DNS_DB_STALEOK) != 0) + /*% * Number of buckets for cache DB entries (locks, LRU lists, TTL heaps). * There is a tradeoff issue about configuring this value: if this is too @@ -8748,7 +8754,17 @@ rdatasetiter_first(dns_rdatasetiter_t *iterator) { for (header = rbtnode->data; header != NULL; header = top_next) { top_next = header->next; do { - if (header->serial <= serial && !IGNORE(header)) { + dns_ttl_t stale_ttl = header->rdh_ttl; + if (STALEOK(rbtiterator)) { + stale_ttl += STALE_TTL(header, rbtdb); + } + if (EXPIREDOK(rbtiterator)) { + if (!NONEXISTENT(header)) { + break; + } + header = header->down; + } else if (header->serial <= serial && !IGNORE(header)) + { /* * Is this a "this rdataset doesn't exist" * record? Or is it too old in the cache? @@ -8759,8 +8775,7 @@ rdatasetiter_first(dns_rdatasetiter_t *iterator) { * queries for 0 TTL rdatasets to work. */ if (NONEXISTENT(header) || - (now != 0 && now > header->rdh_ttl)) - { + (now != 0 && now > stale_ttl)) { header = NULL; } break; @@ -8796,6 +8811,7 @@ rdatasetiter_next(dns_rdatasetiter_t *iterator) { isc_stdtime_t now; rbtdb_rdatatype_t type, negtype; dns_rdatatype_t rdtype, covers; + bool expiredok = EXPIREDOK(rbtiterator); header = rbtiterator->current; if (header == NULL) { @@ -8821,37 +8837,69 @@ rdatasetiter_next(dns_rdatasetiter_t *iterator) { } else { negtype = RBTDB_RDATATYPE_VALUE(0, rdtype); } - for (header = header->next; header != NULL; header = top_next) { - top_next = header->next; + + /* + * Find the start of the header chain for the next type + * by walking back up the list. + */ + top_next = header->next; + while (top_next != NULL && + (top_next->type == type || top_next->type == negtype)) + { + top_next = top_next->next; + } + if (expiredok) { /* - * If not walking back up the down list. + * Keep walking down the list if possible or + * start the next type. */ - if (header->type != type && header->type != negtype) { - do { - if (header->serial <= serial && !IGNORE(header)) - { - /* - * Is this a "this rdataset doesn't - * exist" record? - * - * Note: unlike everywhere else, we - * check for now > header->ttl instead - * of ">=". This allows ANY and RRSIG - * queries for 0 TTL rdatasets to work. - */ - if (NONEXISTENT(header) || - (now != 0 && now > header->rdh_ttl)) - { - header = NULL; - } - break; - } else { - header = header->down; - } - } while (header != NULL); - if (header != NULL) { - break; + header = header->down != NULL ? header->down : top_next; + } else { + header = top_next; + } + for (; header != NULL; header = top_next) { + top_next = header->next; + do { + dns_ttl_t stale_ttl = header->rdh_ttl; + if (STALEOK(rbtiterator)) { + stale_ttl += STALE_TTL(header, rbtdb); } + if (expiredok) { + if (!NONEXISTENT(header)) { + break; + } + header = header->down; + } else if (header->serial <= serial && !IGNORE(header)) + { + /* + * Is this a "this rdataset doesn't + * exist" record? + * + * Note: unlike everywhere else, we + * check for now > header->ttl instead + * of ">=". This allows ANY and RRSIG + * queries for 0 TTL rdatasets to work. + */ + if (NONEXISTENT(header) || + (now != 0 && now > stale_ttl)) { + header = NULL; + } + break; + } else { + header = header->down; + } + } while (header != NULL); + if (header != NULL) { + break; + } + /* + * Find the start of the header chain for the next type + * by walking back up the list. + */ + while (top_next != NULL && + (top_next->type == type || top_next->type == negtype)) + { + top_next = top_next->next; } }