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