xref: /netbsd-src/external/mpl/bind/dist/lib/dns/rbt-cachedb.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1*bcda20f6Schristos /*	$NetBSD: rbt-cachedb.c,v 1.2 2025/01/26 16:25:24 christos Exp $	*/
29689912eSchristos 
39689912eSchristos /*
49689912eSchristos  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
59689912eSchristos  *
69689912eSchristos  * SPDX-License-Identifier: MPL-2.0
79689912eSchristos  *
89689912eSchristos  * This Source Code Form is subject to the terms of the Mozilla Public
99689912eSchristos  * License, v. 2.0. If a copy of the MPL was not distributed with this
109689912eSchristos  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
119689912eSchristos  *
129689912eSchristos  * See the COPYRIGHT file distributed with this work for additional
139689912eSchristos  * information regarding copyright ownership.
149689912eSchristos  */
159689912eSchristos 
169689912eSchristos /*! \file */
179689912eSchristos 
189689912eSchristos #include <inttypes.h>
199689912eSchristos #include <stdbool.h>
209689912eSchristos #include <sys/mman.h>
219689912eSchristos 
229689912eSchristos #include <isc/ascii.h>
239689912eSchristos #include <isc/async.h>
249689912eSchristos #include <isc/atomic.h>
259689912eSchristos #include <isc/crc64.h>
269689912eSchristos #include <isc/file.h>
279689912eSchristos #include <isc/hash.h>
289689912eSchristos #include <isc/hashmap.h>
299689912eSchristos #include <isc/heap.h>
309689912eSchristos #include <isc/hex.h>
319689912eSchristos #include <isc/loop.h>
329689912eSchristos #include <isc/mem.h>
339689912eSchristos #include <isc/mutex.h>
349689912eSchristos #include <isc/once.h>
359689912eSchristos #include <isc/random.h>
369689912eSchristos #include <isc/refcount.h>
379689912eSchristos #include <isc/result.h>
389689912eSchristos #include <isc/rwlock.h>
399689912eSchristos #include <isc/serial.h>
409689912eSchristos #include <isc/stdio.h>
419689912eSchristos #include <isc/string.h>
429689912eSchristos #include <isc/time.h>
439689912eSchristos #include <isc/urcu.h>
449689912eSchristos #include <isc/util.h>
459689912eSchristos 
469689912eSchristos #include <dns/callbacks.h>
479689912eSchristos #include <dns/db.h>
489689912eSchristos #include <dns/dbiterator.h>
499689912eSchristos #include <dns/fixedname.h>
509689912eSchristos #include <dns/log.h>
519689912eSchristos #include <dns/masterdump.h>
529689912eSchristos #include <dns/nsec.h>
539689912eSchristos #include <dns/nsec3.h>
549689912eSchristos #include <dns/rbt.h>
559689912eSchristos #include <dns/rdata.h>
569689912eSchristos #include <dns/rdataset.h>
579689912eSchristos #include <dns/rdatasetiter.h>
589689912eSchristos #include <dns/rdataslab.h>
599689912eSchristos #include <dns/rdatastruct.h>
609689912eSchristos #include <dns/stats.h>
619689912eSchristos #include <dns/time.h>
629689912eSchristos #include <dns/view.h>
639689912eSchristos #include <dns/zone.h>
649689912eSchristos #include <dns/zonekey.h>
659689912eSchristos 
669689912eSchristos #include "db_p.h"
679689912eSchristos #include "rbtdb_p.h"
689689912eSchristos 
699689912eSchristos #define CHECK(op)                            \
709689912eSchristos 	do {                                 \
719689912eSchristos 		result = (op);               \
729689912eSchristos 		if (result != ISC_R_SUCCESS) \
739689912eSchristos 			goto failure;        \
749689912eSchristos 	} while (0)
759689912eSchristos 
769689912eSchristos /*%
779689912eSchristos  * Whether to rate-limit updating the LRU to avoid possible thread contention.
789689912eSchristos  * Updating LRU requires write locking, so we don't do it every time the
799689912eSchristos  * record is touched - only after some time passes.
809689912eSchristos  */
819689912eSchristos #ifndef DNS_RBTDB_LIMITLRUUPDATE
829689912eSchristos #define DNS_RBTDB_LIMITLRUUPDATE 1
839689912eSchristos #endif
849689912eSchristos 
859689912eSchristos /*% Time after which we update LRU for glue records, 5 minutes */
869689912eSchristos #define DNS_RBTDB_LRUUPDATE_GLUE 300
879689912eSchristos /*% Time after which we update LRU for all other records, 10 minutes */
889689912eSchristos #define DNS_RBTDB_LRUUPDATE_REGULAR 600
899689912eSchristos 
909689912eSchristos #define EXISTS(header)                                 \
919689912eSchristos 	((atomic_load_acquire(&(header)->attributes) & \
929689912eSchristos 	  DNS_SLABHEADERATTR_NONEXISTENT) == 0)
939689912eSchristos #define NONEXISTENT(header)                            \
949689912eSchristos 	((atomic_load_acquire(&(header)->attributes) & \
959689912eSchristos 	  DNS_SLABHEADERATTR_NONEXISTENT) != 0)
969689912eSchristos #define NXDOMAIN(header)                               \
979689912eSchristos 	((atomic_load_acquire(&(header)->attributes) & \
989689912eSchristos 	  DNS_SLABHEADERATTR_NXDOMAIN) != 0)
999689912eSchristos #define STALE(header)                                  \
1009689912eSchristos 	((atomic_load_acquire(&(header)->attributes) & \
1019689912eSchristos 	  DNS_SLABHEADERATTR_STALE) != 0)
1029689912eSchristos #define NEGATIVE(header)                               \
1039689912eSchristos 	((atomic_load_acquire(&(header)->attributes) & \
1049689912eSchristos 	  DNS_SLABHEADERATTR_NEGATIVE) != 0)
1059689912eSchristos #define ZEROTTL(header)                                \
1069689912eSchristos 	((atomic_load_acquire(&(header)->attributes) & \
1079689912eSchristos 	  DNS_SLABHEADERATTR_ZEROTTL) != 0)
1089689912eSchristos #define ANCIENT(header)                                \
1099689912eSchristos 	((atomic_load_acquire(&(header)->attributes) & \
1109689912eSchristos 	  DNS_SLABHEADERATTR_ANCIENT) != 0)
1119689912eSchristos #define STATCOUNT(header)                              \
1129689912eSchristos 	((atomic_load_acquire(&(header)->attributes) & \
1139689912eSchristos 	  DNS_SLABHEADERATTR_STATCOUNT) != 0)
1149689912eSchristos 
1159689912eSchristos #define STALE_TTL(header, rbtdb) \
1169689912eSchristos 	(NXDOMAIN(header) ? 0 : rbtdb->common.serve_stale_ttl)
1179689912eSchristos 
1189689912eSchristos #define ACTIVE(header, now) \
1199689912eSchristos 	(((header)->ttl > (now)) || ((header)->ttl == (now) && ZEROTTL(header)))
1209689912eSchristos 
1219689912eSchristos #define KEEPSTALE(rbtdb) ((rbtdb)->common.serve_stale_ttl > 0)
1229689912eSchristos 
1239689912eSchristos /*%
1249689912eSchristos  * Routines for LRU-based cache management.
1259689912eSchristos  */
1269689912eSchristos 
1279689912eSchristos /*%
1289689912eSchristos  * See if a given cache entry that is being reused needs to be updated
1299689912eSchristos  * in the LRU-list.  From the LRU management point of view, this function is
1309689912eSchristos  * expected to return true for almost all cases.  When used with threads,
1319689912eSchristos  * however, this may cause a non-negligible performance penalty because a
1329689912eSchristos  * writer lock will have to be acquired before updating the list.
1339689912eSchristos  * If DNS_RBTDB_LIMITLRUUPDATE is defined to be non 0 at compilation time, this
1349689912eSchristos  * function returns true if the entry has not been updated for some period of
1359689912eSchristos  * time.  We differentiate the NS or glue address case and the others since
1369689912eSchristos  * experiments have shown that the former tends to be accessed relatively
1379689912eSchristos  * infrequently and the cost of cache miss is higher (e.g., a missing NS records
1389689912eSchristos  * may cause external queries at a higher level zone, involving more
1399689912eSchristos  * transactions).
1409689912eSchristos  *
1419689912eSchristos  * Caller must hold the node (read or write) lock.
1429689912eSchristos  */
1439689912eSchristos static bool
1449689912eSchristos need_headerupdate(dns_slabheader_t *header, isc_stdtime_t now) {
1459689912eSchristos 	if (DNS_SLABHEADER_GETATTR(header, (DNS_SLABHEADERATTR_NONEXISTENT |
1469689912eSchristos 					    DNS_SLABHEADERATTR_ANCIENT |
1479689912eSchristos 					    DNS_SLABHEADERATTR_ZEROTTL)) != 0)
1489689912eSchristos 	{
1499689912eSchristos 		return false;
1509689912eSchristos 	}
1519689912eSchristos 
1529689912eSchristos #if DNS_RBTDB_LIMITLRUUPDATE
1539689912eSchristos 	if (header->type == dns_rdatatype_ns ||
1549689912eSchristos 	    (header->trust == dns_trust_glue &&
1559689912eSchristos 	     (header->type == dns_rdatatype_a ||
1569689912eSchristos 	      header->type == dns_rdatatype_aaaa)))
1579689912eSchristos 	{
1589689912eSchristos 		/*
1599689912eSchristos 		 * Glue records are updated if at least DNS_RBTDB_LRUUPDATE_GLUE
1609689912eSchristos 		 * seconds have passed since the previous update time.
1619689912eSchristos 		 */
1629689912eSchristos 		return header->last_used + DNS_RBTDB_LRUUPDATE_GLUE <= now;
1639689912eSchristos 	}
1649689912eSchristos 
1659689912eSchristos 	/*
1669689912eSchristos 	 * Other records are updated if DNS_RBTDB_LRUUPDATE_REGULAR seconds
1679689912eSchristos 	 * have passed.
1689689912eSchristos 	 */
1699689912eSchristos 	return header->last_used + DNS_RBTDB_LRUUPDATE_REGULAR <= now;
1709689912eSchristos #else
1719689912eSchristos 	UNUSED(now);
1729689912eSchristos 
1739689912eSchristos 	return true;
1749689912eSchristos #endif /* if DNS_RBTDB_LIMITLRUUPDATE */
1759689912eSchristos }
1769689912eSchristos 
1779689912eSchristos /*%
1789689912eSchristos  * Update the timestamp of a given cache entry and move it to the head
1799689912eSchristos  * of the corresponding LRU list.
1809689912eSchristos  *
1819689912eSchristos  * Caller must hold the node (write) lock.
1829689912eSchristos  *
1839689912eSchristos  * Note that the we do NOT touch the heap here, as the TTL has not changed.
1849689912eSchristos  */
1859689912eSchristos static void
1869689912eSchristos update_header(dns_rbtdb_t *rbtdb, dns_slabheader_t *header, isc_stdtime_t now) {
1879689912eSchristos 	INSIST(IS_CACHE(rbtdb));
1889689912eSchristos 
1899689912eSchristos 	/* To be checked: can we really assume this? XXXMLG */
1909689912eSchristos 	INSIST(ISC_LINK_LINKED(header, link));
1919689912eSchristos 
1929689912eSchristos 	ISC_LIST_UNLINK(rbtdb->lru[RBTDB_HEADERNODE(header)->locknum], header,
1939689912eSchristos 			link);
1949689912eSchristos 	header->last_used = now;
1959689912eSchristos 	ISC_LIST_PREPEND(rbtdb->lru[RBTDB_HEADERNODE(header)->locknum], header,
1969689912eSchristos 			 link);
1979689912eSchristos }
1989689912eSchristos 
1999689912eSchristos /*
2009689912eSchristos  * Locking
2019689912eSchristos  *
2029689912eSchristos  * If a routine is going to lock more than one lock in this module, then
2039689912eSchristos  * the locking must be done in the following order:
2049689912eSchristos  *
2059689912eSchristos  *      Tree Lock
2069689912eSchristos  *
2079689912eSchristos  *      Node Lock       (Only one from the set may be locked at one time by
2089689912eSchristos  *                       any caller)
2099689912eSchristos  *
2109689912eSchristos  *      Database Lock
2119689912eSchristos  *
2129689912eSchristos  * Failure to follow this hierarchy can result in deadlock.
2139689912eSchristos  */
2149689912eSchristos 
2159689912eSchristos /*
2169689912eSchristos  * Deleting Nodes
2179689912eSchristos  *
2189689912eSchristos  * For zone databases the node for the origin of the zone MUST NOT be deleted.
2199689912eSchristos  */
2209689912eSchristos 
2219689912eSchristos /*
2229689912eSchristos  * DB Routines
2239689912eSchristos  */
2249689912eSchristos 
2259689912eSchristos static void
2269689912eSchristos update_cachestats(dns_rbtdb_t *rbtdb, isc_result_t result) {
2279689912eSchristos 	INSIST(IS_CACHE(rbtdb));
2289689912eSchristos 
2299689912eSchristos 	if (rbtdb->cachestats == NULL) {
2309689912eSchristos 		return;
2319689912eSchristos 	}
2329689912eSchristos 
2339689912eSchristos 	switch (result) {
2349689912eSchristos 	case DNS_R_COVERINGNSEC:
2359689912eSchristos 		isc_stats_increment(rbtdb->cachestats,
2369689912eSchristos 				    dns_cachestatscounter_coveringnsec);
2379689912eSchristos 		FALLTHROUGH;
2389689912eSchristos 	case ISC_R_SUCCESS:
2399689912eSchristos 	case DNS_R_CNAME:
2409689912eSchristos 	case DNS_R_DNAME:
2419689912eSchristos 	case DNS_R_DELEGATION:
2429689912eSchristos 	case DNS_R_NCACHENXDOMAIN:
2439689912eSchristos 	case DNS_R_NCACHENXRRSET:
2449689912eSchristos 		isc_stats_increment(rbtdb->cachestats,
2459689912eSchristos 				    dns_cachestatscounter_hits);
2469689912eSchristos 		break;
2479689912eSchristos 	default:
2489689912eSchristos 		isc_stats_increment(rbtdb->cachestats,
2499689912eSchristos 				    dns_cachestatscounter_misses);
2509689912eSchristos 	}
2519689912eSchristos }
2529689912eSchristos 
2539689912eSchristos static void
2549689912eSchristos clean_stale_headers(dns_slabheader_t *top) {
2559689912eSchristos 	dns_slabheader_t *d = NULL, *down_next = NULL;
2569689912eSchristos 
2579689912eSchristos 	for (d = top->down; d != NULL; d = down_next) {
2589689912eSchristos 		down_next = d->down;
2599689912eSchristos 		dns_slabheader_destroy(&d);
2609689912eSchristos 	}
2619689912eSchristos 	top->down = NULL;
2629689912eSchristos }
2639689912eSchristos 
2649689912eSchristos static isc_result_t
2659689912eSchristos setup_delegation(rbtdb_search_t *search, dns_dbnode_t **nodep,
2669689912eSchristos 		 dns_name_t *foundname, dns_rdataset_t *rdataset,
2679689912eSchristos 		 dns_rdataset_t *sigrdataset DNS__DB_FLARG) {
2689689912eSchristos 	dns_name_t *zcname = NULL;
2699689912eSchristos 	dns_typepair_t type;
2709689912eSchristos 	dns_rbtnode_t *node = NULL;
2719689912eSchristos 
2729689912eSchristos 	REQUIRE(search != NULL);
2739689912eSchristos 	REQUIRE(search->zonecut != NULL);
2749689912eSchristos 	REQUIRE(search->zonecut_header != NULL);
2759689912eSchristos 
2769689912eSchristos 	/*
2779689912eSchristos 	 * The caller MUST NOT be holding any node locks.
2789689912eSchristos 	 */
2799689912eSchristos 
2809689912eSchristos 	node = search->zonecut;
2819689912eSchristos 	type = search->zonecut_header->type;
2829689912eSchristos 
2839689912eSchristos 	/*
2849689912eSchristos 	 * If we have to set foundname, we do it before anything else.
2859689912eSchristos 	 * If we were to set foundname after we had set nodep or bound the
2869689912eSchristos 	 * rdataset, then we'd have to undo that work if dns_name_copy()
2879689912eSchristos 	 * failed.  By setting foundname first, there's nothing to undo if
2889689912eSchristos 	 * we have trouble.
2899689912eSchristos 	 */
2909689912eSchristos 	if (foundname != NULL && search->copy_name) {
2919689912eSchristos 		zcname = dns_fixedname_name(&search->zonecut_name);
2929689912eSchristos 		dns_name_copy(zcname, foundname);
2939689912eSchristos 	}
2949689912eSchristos 	if (nodep != NULL) {
2959689912eSchristos 		/*
2969689912eSchristos 		 * Note that we don't have to increment the node's reference
2979689912eSchristos 		 * count here because we're going to use the reference we
2989689912eSchristos 		 * already have in the search block.
2999689912eSchristos 		 */
3009689912eSchristos 		*nodep = node;
3019689912eSchristos 		search->need_cleanup = false;
3029689912eSchristos 	}
3039689912eSchristos 	if (rdataset != NULL) {
3049689912eSchristos 		isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
3059689912eSchristos 		NODE_RDLOCK(&(search->rbtdb->node_locks[node->locknum].lock),
3069689912eSchristos 			    &nlocktype);
3079689912eSchristos 		dns__rbtdb_bindrdataset(search->rbtdb, node,
3089689912eSchristos 					search->zonecut_header, search->now,
3099689912eSchristos 					isc_rwlocktype_read,
3109689912eSchristos 					rdataset DNS__DB_FLARG_PASS);
3119689912eSchristos 		if (sigrdataset != NULL && search->zonecut_sigheader != NULL) {
3129689912eSchristos 			dns__rbtdb_bindrdataset(
3139689912eSchristos 				search->rbtdb, node, search->zonecut_sigheader,
3149689912eSchristos 				search->now, isc_rwlocktype_read,
3159689912eSchristos 				sigrdataset DNS__DB_FLARG_PASS);
3169689912eSchristos 		}
3179689912eSchristos 		NODE_UNLOCK(&(search->rbtdb->node_locks[node->locknum].lock),
3189689912eSchristos 			    &nlocktype);
3199689912eSchristos 	}
3209689912eSchristos 
3219689912eSchristos 	if (type == dns_rdatatype_dname) {
3229689912eSchristos 		return DNS_R_DNAME;
3239689912eSchristos 	}
3249689912eSchristos 	return DNS_R_DELEGATION;
3259689912eSchristos }
3269689912eSchristos 
3279689912eSchristos static bool
3289689912eSchristos check_stale_header(dns_rbtnode_t *node, dns_slabheader_t *header,
3299689912eSchristos 		   isc_rwlocktype_t *nlocktypep, isc_rwlock_t *lock,
3309689912eSchristos 		   rbtdb_search_t *search, dns_slabheader_t **header_prev) {
3319689912eSchristos 	if (!ACTIVE(header, search->now)) {
3329689912eSchristos 		dns_ttl_t stale = header->ttl +
3339689912eSchristos 				  STALE_TTL(header, search->rbtdb);
3349689912eSchristos 		/*
3359689912eSchristos 		 * If this data is in the stale window keep it and if
3369689912eSchristos 		 * DNS_DBFIND_STALEOK is not set we tell the caller to
3379689912eSchristos 		 * skip this record.  We skip the records with ZEROTTL
3389689912eSchristos 		 * (these records should not be cached anyway).
3399689912eSchristos 		 */
3409689912eSchristos 
3419689912eSchristos 		DNS_SLABHEADER_CLRATTR(header, DNS_SLABHEADERATTR_STALE_WINDOW);
3429689912eSchristos 		if (!ZEROTTL(header) && KEEPSTALE(search->rbtdb) &&
3439689912eSchristos 		    stale > search->now)
3449689912eSchristos 		{
3459689912eSchristos 			dns__rbtdb_mark(header, DNS_SLABHEADERATTR_STALE);
3469689912eSchristos 			*header_prev = header;
3479689912eSchristos 			/*
3489689912eSchristos 			 * If DNS_DBFIND_STALESTART is set then it means we
3499689912eSchristos 			 * failed to resolve the name during recursion, in
3509689912eSchristos 			 * this case we mark the time in which the refresh
3519689912eSchristos 			 * failed.
3529689912eSchristos 			 */
3539689912eSchristos 			if ((search->options & DNS_DBFIND_STALESTART) != 0) {
3549689912eSchristos 				atomic_store_release(
3559689912eSchristos 					&header->last_refresh_fail_ts,
3569689912eSchristos 					search->now);
3579689912eSchristos 			} else if ((search->options &
3589689912eSchristos 				    DNS_DBFIND_STALEENABLED) != 0 &&
3599689912eSchristos 				   search->now <
3609689912eSchristos 					   (atomic_load_acquire(
3619689912eSchristos 						    &header->last_refresh_fail_ts) +
3629689912eSchristos 					    search->rbtdb->serve_stale_refresh))
3639689912eSchristos 			{
3649689912eSchristos 				/*
3659689912eSchristos 				 * If we are within interval between last
3669689912eSchristos 				 * refresh failure time + 'stale-refresh-time',
3679689912eSchristos 				 * then don't skip this stale entry but use it
3689689912eSchristos 				 * instead.
3699689912eSchristos 				 */
3709689912eSchristos 				DNS_SLABHEADER_SETATTR(
3719689912eSchristos 					header,
3729689912eSchristos 					DNS_SLABHEADERATTR_STALE_WINDOW);
3739689912eSchristos 				return false;
3749689912eSchristos 			} else if ((search->options &
3759689912eSchristos 				    DNS_DBFIND_STALETIMEOUT) != 0)
3769689912eSchristos 			{
3779689912eSchristos 				/*
3789689912eSchristos 				 * We want stale RRset due to timeout, so we
3799689912eSchristos 				 * don't skip it.
3809689912eSchristos 				 */
3819689912eSchristos 				return false;
3829689912eSchristos 			}
3839689912eSchristos 			return (search->options & DNS_DBFIND_STALEOK) == 0;
3849689912eSchristos 		}
3859689912eSchristos 
3869689912eSchristos 		/*
3879689912eSchristos 		 * This rdataset is stale.  If no one else is using the
3889689912eSchristos 		 * node, we can clean it up right now, otherwise we mark
3899689912eSchristos 		 * it as ancient, and the node as dirty, so it will get
3909689912eSchristos 		 * cleaned up later.
3919689912eSchristos 		 */
3929689912eSchristos 		if ((header->ttl < search->now - RBTDB_VIRTUAL) &&
3939689912eSchristos 		    (*nlocktypep == isc_rwlocktype_write ||
3949689912eSchristos 		     NODE_TRYUPGRADE(lock, nlocktypep) == ISC_R_SUCCESS))
3959689912eSchristos 		{
3969689912eSchristos 			/*
3979689912eSchristos 			 * We update the node's status only when we can
3989689912eSchristos 			 * get write access; otherwise, we leave others
3999689912eSchristos 			 * to this work.  Periodical cleaning will
4009689912eSchristos 			 * eventually take the job as the last resort.
4019689912eSchristos 			 * We won't downgrade the lock, since other
4029689912eSchristos 			 * rdatasets are probably stale, too.
4039689912eSchristos 			 */
4049689912eSchristos 
4059689912eSchristos 			if (isc_refcount_current(&node->references) == 0) {
4069689912eSchristos 				/*
4079689912eSchristos 				 * header->down can be non-NULL if the
4089689912eSchristos 				 * refcount has just decremented to 0
4099689912eSchristos 				 * but dns__rbtdb_decref() has not
4109689912eSchristos 				 * performed clean_cache_node(), in
4119689912eSchristos 				 * which case we need to purge the stale
4129689912eSchristos 				 * headers first.
4139689912eSchristos 				 */
4149689912eSchristos 				clean_stale_headers(header);
4159689912eSchristos 				if (*header_prev != NULL) {
4169689912eSchristos 					(*header_prev)->next = header->next;
4179689912eSchristos 				} else {
4189689912eSchristos 					node->data = header->next;
4199689912eSchristos 				}
4209689912eSchristos 				dns_slabheader_destroy(&header);
4219689912eSchristos 			} else {
4229689912eSchristos 				dns__rbtdb_mark(header,
4239689912eSchristos 						DNS_SLABHEADERATTR_ANCIENT);
4249689912eSchristos 				RBTDB_HEADERNODE(header)->dirty = 1;
4259689912eSchristos 				*header_prev = header;
4269689912eSchristos 			}
4279689912eSchristos 		} else {
4289689912eSchristos 			*header_prev = header;
4299689912eSchristos 		}
4309689912eSchristos 		return true;
4319689912eSchristos 	}
4329689912eSchristos 	return false;
4339689912eSchristos }
4349689912eSchristos 
4359689912eSchristos static isc_result_t
4369689912eSchristos cache_zonecut_callback(dns_rbtnode_t *node, dns_name_t *name,
4379689912eSchristos 		       void *arg DNS__DB_FLARG) {
4389689912eSchristos 	rbtdb_search_t *search = arg;
4399689912eSchristos 	dns_slabheader_t *header = NULL;
4409689912eSchristos 	dns_slabheader_t *header_prev = NULL, *header_next = NULL;
4419689912eSchristos 	dns_slabheader_t *dname_header = NULL, *sigdname_header = NULL;
4429689912eSchristos 	isc_result_t result;
4439689912eSchristos 	isc_rwlock_t *lock = NULL;
4449689912eSchristos 	isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
4459689912eSchristos 
4469689912eSchristos 	REQUIRE(search->zonecut == NULL);
4479689912eSchristos 
4489689912eSchristos 	/*
4499689912eSchristos 	 * Keep compiler silent.
4509689912eSchristos 	 */
4519689912eSchristos 	UNUSED(name);
4529689912eSchristos 
4539689912eSchristos 	lock = &(search->rbtdb->node_locks[node->locknum].lock);
4549689912eSchristos 	NODE_RDLOCK(lock, &nlocktype);
4559689912eSchristos 
4569689912eSchristos 	/*
4579689912eSchristos 	 * Look for a DNAME or RRSIG DNAME rdataset.
4589689912eSchristos 	 */
4599689912eSchristos 	for (header = node->data; header != NULL; header = header_next) {
4609689912eSchristos 		header_next = header->next;
4619689912eSchristos 		if (check_stale_header(node, header, &nlocktype, lock, search,
4629689912eSchristos 				       &header_prev))
4639689912eSchristos 		{
4649689912eSchristos 			/* Do nothing. */
4659689912eSchristos 		} else if (header->type == dns_rdatatype_dname &&
4669689912eSchristos 			   EXISTS(header) && !ANCIENT(header))
4679689912eSchristos 		{
4689689912eSchristos 			dname_header = header;
4699689912eSchristos 			header_prev = header;
4709689912eSchristos 		} else if (header->type == DNS_SIGTYPE(dns_rdatatype_dname) &&
4719689912eSchristos 			   EXISTS(header) && !ANCIENT(header))
4729689912eSchristos 		{
4739689912eSchristos 			sigdname_header = header;
4749689912eSchristos 			header_prev = header;
4759689912eSchristos 		} else {
4769689912eSchristos 			header_prev = header;
4779689912eSchristos 		}
4789689912eSchristos 	}
4799689912eSchristos 
4809689912eSchristos 	if (dname_header != NULL &&
4819689912eSchristos 	    (!DNS_TRUST_PENDING(dname_header->trust) ||
4829689912eSchristos 	     (search->options & DNS_DBFIND_PENDINGOK) != 0))
4839689912eSchristos 	{
4849689912eSchristos 		/*
4859689912eSchristos 		 * We increment the reference count on node to ensure that
4869689912eSchristos 		 * search->zonecut_header will still be valid later.
4879689912eSchristos 		 */
4889689912eSchristos 		dns__rbtdb_newref(search->rbtdb, node,
4899689912eSchristos 				  nlocktype DNS__DB_FLARG_PASS);
4909689912eSchristos 		search->zonecut = node;
4919689912eSchristos 		search->zonecut_header = dname_header;
4929689912eSchristos 		search->zonecut_sigheader = sigdname_header;
4939689912eSchristos 		search->need_cleanup = true;
4949689912eSchristos 		result = DNS_R_PARTIALMATCH;
4959689912eSchristos 	} else {
4969689912eSchristos 		result = DNS_R_CONTINUE;
4979689912eSchristos 	}
4989689912eSchristos 
4999689912eSchristos 	NODE_UNLOCK(lock, &nlocktype);
5009689912eSchristos 
5019689912eSchristos 	return result;
5029689912eSchristos }
5039689912eSchristos 
5049689912eSchristos static isc_result_t
5059689912eSchristos find_deepest_zonecut(rbtdb_search_t *search, dns_rbtnode_t *node,
5069689912eSchristos 		     dns_dbnode_t **nodep, dns_name_t *foundname,
5079689912eSchristos 		     dns_rdataset_t *rdataset,
5089689912eSchristos 		     dns_rdataset_t *sigrdataset DNS__DB_FLARG) {
5099689912eSchristos 	unsigned int i;
5109689912eSchristos 	isc_result_t result = ISC_R_NOTFOUND;
5119689912eSchristos 	dns_name_t name;
5129689912eSchristos 	dns_rbtdb_t *rbtdb = NULL;
5139689912eSchristos 	bool done;
5149689912eSchristos 
5159689912eSchristos 	/*
5169689912eSchristos 	 * Caller must be holding the tree lock.
5179689912eSchristos 	 */
5189689912eSchristos 
5199689912eSchristos 	rbtdb = search->rbtdb;
5209689912eSchristos 	i = search->chain.level_matches;
5219689912eSchristos 	done = false;
5229689912eSchristos 	do {
5239689912eSchristos 		dns_slabheader_t *header = NULL;
5249689912eSchristos 		dns_slabheader_t *header_prev = NULL, *header_next = NULL;
5259689912eSchristos 		dns_slabheader_t *found = NULL, *foundsig = NULL;
5269689912eSchristos 		isc_rwlock_t *lock = &rbtdb->node_locks[node->locknum].lock;
5279689912eSchristos 		isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
5289689912eSchristos 
5299689912eSchristos 		NODE_RDLOCK(lock, &nlocktype);
5309689912eSchristos 
5319689912eSchristos 		/*
5329689912eSchristos 		 * Look for NS and RRSIG NS rdatasets.
5339689912eSchristos 		 */
5349689912eSchristos 		for (header = node->data; header != NULL; header = header_next)
5359689912eSchristos 		{
5369689912eSchristos 			header_next = header->next;
5379689912eSchristos 			if (check_stale_header(node, header, &nlocktype, lock,
5389689912eSchristos 					       search, &header_prev))
5399689912eSchristos 			{
5409689912eSchristos 				/* Do nothing. */
5419689912eSchristos 			} else if (EXISTS(header) && !ANCIENT(header)) {
5429689912eSchristos 				/*
5439689912eSchristos 				 * We've found an extant rdataset.  See if
5449689912eSchristos 				 * we're interested in it.
5459689912eSchristos 				 */
5469689912eSchristos 				if (header->type == dns_rdatatype_ns) {
5479689912eSchristos 					found = header;
5489689912eSchristos 					if (foundsig != NULL) {
5499689912eSchristos 						break;
5509689912eSchristos 					}
5519689912eSchristos 				} else if (header->type ==
5529689912eSchristos 					   DNS_SIGTYPE(dns_rdatatype_ns))
5539689912eSchristos 				{
5549689912eSchristos 					foundsig = header;
5559689912eSchristos 					if (found != NULL) {
5569689912eSchristos 						break;
5579689912eSchristos 					}
5589689912eSchristos 				}
5599689912eSchristos 				header_prev = header;
5609689912eSchristos 			} else {
5619689912eSchristos 				header_prev = header;
5629689912eSchristos 			}
5639689912eSchristos 		}
5649689912eSchristos 
5659689912eSchristos 		if (found != NULL) {
5669689912eSchristos 			/*
5679689912eSchristos 			 * If we have to set foundname, we do it before
5689689912eSchristos 			 * anything else.  If we were to set foundname after
5699689912eSchristos 			 * we had set nodep or bound the rdataset, then we'd
5709689912eSchristos 			 * have to undo that work if dns_name_concatenate()
5719689912eSchristos 			 * failed.  By setting foundname first, there's
5729689912eSchristos 			 * nothing to undo if we have trouble.
5739689912eSchristos 			 */
5749689912eSchristos 			if (foundname != NULL) {
5759689912eSchristos 				dns_name_init(&name, NULL);
5769689912eSchristos 				dns_rbt_namefromnode(node, &name);
5779689912eSchristos 				dns_name_copy(&name, foundname);
5789689912eSchristos 				while (i > 0) {
5799689912eSchristos 					dns_rbtnode_t *level_node =
5809689912eSchristos 						search->chain.levels[--i];
5819689912eSchristos 					dns_name_init(&name, NULL);
5829689912eSchristos 					dns_rbt_namefromnode(level_node, &name);
5839689912eSchristos 					result = dns_name_concatenate(
5849689912eSchristos 						foundname, &name, foundname,
5859689912eSchristos 						NULL);
5869689912eSchristos 					if (result != ISC_R_SUCCESS) {
5879689912eSchristos 						if (nodep != NULL) {
5889689912eSchristos 							*nodep = NULL;
5899689912eSchristos 						}
5909689912eSchristos 						goto node_exit;
5919689912eSchristos 					}
5929689912eSchristos 				}
5939689912eSchristos 			}
5949689912eSchristos 			result = DNS_R_DELEGATION;
5959689912eSchristos 			if (nodep != NULL) {
5969689912eSchristos 				dns__rbtdb_newref(search->rbtdb, node,
5979689912eSchristos 						  nlocktype DNS__DB_FLARG_PASS);
5989689912eSchristos 				*nodep = node;
5999689912eSchristos 			}
6009689912eSchristos 			dns__rbtdb_bindrdataset(search->rbtdb, node, found,
6019689912eSchristos 						search->now, nlocktype,
6029689912eSchristos 						rdataset DNS__DB_FLARG_PASS);
6039689912eSchristos 			if (foundsig != NULL) {
6049689912eSchristos 				dns__rbtdb_bindrdataset(
6059689912eSchristos 					search->rbtdb, node, foundsig,
6069689912eSchristos 					search->now, nlocktype,
6079689912eSchristos 					sigrdataset DNS__DB_FLARG_PASS);
6089689912eSchristos 			}
6099689912eSchristos 			if (need_headerupdate(found, search->now) ||
6109689912eSchristos 			    (foundsig != NULL &&
6119689912eSchristos 			     need_headerupdate(foundsig, search->now)))
6129689912eSchristos 			{
6139689912eSchristos 				if (nlocktype != isc_rwlocktype_write) {
6149689912eSchristos 					NODE_FORCEUPGRADE(lock, &nlocktype);
6159689912eSchristos 					POST(nlocktype);
6169689912eSchristos 				}
6179689912eSchristos 				if (need_headerupdate(found, search->now)) {
6189689912eSchristos 					update_header(search->rbtdb, found,
6199689912eSchristos 						      search->now);
6209689912eSchristos 				}
6219689912eSchristos 				if (foundsig != NULL &&
6229689912eSchristos 				    need_headerupdate(foundsig, search->now))
6239689912eSchristos 				{
6249689912eSchristos 					update_header(search->rbtdb, foundsig,
6259689912eSchristos 						      search->now);
6269689912eSchristos 				}
6279689912eSchristos 			}
6289689912eSchristos 		}
6299689912eSchristos 
6309689912eSchristos 	node_exit:
6319689912eSchristos 		NODE_UNLOCK(lock, &nlocktype);
6329689912eSchristos 
6339689912eSchristos 		if (found == NULL && i > 0) {
6349689912eSchristos 			i--;
6359689912eSchristos 			node = search->chain.levels[i];
6369689912eSchristos 		} else {
6379689912eSchristos 			done = true;
6389689912eSchristos 		}
6399689912eSchristos 	} while (!done);
6409689912eSchristos 
6419689912eSchristos 	return result;
6429689912eSchristos }
6439689912eSchristos 
6449689912eSchristos /*
6459689912eSchristos  * Look for a potentially covering NSEC in the cache where `name`
6469689912eSchristos  * is known not to exist.  This uses the auxiliary NSEC tree to find
6479689912eSchristos  * the potential NSEC owner. If found, we update 'foundname', 'nodep',
6489689912eSchristos  * 'rdataset' and 'sigrdataset', and return DNS_R_COVERINGNSEC.
6499689912eSchristos  * Otherwise, return ISC_R_NOTFOUND.
6509689912eSchristos  */
6519689912eSchristos static isc_result_t
6529689912eSchristos find_coveringnsec(rbtdb_search_t *search, const dns_name_t *name,
6539689912eSchristos 		  dns_dbnode_t **nodep, isc_stdtime_t now,
6549689912eSchristos 		  dns_name_t *foundname, dns_rdataset_t *rdataset,
6559689912eSchristos 		  dns_rdataset_t *sigrdataset DNS__DB_FLARG) {
6569689912eSchristos 	dns_fixedname_t fprefix, forigin, ftarget, fixed;
6579689912eSchristos 	dns_name_t *prefix = NULL, *origin = NULL;
6589689912eSchristos 	dns_name_t *target = NULL, *fname = NULL;
6599689912eSchristos 	dns_rbtnode_t *node = NULL;
6609689912eSchristos 	dns_rbtnodechain_t chain;
6619689912eSchristos 	isc_result_t result;
6629689912eSchristos 	isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
6639689912eSchristos 	isc_rwlock_t *lock = NULL;
6649689912eSchristos 	dns_typepair_t matchtype, sigmatchtype;
6659689912eSchristos 	dns_slabheader_t *found = NULL, *foundsig = NULL;
6669689912eSchristos 	dns_slabheader_t *header = NULL;
6679689912eSchristos 	dns_slabheader_t *header_next = NULL, *header_prev = NULL;
6689689912eSchristos 
6699689912eSchristos 	/*
6709689912eSchristos 	 * Look for the node in the auxilary tree.
6719689912eSchristos 	 */
6729689912eSchristos 	dns_rbtnodechain_init(&chain);
6739689912eSchristos 	target = dns_fixedname_initname(&ftarget);
6749689912eSchristos 	result = dns_rbt_findnode(search->rbtdb->nsec, name, target, &node,
6759689912eSchristos 				  &chain, DNS_RBTFIND_EMPTYDATA, NULL, NULL);
6769689912eSchristos 	if (result != DNS_R_PARTIALMATCH) {
6779689912eSchristos 		dns_rbtnodechain_reset(&chain);
6789689912eSchristos 		return ISC_R_NOTFOUND;
6799689912eSchristos 	}
6809689912eSchristos 
6819689912eSchristos 	prefix = dns_fixedname_initname(&fprefix);
6829689912eSchristos 	origin = dns_fixedname_initname(&forigin);
6839689912eSchristos 	target = dns_fixedname_initname(&ftarget);
6849689912eSchristos 	fname = dns_fixedname_initname(&fixed);
6859689912eSchristos 
6869689912eSchristos 	matchtype = DNS_TYPEPAIR_VALUE(dns_rdatatype_nsec, 0);
6879689912eSchristos 	sigmatchtype = DNS_SIGTYPE(dns_rdatatype_nsec);
6889689912eSchristos 
6899689912eSchristos 	/*
6909689912eSchristos 	 * Extract predecessor from chain.
6919689912eSchristos 	 */
6929689912eSchristos 	result = dns_rbtnodechain_current(&chain, prefix, origin, NULL);
6939689912eSchristos 	dns_rbtnodechain_reset(&chain);
6949689912eSchristos 	if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
6959689912eSchristos 		return ISC_R_NOTFOUND;
6969689912eSchristos 	}
6979689912eSchristos 
6989689912eSchristos 	result = dns_name_concatenate(prefix, origin, target, NULL);
6999689912eSchristos 	if (result != ISC_R_SUCCESS) {
7009689912eSchristos 		return ISC_R_NOTFOUND;
7019689912eSchristos 	}
7029689912eSchristos 
7039689912eSchristos 	/*
7049689912eSchristos 	 * Lookup the predecessor in the main tree.
7059689912eSchristos 	 */
7069689912eSchristos 	node = NULL;
7079689912eSchristos 	result = dns_rbt_findnode(search->rbtdb->tree, target, fname, &node,
7089689912eSchristos 				  NULL, DNS_RBTFIND_EMPTYDATA, NULL, NULL);
7099689912eSchristos 	if (result != ISC_R_SUCCESS) {
7109689912eSchristos 		return ISC_R_NOTFOUND;
7119689912eSchristos 	}
7129689912eSchristos 
7139689912eSchristos 	lock = &(search->rbtdb->node_locks[node->locknum].lock);
7149689912eSchristos 	NODE_RDLOCK(lock, &nlocktype);
7159689912eSchristos 	for (header = node->data; header != NULL; header = header_next) {
7169689912eSchristos 		header_next = header->next;
7179689912eSchristos 		if (check_stale_header(node, header, &nlocktype, lock, search,
7189689912eSchristos 				       &header_prev))
7199689912eSchristos 		{
7209689912eSchristos 			continue;
7219689912eSchristos 		}
7229689912eSchristos 		if (NONEXISTENT(header) || DNS_TYPEPAIR_TYPE(header->type) == 0)
7239689912eSchristos 		{
7249689912eSchristos 			header_prev = header;
7259689912eSchristos 			continue;
7269689912eSchristos 		}
7279689912eSchristos 		if (header->type == matchtype) {
7289689912eSchristos 			found = header;
7299689912eSchristos 			if (foundsig != NULL) {
7309689912eSchristos 				break;
7319689912eSchristos 			}
7329689912eSchristos 		} else if (header->type == sigmatchtype) {
7339689912eSchristos 			foundsig = header;
7349689912eSchristos 			if (found != NULL) {
7359689912eSchristos 				break;
7369689912eSchristos 			}
7379689912eSchristos 		}
7389689912eSchristos 		header_prev = header;
7399689912eSchristos 	}
7409689912eSchristos 	if (found != NULL) {
7419689912eSchristos 		dns__rbtdb_bindrdataset(search->rbtdb, node, found, now,
7429689912eSchristos 					nlocktype, rdataset DNS__DB_FLARG_PASS);
7439689912eSchristos 		if (foundsig != NULL) {
7449689912eSchristos 			dns__rbtdb_bindrdataset(search->rbtdb, node, foundsig,
7459689912eSchristos 						now, nlocktype,
7469689912eSchristos 						sigrdataset DNS__DB_FLARG_PASS);
7479689912eSchristos 		}
7489689912eSchristos 		dns__rbtdb_newref(search->rbtdb, node,
7499689912eSchristos 				  nlocktype DNS__DB_FLARG_PASS);
7509689912eSchristos 
7519689912eSchristos 		dns_name_copy(fname, foundname);
7529689912eSchristos 
7539689912eSchristos 		*nodep = node;
7549689912eSchristos 		result = DNS_R_COVERINGNSEC;
7559689912eSchristos 	} else {
7569689912eSchristos 		result = ISC_R_NOTFOUND;
7579689912eSchristos 	}
7589689912eSchristos 	NODE_UNLOCK(lock, &nlocktype);
7599689912eSchristos 	return result;
7609689912eSchristos }
7619689912eSchristos 
7629689912eSchristos static isc_result_t
7639689912eSchristos cache_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
7649689912eSchristos 	   dns_rdatatype_t type, unsigned int options, isc_stdtime_t now,
7659689912eSchristos 	   dns_dbnode_t **nodep, dns_name_t *foundname,
7669689912eSchristos 	   dns_rdataset_t *rdataset,
7679689912eSchristos 	   dns_rdataset_t *sigrdataset DNS__DB_FLARG) {
7689689912eSchristos 	dns_rbtnode_t *node = NULL;
7699689912eSchristos 	isc_result_t result;
7709689912eSchristos 	rbtdb_search_t search;
7719689912eSchristos 	bool cname_ok = true;
7729689912eSchristos 	bool found_noqname = false;
7739689912eSchristos 	bool all_negative = true;
7749689912eSchristos 	bool empty_node;
7759689912eSchristos 	isc_rwlock_t *lock = NULL;
7769689912eSchristos 	isc_rwlocktype_t tlocktype = isc_rwlocktype_none;
7779689912eSchristos 	isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
7789689912eSchristos 	dns_slabheader_t *header = NULL;
7799689912eSchristos 	dns_slabheader_t *header_prev = NULL, *header_next = NULL;
7809689912eSchristos 	dns_slabheader_t *found = NULL, *nsheader = NULL;
7819689912eSchristos 	dns_slabheader_t *foundsig = NULL, *nssig = NULL, *cnamesig = NULL;
7829689912eSchristos 	dns_slabheader_t *update = NULL, *updatesig = NULL;
7839689912eSchristos 	dns_slabheader_t *nsecheader = NULL, *nsecsig = NULL;
7849689912eSchristos 	dns_typepair_t sigtype, negtype;
7859689912eSchristos 
7869689912eSchristos 	UNUSED(version);
7879689912eSchristos 
7889689912eSchristos 	REQUIRE(VALID_RBTDB((dns_rbtdb_t *)db));
7899689912eSchristos 	REQUIRE(version == NULL);
7909689912eSchristos 
7919689912eSchristos 	if (now == 0) {
7929689912eSchristos 		now = isc_stdtime_now();
7939689912eSchristos 	}
7949689912eSchristos 
7959689912eSchristos 	search = (rbtdb_search_t){
7969689912eSchristos 		.rbtdb = (dns_rbtdb_t *)db,
7979689912eSchristos 		.serial = 1,
7989689912eSchristos 		.options = options,
7999689912eSchristos 		.now = now,
8009689912eSchristos 	};
8019689912eSchristos 	dns_fixedname_init(&search.zonecut_name);
8029689912eSchristos 	dns_rbtnodechain_init(&search.chain);
8039689912eSchristos 
8049689912eSchristos 	TREE_RDLOCK(&search.rbtdb->tree_lock, &tlocktype);
8059689912eSchristos 
8069689912eSchristos 	/*
8079689912eSchristos 	 * Search down from the root of the tree.  If, while going down, we
8089689912eSchristos 	 * encounter a callback node, cache_zonecut_callback() will search the
8099689912eSchristos 	 * rdatasets at the zone cut for a DNAME rdataset.
8109689912eSchristos 	 */
8119689912eSchristos 	result = dns_rbt_findnode(search.rbtdb->tree, name, foundname, &node,
8129689912eSchristos 				  &search.chain, DNS_RBTFIND_EMPTYDATA,
8139689912eSchristos 				  cache_zonecut_callback, &search);
8149689912eSchristos 
8159689912eSchristos 	if (result == DNS_R_PARTIALMATCH) {
8169689912eSchristos 		/*
8179689912eSchristos 		 * If dns_rbt_findnode discovered a covering DNAME skip
8189689912eSchristos 		 * looking for a covering NSEC.
8199689912eSchristos 		 */
8209689912eSchristos 		if ((search.options & DNS_DBFIND_COVERINGNSEC) != 0 &&
8219689912eSchristos 		    (search.zonecut_header == NULL ||
8229689912eSchristos 		     search.zonecut_header->type != dns_rdatatype_dname))
8239689912eSchristos 		{
8249689912eSchristos 			result = find_coveringnsec(
8259689912eSchristos 				&search, name, nodep, now, foundname, rdataset,
8269689912eSchristos 				sigrdataset DNS__DB_FLARG_PASS);
8279689912eSchristos 			if (result == DNS_R_COVERINGNSEC) {
8289689912eSchristos 				goto tree_exit;
8299689912eSchristos 			}
8309689912eSchristos 		}
8319689912eSchristos 		if (search.zonecut != NULL) {
8329689912eSchristos 			result = setup_delegation(
8339689912eSchristos 				&search, nodep, foundname, rdataset,
8349689912eSchristos 				sigrdataset DNS__DB_FLARG_PASS);
8359689912eSchristos 			goto tree_exit;
8369689912eSchristos 		} else {
8379689912eSchristos 		find_ns:
8389689912eSchristos 			result = find_deepest_zonecut(
8399689912eSchristos 				&search, node, nodep, foundname, rdataset,
8409689912eSchristos 				sigrdataset DNS__DB_FLARG_PASS);
8419689912eSchristos 			goto tree_exit;
8429689912eSchristos 		}
8439689912eSchristos 	} else if (result != ISC_R_SUCCESS) {
8449689912eSchristos 		goto tree_exit;
8459689912eSchristos 	}
8469689912eSchristos 
8479689912eSchristos 	/*
8489689912eSchristos 	 * Certain DNSSEC types are not subject to CNAME matching
8499689912eSchristos 	 * (RFC4035, section 2.5 and RFC3007).
8509689912eSchristos 	 *
8519689912eSchristos 	 * We don't check for RRSIG, because we don't store RRSIG records
8529689912eSchristos 	 * directly.
8539689912eSchristos 	 */
8549689912eSchristos 	if (type == dns_rdatatype_key || type == dns_rdatatype_nsec) {
8559689912eSchristos 		cname_ok = false;
8569689912eSchristos 	}
8579689912eSchristos 
8589689912eSchristos 	/*
8599689912eSchristos 	 * We now go looking for rdata...
8609689912eSchristos 	 */
8619689912eSchristos 
8629689912eSchristos 	lock = &(search.rbtdb->node_locks[node->locknum].lock);
8639689912eSchristos 	NODE_RDLOCK(lock, &nlocktype);
8649689912eSchristos 
8659689912eSchristos 	/*
8669689912eSchristos 	 * These pointers need to be reset here in case we did
8679689912eSchristos 	 * 'goto find_ns' from somewhere below.
8689689912eSchristos 	 */
8699689912eSchristos 	found = NULL;
8709689912eSchristos 	foundsig = NULL;
8719689912eSchristos 	sigtype = DNS_SIGTYPE(type);
8729689912eSchristos 	negtype = DNS_TYPEPAIR_VALUE(0, type);
8739689912eSchristos 	nsheader = NULL;
8749689912eSchristos 	nsecheader = NULL;
8759689912eSchristos 	nssig = NULL;
8769689912eSchristos 	nsecsig = NULL;
8779689912eSchristos 	cnamesig = NULL;
8789689912eSchristos 	empty_node = true;
8799689912eSchristos 	header_prev = NULL;
8809689912eSchristos 	for (header = node->data; header != NULL; header = header_next) {
8819689912eSchristos 		header_next = header->next;
8829689912eSchristos 		if (check_stale_header(node, header, &nlocktype, lock, &search,
8839689912eSchristos 				       &header_prev))
8849689912eSchristos 		{
8859689912eSchristos 			/* Do nothing. */
8869689912eSchristos 		} else if (EXISTS(header) && !ANCIENT(header)) {
8879689912eSchristos 			/*
8889689912eSchristos 			 * We now know that there is at least one active
8899689912eSchristos 			 * non-stale rdataset at this node.
8909689912eSchristos 			 */
8919689912eSchristos 			empty_node = false;
8929689912eSchristos 			if (header->noqname != NULL &&
8939689912eSchristos 			    header->trust == dns_trust_secure)
8949689912eSchristos 			{
8959689912eSchristos 				found_noqname = true;
8969689912eSchristos 			}
8979689912eSchristos 			if (!NEGATIVE(header)) {
8989689912eSchristos 				all_negative = false;
8999689912eSchristos 			}
9009689912eSchristos 
9019689912eSchristos 			/*
9029689912eSchristos 			 * If we found a type we were looking for, remember
9039689912eSchristos 			 * it.
9049689912eSchristos 			 */
9059689912eSchristos 			if (header->type == type ||
9069689912eSchristos 			    (type == dns_rdatatype_any &&
9079689912eSchristos 			     DNS_TYPEPAIR_TYPE(header->type) != 0) ||
9089689912eSchristos 			    (cname_ok && header->type == dns_rdatatype_cname))
9099689912eSchristos 			{
9109689912eSchristos 				/*
9119689912eSchristos 				 * We've found the answer.
9129689912eSchristos 				 */
9139689912eSchristos 				found = header;
9149689912eSchristos 				if (header->type == dns_rdatatype_cname &&
9159689912eSchristos 				    cname_ok)
9169689912eSchristos 				{
9179689912eSchristos 					/*
9189689912eSchristos 					 * If we've already got the
9199689912eSchristos 					 * CNAME RRSIG, use it.
9209689912eSchristos 					 */
9219689912eSchristos 					if (cnamesig != NULL) {
9229689912eSchristos 						foundsig = cnamesig;
9239689912eSchristos 					} else {
9249689912eSchristos 						sigtype = DNS_SIGTYPE(
9259689912eSchristos 							dns_rdatatype_cname);
9269689912eSchristos 					}
9279689912eSchristos 				}
9289689912eSchristos 			} else if (header->type == sigtype) {
9299689912eSchristos 				/*
9309689912eSchristos 				 * We've found the RRSIG rdataset for our
9319689912eSchristos 				 * target type.  Remember it.
9329689912eSchristos 				 */
9339689912eSchristos 				foundsig = header;
9349689912eSchristos 			} else if (header->type == RDATATYPE_NCACHEANY ||
9359689912eSchristos 				   header->type == negtype)
9369689912eSchristos 			{
9379689912eSchristos 				/*
9389689912eSchristos 				 * We've found a negative cache entry.
9399689912eSchristos 				 */
9409689912eSchristos 				found = header;
9419689912eSchristos 			} else if (header->type == dns_rdatatype_ns) {
9429689912eSchristos 				/*
9439689912eSchristos 				 * Remember a NS rdataset even if we're
9449689912eSchristos 				 * not specifically looking for it, because
9459689912eSchristos 				 * we might need it later.
9469689912eSchristos 				 */
9479689912eSchristos 				nsheader = header;
9489689912eSchristos 			} else if (header->type ==
9499689912eSchristos 				   DNS_SIGTYPE(dns_rdatatype_ns))
9509689912eSchristos 			{
9519689912eSchristos 				/*
9529689912eSchristos 				 * If we need the NS rdataset, we'll also
9539689912eSchristos 				 * need its signature.
9549689912eSchristos 				 */
9559689912eSchristos 				nssig = header;
9569689912eSchristos 			} else if (header->type == dns_rdatatype_nsec) {
9579689912eSchristos 				nsecheader = header;
9589689912eSchristos 			} else if (header->type ==
9599689912eSchristos 				   DNS_SIGTYPE(dns_rdatatype_nsec))
9609689912eSchristos 			{
9619689912eSchristos 				nsecsig = header;
9629689912eSchristos 			} else if (cname_ok &&
9639689912eSchristos 				   header->type ==
9649689912eSchristos 					   DNS_SIGTYPE(dns_rdatatype_cname))
9659689912eSchristos 			{
9669689912eSchristos 				/*
9679689912eSchristos 				 * If we get a CNAME match, we'll also need
9689689912eSchristos 				 * its signature.
9699689912eSchristos 				 */
9709689912eSchristos 				cnamesig = header;
9719689912eSchristos 			}
9729689912eSchristos 			header_prev = header;
9739689912eSchristos 		} else {
9749689912eSchristos 			header_prev = header;
9759689912eSchristos 		}
9769689912eSchristos 	}
9779689912eSchristos 
9789689912eSchristos 	if (empty_node) {
9799689912eSchristos 		/*
9809689912eSchristos 		 * We have an exact match for the name, but there are no
9819689912eSchristos 		 * extant rdatasets.  That means that this node doesn't
9829689912eSchristos 		 * meaningfully exist, and that we really have a partial match.
9839689912eSchristos 		 */
9849689912eSchristos 		NODE_UNLOCK(lock, &nlocktype);
9859689912eSchristos 		if ((search.options & DNS_DBFIND_COVERINGNSEC) != 0) {
9869689912eSchristos 			result = find_coveringnsec(
9879689912eSchristos 				&search, name, nodep, now, foundname, rdataset,
9889689912eSchristos 				sigrdataset DNS__DB_FLARG_PASS);
9899689912eSchristos 			if (result == DNS_R_COVERINGNSEC) {
9909689912eSchristos 				goto tree_exit;
9919689912eSchristos 			}
9929689912eSchristos 		}
9939689912eSchristos 		goto find_ns;
9949689912eSchristos 	}
9959689912eSchristos 
9969689912eSchristos 	/*
9979689912eSchristos 	 * If we didn't find what we were looking for...
9989689912eSchristos 	 */
9999689912eSchristos 	if (found == NULL ||
10009689912eSchristos 	    (DNS_TRUST_ADDITIONAL(found->trust) &&
10019689912eSchristos 	     ((options & DNS_DBFIND_ADDITIONALOK) == 0)) ||
10029689912eSchristos 	    (found->trust == dns_trust_glue &&
10039689912eSchristos 	     ((options & DNS_DBFIND_GLUEOK) == 0)) ||
10049689912eSchristos 	    (DNS_TRUST_PENDING(found->trust) &&
10059689912eSchristos 	     ((options & DNS_DBFIND_PENDINGOK) == 0)))
10069689912eSchristos 	{
10079689912eSchristos 		/*
10089689912eSchristos 		 * Return covering NODATA NSEC record.
10099689912eSchristos 		 */
10109689912eSchristos 		if ((search.options & DNS_DBFIND_COVERINGNSEC) != 0 &&
10119689912eSchristos 		    nsecheader != NULL)
10129689912eSchristos 		{
10139689912eSchristos 			if (nodep != NULL) {
10149689912eSchristos 				dns__rbtdb_newref(search.rbtdb, node,
10159689912eSchristos 						  nlocktype DNS__DB_FLARG_PASS);
10169689912eSchristos 				*nodep = node;
10179689912eSchristos 			}
10189689912eSchristos 			dns__rbtdb_bindrdataset(search.rbtdb, node, nsecheader,
10199689912eSchristos 						search.now, nlocktype,
10209689912eSchristos 						rdataset DNS__DB_FLARG_PASS);
10219689912eSchristos 			if (need_headerupdate(nsecheader, search.now)) {
10229689912eSchristos 				update = nsecheader;
10239689912eSchristos 			}
10249689912eSchristos 			if (nsecsig != NULL) {
10259689912eSchristos 				dns__rbtdb_bindrdataset(
10269689912eSchristos 					search.rbtdb, node, nsecsig, search.now,
10279689912eSchristos 					nlocktype,
10289689912eSchristos 					sigrdataset DNS__DB_FLARG_PASS);
10299689912eSchristos 				if (need_headerupdate(nsecsig, search.now)) {
10309689912eSchristos 					updatesig = nsecsig;
10319689912eSchristos 				}
10329689912eSchristos 			}
10339689912eSchristos 			result = DNS_R_COVERINGNSEC;
10349689912eSchristos 			goto node_exit;
10359689912eSchristos 		}
10369689912eSchristos 
10379689912eSchristos 		/*
10389689912eSchristos 		 * This name was from a wild card.  Look for a covering NSEC.
10399689912eSchristos 		 */
10409689912eSchristos 		if (found == NULL && (found_noqname || all_negative) &&
10419689912eSchristos 		    (search.options & DNS_DBFIND_COVERINGNSEC) != 0)
10429689912eSchristos 		{
10439689912eSchristos 			NODE_UNLOCK(lock, &nlocktype);
10449689912eSchristos 			result = find_coveringnsec(
10459689912eSchristos 				&search, name, nodep, now, foundname, rdataset,
10469689912eSchristos 				sigrdataset DNS__DB_FLARG_PASS);
10479689912eSchristos 			if (result == DNS_R_COVERINGNSEC) {
10489689912eSchristos 				goto tree_exit;
10499689912eSchristos 			}
10509689912eSchristos 			goto find_ns;
10519689912eSchristos 		}
10529689912eSchristos 
10539689912eSchristos 		/*
10549689912eSchristos 		 * If there is an NS rdataset at this node, then this is the
10559689912eSchristos 		 * deepest zone cut.
10569689912eSchristos 		 */
10579689912eSchristos 		if (nsheader != NULL) {
10589689912eSchristos 			if (nodep != NULL) {
10599689912eSchristos 				dns__rbtdb_newref(search.rbtdb, node,
10609689912eSchristos 						  nlocktype DNS__DB_FLARG_PASS);
10619689912eSchristos 				*nodep = node;
10629689912eSchristos 			}
10639689912eSchristos 			dns__rbtdb_bindrdataset(search.rbtdb, node, nsheader,
10649689912eSchristos 						search.now, nlocktype,
10659689912eSchristos 						rdataset DNS__DB_FLARG_PASS);
10669689912eSchristos 			if (need_headerupdate(nsheader, search.now)) {
10679689912eSchristos 				update = nsheader;
10689689912eSchristos 			}
10699689912eSchristos 			if (nssig != NULL) {
10709689912eSchristos 				dns__rbtdb_bindrdataset(
10719689912eSchristos 					search.rbtdb, node, nssig, search.now,
10729689912eSchristos 					nlocktype,
10739689912eSchristos 					sigrdataset DNS__DB_FLARG_PASS);
10749689912eSchristos 				if (need_headerupdate(nssig, search.now)) {
10759689912eSchristos 					updatesig = nssig;
10769689912eSchristos 				}
10779689912eSchristos 			}
10789689912eSchristos 			result = DNS_R_DELEGATION;
10799689912eSchristos 			goto node_exit;
10809689912eSchristos 		}
10819689912eSchristos 
10829689912eSchristos 		/*
10839689912eSchristos 		 * Go find the deepest zone cut.
10849689912eSchristos 		 */
10859689912eSchristos 		NODE_UNLOCK(lock, &nlocktype);
10869689912eSchristos 		goto find_ns;
10879689912eSchristos 	}
10889689912eSchristos 
10899689912eSchristos 	/*
10909689912eSchristos 	 * We found what we were looking for, or we found a CNAME.
10919689912eSchristos 	 */
10929689912eSchristos 
10939689912eSchristos 	if (nodep != NULL) {
10949689912eSchristos 		dns__rbtdb_newref(search.rbtdb, node,
10959689912eSchristos 				  nlocktype DNS__DB_FLARG_PASS);
10969689912eSchristos 		*nodep = node;
10979689912eSchristos 	}
10989689912eSchristos 
10999689912eSchristos 	if (NEGATIVE(found)) {
11009689912eSchristos 		/*
11019689912eSchristos 		 * We found a negative cache entry.
11029689912eSchristos 		 */
11039689912eSchristos 		if (NXDOMAIN(found)) {
11049689912eSchristos 			result = DNS_R_NCACHENXDOMAIN;
11059689912eSchristos 		} else {
11069689912eSchristos 			result = DNS_R_NCACHENXRRSET;
11079689912eSchristos 		}
11089689912eSchristos 	} else if (type != found->type && type != dns_rdatatype_any &&
11099689912eSchristos 		   found->type == dns_rdatatype_cname)
11109689912eSchristos 	{
11119689912eSchristos 		/*
11129689912eSchristos 		 * We weren't doing an ANY query and we found a CNAME instead
11139689912eSchristos 		 * of the type we were looking for, so we need to indicate
11149689912eSchristos 		 * that result to the caller.
11159689912eSchristos 		 */
11169689912eSchristos 		result = DNS_R_CNAME;
11179689912eSchristos 	} else {
11189689912eSchristos 		/*
11199689912eSchristos 		 * An ordinary successful query!
11209689912eSchristos 		 */
11219689912eSchristos 		result = ISC_R_SUCCESS;
11229689912eSchristos 	}
11239689912eSchristos 
11249689912eSchristos 	if (type != dns_rdatatype_any || result == DNS_R_NCACHENXDOMAIN ||
11259689912eSchristos 	    result == DNS_R_NCACHENXRRSET)
11269689912eSchristos 	{
11279689912eSchristos 		dns__rbtdb_bindrdataset(search.rbtdb, node, found, search.now,
11289689912eSchristos 					nlocktype, rdataset DNS__DB_FLARG_PASS);
11299689912eSchristos 		if (need_headerupdate(found, search.now)) {
11309689912eSchristos 			update = found;
11319689912eSchristos 		}
11329689912eSchristos 		if (!NEGATIVE(found) && foundsig != NULL) {
11339689912eSchristos 			dns__rbtdb_bindrdataset(search.rbtdb, node, foundsig,
11349689912eSchristos 						search.now, nlocktype,
11359689912eSchristos 						sigrdataset DNS__DB_FLARG_PASS);
11369689912eSchristos 			if (need_headerupdate(foundsig, search.now)) {
11379689912eSchristos 				updatesig = foundsig;
11389689912eSchristos 			}
11399689912eSchristos 		}
11409689912eSchristos 	}
11419689912eSchristos 
11429689912eSchristos node_exit:
11439689912eSchristos 	if ((update != NULL || updatesig != NULL) &&
11449689912eSchristos 	    nlocktype != isc_rwlocktype_write)
11459689912eSchristos 	{
11469689912eSchristos 		NODE_FORCEUPGRADE(lock, &nlocktype);
11479689912eSchristos 		POST(nlocktype);
11489689912eSchristos 	}
11499689912eSchristos 	if (update != NULL && need_headerupdate(update, search.now)) {
11509689912eSchristos 		update_header(search.rbtdb, update, search.now);
11519689912eSchristos 	}
11529689912eSchristos 	if (updatesig != NULL && need_headerupdate(updatesig, search.now)) {
11539689912eSchristos 		update_header(search.rbtdb, updatesig, search.now);
11549689912eSchristos 	}
11559689912eSchristos 
11569689912eSchristos 	NODE_UNLOCK(lock, &nlocktype);
11579689912eSchristos 
11589689912eSchristos tree_exit:
11599689912eSchristos 	TREE_UNLOCK(&search.rbtdb->tree_lock, &tlocktype);
11609689912eSchristos 
11619689912eSchristos 	/*
11629689912eSchristos 	 * If we found a zonecut but aren't going to use it, we have to
11639689912eSchristos 	 * let go of it.
11649689912eSchristos 	 */
11659689912eSchristos 	if (search.need_cleanup) {
11669689912eSchristos 		node = search.zonecut;
11679689912eSchristos 		INSIST(node != NULL);
11689689912eSchristos 		lock = &(search.rbtdb->node_locks[node->locknum].lock);
11699689912eSchristos 
11709689912eSchristos 		NODE_RDLOCK(lock, &nlocktype);
11719689912eSchristos 		dns__rbtdb_decref(search.rbtdb, node, 0, &nlocktype, &tlocktype,
11729689912eSchristos 				  true, false DNS__DB_FLARG_PASS);
11739689912eSchristos 		NODE_UNLOCK(lock, &nlocktype);
11749689912eSchristos 		INSIST(tlocktype == isc_rwlocktype_none);
11759689912eSchristos 	}
11769689912eSchristos 
11779689912eSchristos 	dns_rbtnodechain_reset(&search.chain);
11789689912eSchristos 
11799689912eSchristos 	update_cachestats(search.rbtdb, result);
11809689912eSchristos 	return result;
11819689912eSchristos }
11829689912eSchristos 
11839689912eSchristos static isc_result_t
11849689912eSchristos cache_findzonecut(dns_db_t *db, const dns_name_t *name, unsigned int options,
11859689912eSchristos 		  isc_stdtime_t now, dns_dbnode_t **nodep,
11869689912eSchristos 		  dns_name_t *foundname, dns_name_t *dcname,
11879689912eSchristos 		  dns_rdataset_t *rdataset,
11889689912eSchristos 		  dns_rdataset_t *sigrdataset DNS__DB_FLARG) {
11899689912eSchristos 	dns_rbtnode_t *node = NULL;
11909689912eSchristos 	isc_rwlock_t *lock = NULL;
11919689912eSchristos 	isc_result_t result;
11929689912eSchristos 	rbtdb_search_t search;
11939689912eSchristos 	dns_slabheader_t *header = NULL;
11949689912eSchristos 	dns_slabheader_t *header_prev = NULL, *header_next = NULL;
11959689912eSchristos 	dns_slabheader_t *found = NULL, *foundsig = NULL;
11969689912eSchristos 	unsigned int rbtoptions = DNS_RBTFIND_EMPTYDATA;
11979689912eSchristos 	isc_rwlocktype_t tlocktype = isc_rwlocktype_none;
11989689912eSchristos 	isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
11999689912eSchristos 	bool dcnull = (dcname == NULL);
12009689912eSchristos 
12019689912eSchristos 	REQUIRE(VALID_RBTDB((dns_rbtdb_t *)db));
12029689912eSchristos 
12039689912eSchristos 	if (now == 0) {
12049689912eSchristos 		now = isc_stdtime_now();
12059689912eSchristos 	}
12069689912eSchristos 
12079689912eSchristos 	search = (rbtdb_search_t){
12089689912eSchristos 		.rbtdb = (dns_rbtdb_t *)db,
12099689912eSchristos 		.serial = 1,
12109689912eSchristos 		.options = options,
12119689912eSchristos 		.now = now,
12129689912eSchristos 	};
12139689912eSchristos 	dns_fixedname_init(&search.zonecut_name);
12149689912eSchristos 	dns_rbtnodechain_init(&search.chain);
12159689912eSchristos 
12169689912eSchristos 	if (dcnull) {
12179689912eSchristos 		dcname = foundname;
12189689912eSchristos 	}
12199689912eSchristos 
12209689912eSchristos 	if ((options & DNS_DBFIND_NOEXACT) != 0) {
12219689912eSchristos 		rbtoptions |= DNS_RBTFIND_NOEXACT;
12229689912eSchristos 	}
12239689912eSchristos 
12249689912eSchristos 	TREE_RDLOCK(&search.rbtdb->tree_lock, &tlocktype);
12259689912eSchristos 
12269689912eSchristos 	/*
12279689912eSchristos 	 * Search down from the root of the tree.
12289689912eSchristos 	 */
12299689912eSchristos 	result = dns_rbt_findnode(search.rbtdb->tree, name, dcname, &node,
12309689912eSchristos 				  &search.chain, rbtoptions, NULL, &search);
12319689912eSchristos 
12329689912eSchristos 	if (result == DNS_R_PARTIALMATCH) {
12339689912eSchristos 		result = find_deepest_zonecut(&search, node, nodep, foundname,
12349689912eSchristos 					      rdataset,
12359689912eSchristos 					      sigrdataset DNS__DB_FLARG_PASS);
12369689912eSchristos 		goto tree_exit;
12379689912eSchristos 	} else if (result != ISC_R_SUCCESS) {
12389689912eSchristos 		goto tree_exit;
12399689912eSchristos 	} else if (!dcnull) {
12409689912eSchristos 		dns_name_copy(dcname, foundname);
12419689912eSchristos 	}
12429689912eSchristos 
12439689912eSchristos 	/*
12449689912eSchristos 	 * We now go looking for an NS rdataset at the node.
12459689912eSchristos 	 */
12469689912eSchristos 
12479689912eSchristos 	lock = &(search.rbtdb->node_locks[node->locknum].lock);
12489689912eSchristos 	NODE_RDLOCK(lock, &nlocktype);
12499689912eSchristos 
12509689912eSchristos 	for (header = node->data; header != NULL; header = header_next) {
12519689912eSchristos 		header_next = header->next;
12529689912eSchristos 		if (check_stale_header(node, header, &nlocktype, lock, &search,
12539689912eSchristos 				       &header_prev))
12549689912eSchristos 		{
12559689912eSchristos 			/*
12569689912eSchristos 			 * The function dns_rbt_findnode found us the a matching
12579689912eSchristos 			 * node for 'name' and stored the result in 'dcname'.
12589689912eSchristos 			 * This is the deepest known zonecut in our database.
12599689912eSchristos 			 * However, this node may be stale and if serve-stale
12609689912eSchristos 			 * is not enabled (in other words 'stale-answer-enable'
12619689912eSchristos 			 * is set to no), this node may not be used as a
12629689912eSchristos 			 * zonecut we know about. If so, find the deepest
12639689912eSchristos 			 * zonecut from this node up and return that instead.
12649689912eSchristos 			 */
12659689912eSchristos 			NODE_UNLOCK(lock, &nlocktype);
12669689912eSchristos 			result = find_deepest_zonecut(
12679689912eSchristos 				&search, node, nodep, foundname, rdataset,
12689689912eSchristos 				sigrdataset DNS__DB_FLARG_PASS);
12699689912eSchristos 			dns_name_copy(foundname, dcname);
12709689912eSchristos 			goto tree_exit;
12719689912eSchristos 		} else if (EXISTS(header) && !ANCIENT(header)) {
12729689912eSchristos 			/*
12739689912eSchristos 			 * If we found a type we were looking for, remember
12749689912eSchristos 			 * it.
12759689912eSchristos 			 */
12769689912eSchristos 			if (header->type == dns_rdatatype_ns) {
12779689912eSchristos 				/*
12789689912eSchristos 				 * Remember a NS rdataset even if we're
12799689912eSchristos 				 * not specifically looking for it, because
12809689912eSchristos 				 * we might need it later.
12819689912eSchristos 				 */
12829689912eSchristos 				found = header;
12839689912eSchristos 			} else if (header->type ==
12849689912eSchristos 				   DNS_SIGTYPE(dns_rdatatype_ns))
12859689912eSchristos 			{
12869689912eSchristos 				/*
12879689912eSchristos 				 * If we need the NS rdataset, we'll also
12889689912eSchristos 				 * need its signature.
12899689912eSchristos 				 */
12909689912eSchristos 				foundsig = header;
12919689912eSchristos 			}
12929689912eSchristos 			header_prev = header;
12939689912eSchristos 		} else {
12949689912eSchristos 			header_prev = header;
12959689912eSchristos 		}
12969689912eSchristos 	}
12979689912eSchristos 
12989689912eSchristos 	if (found == NULL) {
12999689912eSchristos 		/*
13009689912eSchristos 		 * No NS records here.
13019689912eSchristos 		 */
13029689912eSchristos 		NODE_UNLOCK(lock, &nlocktype);
13039689912eSchristos 		result = find_deepest_zonecut(&search, node, nodep, foundname,
13049689912eSchristos 					      rdataset,
13059689912eSchristos 					      sigrdataset DNS__DB_FLARG_PASS);
13069689912eSchristos 		goto tree_exit;
13079689912eSchristos 	}
13089689912eSchristos 
13099689912eSchristos 	if (nodep != NULL) {
13109689912eSchristos 		dns__rbtdb_newref(search.rbtdb, node,
13119689912eSchristos 				  nlocktype DNS__DB_FLARG_PASS);
13129689912eSchristos 		*nodep = node;
13139689912eSchristos 	}
13149689912eSchristos 
13159689912eSchristos 	dns__rbtdb_bindrdataset(search.rbtdb, node, found, search.now,
13169689912eSchristos 				nlocktype, rdataset DNS__DB_FLARG_PASS);
13179689912eSchristos 	if (foundsig != NULL) {
13189689912eSchristos 		dns__rbtdb_bindrdataset(search.rbtdb, node, foundsig,
13199689912eSchristos 					search.now, nlocktype,
13209689912eSchristos 					sigrdataset DNS__DB_FLARG_PASS);
13219689912eSchristos 	}
13229689912eSchristos 
13239689912eSchristos 	if (need_headerupdate(found, search.now) ||
13249689912eSchristos 	    (foundsig != NULL && need_headerupdate(foundsig, search.now)))
13259689912eSchristos 	{
13269689912eSchristos 		if (nlocktype != isc_rwlocktype_write) {
13279689912eSchristos 			NODE_FORCEUPGRADE(lock, &nlocktype);
13289689912eSchristos 			POST(nlocktype);
13299689912eSchristos 		}
13309689912eSchristos 		if (need_headerupdate(found, search.now)) {
13319689912eSchristos 			update_header(search.rbtdb, found, search.now);
13329689912eSchristos 		}
13339689912eSchristos 		if (foundsig != NULL && need_headerupdate(foundsig, search.now))
13349689912eSchristos 		{
13359689912eSchristos 			update_header(search.rbtdb, foundsig, search.now);
13369689912eSchristos 		}
13379689912eSchristos 	}
13389689912eSchristos 
13399689912eSchristos 	NODE_UNLOCK(lock, &nlocktype);
13409689912eSchristos 
13419689912eSchristos tree_exit:
13429689912eSchristos 	TREE_UNLOCK(&search.rbtdb->tree_lock, &tlocktype);
13439689912eSchristos 
13449689912eSchristos 	INSIST(!search.need_cleanup);
13459689912eSchristos 
13469689912eSchristos 	dns_rbtnodechain_reset(&search.chain);
13479689912eSchristos 
13489689912eSchristos 	if (result == DNS_R_DELEGATION) {
13499689912eSchristos 		result = ISC_R_SUCCESS;
13509689912eSchristos 	}
13519689912eSchristos 
13529689912eSchristos 	return result;
13539689912eSchristos }
13549689912eSchristos 
13559689912eSchristos static isc_result_t
13569689912eSchristos cache_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
13579689912eSchristos 		   dns_rdatatype_t type, dns_rdatatype_t covers,
13589689912eSchristos 		   isc_stdtime_t now, dns_rdataset_t *rdataset,
13599689912eSchristos 		   dns_rdataset_t *sigrdataset DNS__DB_FLARG) {
13609689912eSchristos 	dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
13619689912eSchristos 	dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node;
13629689912eSchristos 	dns_slabheader_t *header = NULL, *header_next = NULL;
13639689912eSchristos 	dns_slabheader_t *found = NULL, *foundsig = NULL;
13649689912eSchristos 	dns_typepair_t matchtype, sigmatchtype, negtype;
13659689912eSchristos 	isc_result_t result;
13669689912eSchristos 	isc_rwlock_t *lock = NULL;
13679689912eSchristos 	isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
13689689912eSchristos 
13699689912eSchristos 	REQUIRE(VALID_RBTDB(rbtdb));
13709689912eSchristos 	REQUIRE(type != dns_rdatatype_any);
13719689912eSchristos 
13729689912eSchristos 	UNUSED(version);
13739689912eSchristos 
13749689912eSchristos 	result = ISC_R_SUCCESS;
13759689912eSchristos 
13769689912eSchristos 	if (now == 0) {
13779689912eSchristos 		now = isc_stdtime_now();
13789689912eSchristos 	}
13799689912eSchristos 
13809689912eSchristos 	lock = &rbtdb->node_locks[rbtnode->locknum].lock;
13819689912eSchristos 	NODE_RDLOCK(lock, &nlocktype);
13829689912eSchristos 
13839689912eSchristos 	matchtype = DNS_TYPEPAIR_VALUE(type, covers);
13849689912eSchristos 	negtype = DNS_TYPEPAIR_VALUE(0, type);
13859689912eSchristos 	if (covers == 0) {
13869689912eSchristos 		sigmatchtype = DNS_SIGTYPE(type);
13879689912eSchristos 	} else {
13889689912eSchristos 		sigmatchtype = 0;
13899689912eSchristos 	}
13909689912eSchristos 
13919689912eSchristos 	for (header = rbtnode->data; header != NULL; header = header_next) {
13929689912eSchristos 		header_next = header->next;
13939689912eSchristos 		if (!ACTIVE(header, now)) {
13949689912eSchristos 			if ((header->ttl + STALE_TTL(header, rbtdb) <
13959689912eSchristos 			     now - RBTDB_VIRTUAL) &&
13969689912eSchristos 			    (nlocktype == isc_rwlocktype_write ||
13979689912eSchristos 			     NODE_TRYUPGRADE(lock, &nlocktype) ==
13989689912eSchristos 				     ISC_R_SUCCESS))
13999689912eSchristos 			{
14009689912eSchristos 				/*
14019689912eSchristos 				 * We update the node's status only when we
14029689912eSchristos 				 * can get write access.
14039689912eSchristos 				 *
14049689912eSchristos 				 * We don't check if refcurrent(rbtnode) == 0
14059689912eSchristos 				 * and try to free like we do in cache_find(),
14069689912eSchristos 				 * because refcurrent(rbtnode) must be
14079689912eSchristos 				 * non-zero.  This is so because 'node' is an
14089689912eSchristos 				 * argument to the function.
14099689912eSchristos 				 */
14109689912eSchristos 				dns__rbtdb_mark(header,
14119689912eSchristos 						DNS_SLABHEADERATTR_ANCIENT);
14129689912eSchristos 				RBTDB_HEADERNODE(header)->dirty = 1;
14139689912eSchristos 			}
14149689912eSchristos 		} else if (EXISTS(header) && !ANCIENT(header)) {
14159689912eSchristos 			if (header->type == matchtype) {
14169689912eSchristos 				found = header;
14179689912eSchristos 			} else if (header->type == RDATATYPE_NCACHEANY ||
14189689912eSchristos 				   header->type == negtype)
14199689912eSchristos 			{
14209689912eSchristos 				found = header;
14219689912eSchristos 			} else if (header->type == sigmatchtype) {
14229689912eSchristos 				foundsig = header;
14239689912eSchristos 			}
14249689912eSchristos 		}
14259689912eSchristos 	}
14269689912eSchristos 	if (found != NULL) {
14279689912eSchristos 		dns__rbtdb_bindrdataset(rbtdb, rbtnode, found, now, nlocktype,
14289689912eSchristos 					rdataset DNS__DB_FLARG_PASS);
14299689912eSchristos 		if (!NEGATIVE(found) && foundsig != NULL) {
14309689912eSchristos 			dns__rbtdb_bindrdataset(rbtdb, rbtnode, foundsig, now,
14319689912eSchristos 						nlocktype,
14329689912eSchristos 						sigrdataset DNS__DB_FLARG_PASS);
14339689912eSchristos 		}
14349689912eSchristos 	}
14359689912eSchristos 
14369689912eSchristos 	NODE_UNLOCK(lock, &nlocktype);
14379689912eSchristos 
14389689912eSchristos 	if (found == NULL) {
14399689912eSchristos 		return ISC_R_NOTFOUND;
14409689912eSchristos 	}
14419689912eSchristos 
14429689912eSchristos 	if (NEGATIVE(found)) {
14439689912eSchristos 		/*
14449689912eSchristos 		 * We found a negative cache entry.
14459689912eSchristos 		 */
14469689912eSchristos 		if (NXDOMAIN(found)) {
14479689912eSchristos 			result = DNS_R_NCACHENXDOMAIN;
14489689912eSchristos 		} else {
14499689912eSchristos 			result = DNS_R_NCACHENXRRSET;
14509689912eSchristos 		}
14519689912eSchristos 	}
14529689912eSchristos 
14539689912eSchristos 	update_cachestats(rbtdb, result);
14549689912eSchristos 
14559689912eSchristos 	return result;
14569689912eSchristos }
14579689912eSchristos 
14589689912eSchristos static size_t
14599689912eSchristos hashsize(dns_db_t *db) {
14609689912eSchristos 	dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
14619689912eSchristos 	size_t size;
14629689912eSchristos 	isc_rwlocktype_t tlocktype = isc_rwlocktype_none;
14639689912eSchristos 
14649689912eSchristos 	REQUIRE(VALID_RBTDB(rbtdb));
14659689912eSchristos 
14669689912eSchristos 	TREE_RDLOCK(&rbtdb->tree_lock, &tlocktype);
14679689912eSchristos 	size = dns_rbt_hashsize(rbtdb->tree);
14689689912eSchristos 	TREE_UNLOCK(&rbtdb->tree_lock, &tlocktype);
14699689912eSchristos 
14709689912eSchristos 	return size;
14719689912eSchristos }
14729689912eSchristos 
14739689912eSchristos static isc_result_t
14749689912eSchristos setcachestats(dns_db_t *db, isc_stats_t *stats) {
14759689912eSchristos 	dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
14769689912eSchristos 
14779689912eSchristos 	REQUIRE(VALID_RBTDB(rbtdb));
14789689912eSchristos 	REQUIRE(IS_CACHE(rbtdb)); /* current restriction */
14799689912eSchristos 	REQUIRE(stats != NULL);
14809689912eSchristos 
14819689912eSchristos 	isc_stats_attach(stats, &rbtdb->cachestats);
14829689912eSchristos 	return ISC_R_SUCCESS;
14839689912eSchristos }
14849689912eSchristos 
14859689912eSchristos static dns_stats_t *
14869689912eSchristos getrrsetstats(dns_db_t *db) {
14879689912eSchristos 	dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
14889689912eSchristos 
14899689912eSchristos 	REQUIRE(VALID_RBTDB(rbtdb));
14909689912eSchristos 	REQUIRE(IS_CACHE(rbtdb)); /* current restriction */
14919689912eSchristos 
14929689912eSchristos 	return rbtdb->rrsetstats;
14939689912eSchristos }
14949689912eSchristos 
14959689912eSchristos static isc_result_t
14969689912eSchristos setservestalettl(dns_db_t *db, dns_ttl_t ttl) {
14979689912eSchristos 	dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
14989689912eSchristos 
14999689912eSchristos 	REQUIRE(VALID_RBTDB(rbtdb));
15009689912eSchristos 	REQUIRE(IS_CACHE(rbtdb));
15019689912eSchristos 
15029689912eSchristos 	/* currently no bounds checking.  0 means disable. */
15039689912eSchristos 	rbtdb->common.serve_stale_ttl = ttl;
15049689912eSchristos 	return ISC_R_SUCCESS;
15059689912eSchristos }
15069689912eSchristos 
15079689912eSchristos static isc_result_t
15089689912eSchristos getservestalettl(dns_db_t *db, dns_ttl_t *ttl) {
15099689912eSchristos 	dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
15109689912eSchristos 
15119689912eSchristos 	REQUIRE(VALID_RBTDB(rbtdb));
15129689912eSchristos 	REQUIRE(IS_CACHE(rbtdb));
15139689912eSchristos 
15149689912eSchristos 	*ttl = rbtdb->common.serve_stale_ttl;
15159689912eSchristos 	return ISC_R_SUCCESS;
15169689912eSchristos }
15179689912eSchristos 
15189689912eSchristos static isc_result_t
15199689912eSchristos setservestalerefresh(dns_db_t *db, uint32_t interval) {
15209689912eSchristos 	dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
15219689912eSchristos 
15229689912eSchristos 	REQUIRE(VALID_RBTDB(rbtdb));
15239689912eSchristos 	REQUIRE(IS_CACHE(rbtdb));
15249689912eSchristos 
15259689912eSchristos 	/* currently no bounds checking.  0 means disable. */
15269689912eSchristos 	rbtdb->serve_stale_refresh = interval;
15279689912eSchristos 	return ISC_R_SUCCESS;
15289689912eSchristos }
15299689912eSchristos 
15309689912eSchristos static isc_result_t
15319689912eSchristos getservestalerefresh(dns_db_t *db, uint32_t *interval) {
15329689912eSchristos 	dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
15339689912eSchristos 
15349689912eSchristos 	REQUIRE(VALID_RBTDB(rbtdb));
15359689912eSchristos 	REQUIRE(IS_CACHE(rbtdb));
15369689912eSchristos 
15379689912eSchristos 	*interval = rbtdb->serve_stale_refresh;
15389689912eSchristos 	return ISC_R_SUCCESS;
15399689912eSchristos }
15409689912eSchristos 
15419689912eSchristos static void
15429689912eSchristos expiredata(dns_db_t *db, dns_dbnode_t *node, void *data) {
15439689912eSchristos 	dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
15449689912eSchristos 	dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node;
15459689912eSchristos 	dns_slabheader_t *header = data;
15469689912eSchristos 	isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
15479689912eSchristos 	isc_rwlocktype_t tlocktype = isc_rwlocktype_none;
15489689912eSchristos 
15499689912eSchristos 	NODE_WRLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype);
15509689912eSchristos 	dns__cacherbt_expireheader(header, &tlocktype,
15519689912eSchristos 				   dns_expire_flush DNS__DB_FILELINE);
15529689912eSchristos 	NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype);
15539689912eSchristos 	INSIST(tlocktype == isc_rwlocktype_none);
15549689912eSchristos }
15559689912eSchristos 
15569689912eSchristos dns_dbmethods_t dns__rbtdb_cachemethods = {
15579689912eSchristos 	.destroy = dns__rbtdb_destroy,
15589689912eSchristos 	.currentversion = dns__rbtdb_currentversion,
15599689912eSchristos 	.newversion = dns__rbtdb_newversion,
15609689912eSchristos 	.attachversion = dns__rbtdb_attachversion,
15619689912eSchristos 	.closeversion = dns__rbtdb_closeversion,
15629689912eSchristos 	.findnode = dns__rbtdb_findnode,
15639689912eSchristos 	.find = cache_find,
15649689912eSchristos 	.findzonecut = cache_findzonecut,
15659689912eSchristos 	.attachnode = dns__rbtdb_attachnode,
15669689912eSchristos 	.detachnode = dns__rbtdb_detachnode,
15679689912eSchristos 	.createiterator = dns__rbtdb_createiterator,
15689689912eSchristos 	.findrdataset = cache_findrdataset,
15699689912eSchristos 	.allrdatasets = dns__rbtdb_allrdatasets,
15709689912eSchristos 	.addrdataset = dns__rbtdb_addrdataset,
15719689912eSchristos 	.subtractrdataset = dns__rbtdb_subtractrdataset,
15729689912eSchristos 	.deleterdataset = dns__rbtdb_deleterdataset,
15739689912eSchristos 	.nodecount = dns__rbtdb_nodecount,
15749689912eSchristos 	.setloop = dns__rbtdb_setloop,
15759689912eSchristos 	.getoriginnode = dns__rbtdb_getoriginnode,
15769689912eSchristos 	.getrrsetstats = getrrsetstats,
15779689912eSchristos 	.setcachestats = setcachestats,
15789689912eSchristos 	.hashsize = hashsize,
15799689912eSchristos 	.setservestalettl = setservestalettl,
15809689912eSchristos 	.getservestalettl = getservestalettl,
15819689912eSchristos 	.setservestalerefresh = setservestalerefresh,
15829689912eSchristos 	.getservestalerefresh = getservestalerefresh,
15839689912eSchristos 	.locknode = dns__rbtdb_locknode,
15849689912eSchristos 	.unlocknode = dns__rbtdb_unlocknode,
15859689912eSchristos 	.expiredata = expiredata,
15869689912eSchristos 	.deletedata = dns__rbtdb_deletedata,
15879689912eSchristos 	.setmaxrrperset = dns__rbtdb_setmaxrrperset,
15889689912eSchristos 	.setmaxtypepername = dns__rbtdb_setmaxtypepername,
15899689912eSchristos };
15909689912eSchristos 
15919689912eSchristos /*
15929689912eSchristos  * Caller must hold the node (write) lock.
15939689912eSchristos  */
15949689912eSchristos void
15959689912eSchristos dns__cacherbt_expireheader(dns_slabheader_t *header,
15969689912eSchristos 			   isc_rwlocktype_t *tlocktypep,
15979689912eSchristos 			   dns_expire_t reason DNS__DB_FLARG) {
15989689912eSchristos 	dns__rbtdb_setttl(header, 0);
15999689912eSchristos 	dns__rbtdb_mark(header, DNS_SLABHEADERATTR_ANCIENT);
16009689912eSchristos 	RBTDB_HEADERNODE(header)->dirty = 1;
16019689912eSchristos 
16029689912eSchristos 	if (isc_refcount_current(&RBTDB_HEADERNODE(header)->references) == 0) {
16039689912eSchristos 		isc_rwlocktype_t nlocktype = isc_rwlocktype_write;
16049689912eSchristos 		dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)header->db;
16059689912eSchristos 
16069689912eSchristos 		/*
16079689912eSchristos 		 * If no one else is using the node, we can clean it up now.
16089689912eSchristos 		 * We first need to gain a new reference to the node to meet a
16099689912eSchristos 		 * requirement of dns__rbtdb_decref().
16109689912eSchristos 		 */
16119689912eSchristos 		dns__rbtdb_newref(rbtdb, RBTDB_HEADERNODE(header),
16129689912eSchristos 				  nlocktype DNS__DB_FLARG_PASS);
16139689912eSchristos 		dns__rbtdb_decref(rbtdb, RBTDB_HEADERNODE(header), 0,
16149689912eSchristos 				  &nlocktype, tlocktypep, true,
16159689912eSchristos 				  false DNS__DB_FLARG_PASS);
16169689912eSchristos 
16179689912eSchristos 		if (rbtdb->cachestats == NULL) {
16189689912eSchristos 			return;
16199689912eSchristos 		}
16209689912eSchristos 
16219689912eSchristos 		switch (reason) {
16229689912eSchristos 		case dns_expire_ttl:
16239689912eSchristos 			isc_stats_increment(rbtdb->cachestats,
16249689912eSchristos 					    dns_cachestatscounter_deletettl);
16259689912eSchristos 			break;
16269689912eSchristos 		case dns_expire_lru:
16279689912eSchristos 			isc_stats_increment(rbtdb->cachestats,
16289689912eSchristos 					    dns_cachestatscounter_deletelru);
16299689912eSchristos 			break;
16309689912eSchristos 		default:
16319689912eSchristos 			break;
16329689912eSchristos 		}
16339689912eSchristos 	}
16349689912eSchristos }
16359689912eSchristos 
16369689912eSchristos static size_t
16379689912eSchristos rdataset_size(dns_slabheader_t *header) {
16389689912eSchristos 	if (!NONEXISTENT(header)) {
16399689912eSchristos 		return dns_rdataslab_size((unsigned char *)header,
16409689912eSchristos 					  sizeof(*header));
16419689912eSchristos 	}
16429689912eSchristos 
16439689912eSchristos 	return sizeof(*header);
16449689912eSchristos }
16459689912eSchristos 
16469689912eSchristos static size_t
16479689912eSchristos expire_lru_headers(dns_rbtdb_t *rbtdb, unsigned int locknum,
16489689912eSchristos 		   isc_rwlocktype_t *tlocktypep,
16499689912eSchristos 		   size_t purgesize DNS__DB_FLARG) {
16509689912eSchristos 	dns_slabheader_t *header = NULL;
16519689912eSchristos 	size_t purged = 0;
16529689912eSchristos 
16539689912eSchristos 	for (header = ISC_LIST_TAIL(rbtdb->lru[locknum]);
16549689912eSchristos 	     header != NULL && header->last_used <= rbtdb->last_used &&
16559689912eSchristos 	     purged <= purgesize;
16569689912eSchristos 	     header = ISC_LIST_TAIL(rbtdb->lru[locknum]))
16579689912eSchristos 	{
16589689912eSchristos 		size_t header_size = rdataset_size(header);
16599689912eSchristos 
16609689912eSchristos 		/*
16619689912eSchristos 		 * Unlink the entry at this point to avoid checking it
16629689912eSchristos 		 * again even if it's currently used someone else and
16639689912eSchristos 		 * cannot be purged at this moment.  This entry won't be
16649689912eSchristos 		 * referenced any more (so unlinking is safe) since the
16659689912eSchristos 		 * TTL will be reset to 0.
16669689912eSchristos 		 */
16679689912eSchristos 		ISC_LIST_UNLINK(rbtdb->lru[locknum], header, link);
16689689912eSchristos 		dns__cacherbt_expireheader(header, tlocktypep,
16699689912eSchristos 					   dns_expire_lru DNS__DB_FLARG_PASS);
16709689912eSchristos 		purged += header_size;
16719689912eSchristos 	}
16729689912eSchristos 
16739689912eSchristos 	return purged;
16749689912eSchristos }
16759689912eSchristos 
16769689912eSchristos /*%
16779689912eSchristos  * Purge some expired and/or stale (i.e. unused for some period) cache entries
16789689912eSchristos  * due to an overmem condition.  To recover from this condition quickly,
16799689912eSchristos  * we clean up entries up to the size of newly added rdata that triggered
16809689912eSchristos  * the overmem; this is accessible via newheader.
16819689912eSchristos  *
16829689912eSchristos  * The LRU lists tails are processed in LRU order to the nearest second.
16839689912eSchristos  *
16849689912eSchristos  * A write lock on the tree must be held.
16859689912eSchristos  */
16869689912eSchristos void
16879689912eSchristos dns__cacherbt_overmem(dns_rbtdb_t *rbtdb, dns_slabheader_t *newheader,
16889689912eSchristos 		      isc_rwlocktype_t *tlocktypep DNS__DB_FLARG) {
16899689912eSchristos 	uint32_t locknum_start = rbtdb->lru_sweep++ % rbtdb->node_lock_count;
16909689912eSchristos 	uint32_t locknum = locknum_start;
16919689912eSchristos 	/* Size of added data, possible node and possible ENT node. */
16929689912eSchristos 	size_t purgesize =
16939689912eSchristos 		rdataset_size(newheader) +
16949689912eSchristos 		2 * dns__rbtnode_getsize(RBTDB_HEADERNODE(newheader));
16959689912eSchristos 	size_t purged = 0;
16969689912eSchristos 	isc_stdtime_t min_last_used = 0;
16979689912eSchristos 	size_t max_passes = 8;
16989689912eSchristos 
16999689912eSchristos again:
17009689912eSchristos 	do {
17019689912eSchristos 		isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
17029689912eSchristos 		NODE_WRLOCK(&rbtdb->node_locks[locknum].lock, &nlocktype);
17039689912eSchristos 
17049689912eSchristos 		purged += expire_lru_headers(rbtdb, locknum, tlocktypep,
17059689912eSchristos 					     purgesize -
17069689912eSchristos 						     purged DNS__DB_FLARG_PASS);
17079689912eSchristos 
17089689912eSchristos 		/*
17099689912eSchristos 		 * Work out the oldest remaining last_used values of the list
17109689912eSchristos 		 * tails as we walk across the array of lru lists.
17119689912eSchristos 		 */
17129689912eSchristos 		dns_slabheader_t *header = ISC_LIST_TAIL(rbtdb->lru[locknum]);
17139689912eSchristos 		if (header != NULL &&
17149689912eSchristos 		    (min_last_used == 0 || header->last_used < min_last_used))
17159689912eSchristos 		{
17169689912eSchristos 			min_last_used = header->last_used;
17179689912eSchristos 		}
17189689912eSchristos 		NODE_UNLOCK(&rbtdb->node_locks[locknum].lock, &nlocktype);
17199689912eSchristos 		locknum = (locknum + 1) % rbtdb->node_lock_count;
17209689912eSchristos 	} while (locknum != locknum_start && purged <= purgesize);
17219689912eSchristos 
17229689912eSchristos 	/*
17239689912eSchristos 	 * Update rbtdb->last_used if we have walked all the list tails and have
17249689912eSchristos 	 * not freed the required amount of memory.
17259689912eSchristos 	 */
17269689912eSchristos 	if (purged < purgesize) {
17279689912eSchristos 		if (min_last_used != 0) {
17289689912eSchristos 			rbtdb->last_used = min_last_used;
17299689912eSchristos 			if (max_passes-- > 0) {
17309689912eSchristos 				goto again;
17319689912eSchristos 			}
17329689912eSchristos 		}
17339689912eSchristos 	}
17349689912eSchristos }
1735