xref: /netbsd-src/external/mpl/bind/dist/lib/dns/qpzone.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1*bcda20f6Schristos /*	$NetBSD: qpzone.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/heap.h>
289689912eSchristos #include <isc/hex.h>
299689912eSchristos #include <isc/loop.h>
309689912eSchristos #include <isc/mem.h>
319689912eSchristos #include <isc/mutex.h>
329689912eSchristos #include <isc/once.h>
339689912eSchristos #include <isc/random.h>
349689912eSchristos #include <isc/refcount.h>
359689912eSchristos #include <isc/result.h>
369689912eSchristos #include <isc/rwlock.h>
379689912eSchristos #include <isc/serial.h>
389689912eSchristos #include <isc/stdio.h>
399689912eSchristos #include <isc/string.h>
409689912eSchristos #include <isc/time.h>
419689912eSchristos #include <isc/urcu.h>
429689912eSchristos #include <isc/util.h>
439689912eSchristos 
449689912eSchristos #include <dns/callbacks.h>
459689912eSchristos #include <dns/db.h>
469689912eSchristos #include <dns/dbiterator.h>
479689912eSchristos #include <dns/fixedname.h>
489689912eSchristos #include <dns/log.h>
499689912eSchristos #include <dns/masterdump.h>
509689912eSchristos #include <dns/name.h>
519689912eSchristos #include <dns/nsec.h>
529689912eSchristos #include <dns/nsec3.h>
539689912eSchristos #include <dns/qp.h>
549689912eSchristos #include <dns/rdata.h>
559689912eSchristos #include <dns/rdataset.h>
569689912eSchristos #include <dns/rdatasetiter.h>
579689912eSchristos #include <dns/rdataslab.h>
589689912eSchristos #include <dns/rdatastruct.h>
599689912eSchristos #include <dns/stats.h>
609689912eSchristos #include <dns/time.h>
619689912eSchristos #include <dns/view.h>
629689912eSchristos #include <dns/zone.h>
639689912eSchristos #include <dns/zonekey.h>
649689912eSchristos 
659689912eSchristos #include "db_p.h"
669689912eSchristos #include "qpzone_p.h"
679689912eSchristos 
689689912eSchristos #define CHECK(op)                            \
699689912eSchristos 	do {                                 \
709689912eSchristos 		result = (op);               \
719689912eSchristos 		if (result != ISC_R_SUCCESS) \
729689912eSchristos 			goto failure;        \
739689912eSchristos 	} while (0)
749689912eSchristos 
759689912eSchristos #define NONEXISTENT(header)                            \
769689912eSchristos 	((atomic_load_acquire(&(header)->attributes) & \
779689912eSchristos 	  DNS_SLABHEADERATTR_NONEXISTENT) != 0)
789689912eSchristos #define IGNORE(header)                                 \
799689912eSchristos 	((atomic_load_acquire(&(header)->attributes) & \
809689912eSchristos 	  DNS_SLABHEADERATTR_IGNORE) != 0)
819689912eSchristos #define RESIGN(header)                                 \
829689912eSchristos 	((atomic_load_acquire(&(header)->attributes) & \
839689912eSchristos 	  DNS_SLABHEADERATTR_RESIGN) != 0)
849689912eSchristos #define OPTOUT(header)                                 \
859689912eSchristos 	((atomic_load_acquire(&(header)->attributes) & \
869689912eSchristos 	  DNS_SLABHEADERATTR_OPTOUT) != 0)
879689912eSchristos #define STATCOUNT(header)                              \
889689912eSchristos 	((atomic_load_acquire(&(header)->attributes) & \
899689912eSchristos 	  DNS_SLABHEADERATTR_STATCOUNT) != 0)
909689912eSchristos 
919689912eSchristos #define HEADERNODE(h) ((qpznode_t *)((h)->node))
929689912eSchristos 
939689912eSchristos #define QPDB_ATTR_LOADED  0x01
949689912eSchristos #define QPDB_ATTR_LOADING 0x02
959689912eSchristos 
969689912eSchristos #define DEFAULT_NODE_LOCK_COUNT 7 /*%< Should be prime. */
979689912eSchristos 
989689912eSchristos #define QPDBITER_NSEC3_ORIGIN_NODE(qpdb, iterator)        \
999689912eSchristos 	((iterator)->current == &(iterator)->nsec3iter && \
1009689912eSchristos 	 (iterator)->node == (qpdb)->nsec3_origin)
1019689912eSchristos 
1029689912eSchristos /*%
1039689912eSchristos  * Note that "impmagic" is not the first four bytes of the struct, so
1049689912eSchristos  * ISC_MAGIC_VALID cannot be used.
1059689912eSchristos  */
1069689912eSchristos #define QPZONE_DB_MAGIC ISC_MAGIC('Q', 'Z', 'D', 'B')
1079689912eSchristos #define VALID_QPZONE(qpdb) \
1089689912eSchristos 	((qpdb) != NULL && (qpdb)->common.impmagic == QPZONE_DB_MAGIC)
1099689912eSchristos 
1109689912eSchristos typedef struct qpzonedb qpzonedb_t;
1119689912eSchristos typedef struct qpznode qpznode_t;
1129689912eSchristos 
1139689912eSchristos typedef struct qpz_changed {
1149689912eSchristos 	qpznode_t *node;
1159689912eSchristos 	bool dirty;
1169689912eSchristos 	ISC_LINK(struct qpz_changed) link;
1179689912eSchristos } qpz_changed_t;
1189689912eSchristos 
1199689912eSchristos typedef ISC_LIST(qpz_changed_t) qpz_changedlist_t;
1209689912eSchristos 
1219689912eSchristos typedef struct qpz_version qpz_version_t;
1229689912eSchristos struct qpz_version {
1239689912eSchristos 	/* Not locked */
1249689912eSchristos 	uint32_t serial;
1259689912eSchristos 	qpzonedb_t *qpdb;
1269689912eSchristos 	isc_refcount_t references;
1279689912eSchristos 	/* Locked by database lock. */
1289689912eSchristos 	bool writer;
1299689912eSchristos 	qpz_changedlist_t changed_list;
1309689912eSchristos 	dns_slabheaderlist_t resigned_list;
1319689912eSchristos 	ISC_LINK(qpz_version_t) link;
1329689912eSchristos 	bool secure;
1339689912eSchristos 	bool havensec3;
1349689912eSchristos 	/* NSEC3 parameters */
1359689912eSchristos 	dns_hash_t hash;
1369689912eSchristos 	uint8_t flags;
1379689912eSchristos 	uint16_t iterations;
1389689912eSchristos 	uint8_t salt_length;
1399689912eSchristos 	unsigned char salt[DNS_NSEC3_SALTSIZE];
1409689912eSchristos 
1419689912eSchristos 	/*
1429689912eSchristos 	 * records and xfrsize are covered by rwlock.
1439689912eSchristos 	 */
1449689912eSchristos 	isc_rwlock_t rwlock;
1459689912eSchristos 	uint64_t records;
1469689912eSchristos 	uint64_t xfrsize;
1479689912eSchristos 
1489689912eSchristos 	struct cds_lfht *glue_table;
1499689912eSchristos };
1509689912eSchristos 
1519689912eSchristos typedef ISC_LIST(qpz_version_t) qpz_versionlist_t;
1529689912eSchristos 
1539689912eSchristos struct qpznode {
1549689912eSchristos 	dns_name_t name;
1559689912eSchristos 	isc_mem_t *mctx;
1569689912eSchristos 	isc_refcount_t references;
1579689912eSchristos 	isc_refcount_t erefs;
1589689912eSchristos 	uint16_t locknum;
1599689912eSchristos 	void *data;
1609689912eSchristos 	atomic_uint_fast8_t nsec;
1619689912eSchristos 	atomic_bool wild;
1629689912eSchristos 	atomic_bool delegating;
1639689912eSchristos 	atomic_bool dirty;
1649689912eSchristos };
1659689912eSchristos 
1669689912eSchristos struct qpzonedb {
1679689912eSchristos 	/* Unlocked. */
1689689912eSchristos 	dns_db_t common;
1699689912eSchristos 	/* Locks the data in this struct */
1709689912eSchristos 	isc_rwlock_t lock;
1719689912eSchristos 	/* Locks for tree nodes */
1729689912eSchristos 	int node_lock_count;
1739689912eSchristos 	db_nodelock_t *node_locks;
1749689912eSchristos 	qpznode_t *origin;
1759689912eSchristos 	qpznode_t *nsec3_origin;
1769689912eSchristos 	isc_stats_t *gluecachestats;
1779689912eSchristos 	/* Locked by lock. */
1789689912eSchristos 	unsigned int active;
1799689912eSchristos 	unsigned int attributes;
1809689912eSchristos 	uint32_t current_serial;
1819689912eSchristos 	uint32_t least_serial;
1829689912eSchristos 	uint32_t next_serial;
1839689912eSchristos 	uint32_t maxrrperset;	 /* Maximum RRs per RRset */
1849689912eSchristos 	uint32_t maxtypepername; /* Maximum number of RR types per owner */
1859689912eSchristos 	qpz_version_t *current_version;
1869689912eSchristos 	qpz_version_t *future_version;
1879689912eSchristos 	qpz_versionlist_t open_versions;
1889689912eSchristos 	isc_loop_t *loop;
1899689912eSchristos 	struct rcu_head rcu_head;
1909689912eSchristos 
1919689912eSchristos 	isc_heap_t *heap; /* Resigning heap */
1929689912eSchristos 
1939689912eSchristos 	dns_qpmulti_t *tree;  /* Main QP trie for data storage */
1949689912eSchristos 	dns_qpmulti_t *nsec;  /* NSEC nodes only */
1959689912eSchristos 	dns_qpmulti_t *nsec3; /* NSEC3 nodes only */
1969689912eSchristos };
1979689912eSchristos 
1989689912eSchristos /*%
1999689912eSchristos  * Search Context
2009689912eSchristos  */
2019689912eSchristos typedef struct {
2029689912eSchristos 	qpzonedb_t *qpdb;
2039689912eSchristos 	qpz_version_t *version;
2049689912eSchristos 	dns_qpread_t qpr;
2059689912eSchristos 	uint32_t serial;
2069689912eSchristos 	unsigned int options;
2079689912eSchristos 	dns_qpchain_t chain;
2089689912eSchristos 	dns_qpiter_t iter;
2099689912eSchristos 	bool copy_name;
2109689912eSchristos 	bool need_cleanup;
2119689912eSchristos 	bool wild;
2129689912eSchristos 	qpznode_t *zonecut;
2139689912eSchristos 	dns_slabheader_t *zonecut_header;
2149689912eSchristos 	dns_slabheader_t *zonecut_sigheader;
2159689912eSchristos 	dns_fixedname_t zonecut_name;
2169689912eSchristos 	isc_stdtime_t now;
2179689912eSchristos } qpz_search_t;
2189689912eSchristos 
2199689912eSchristos typedef struct dns_gluenode_t {
2209689912eSchristos 	isc_mem_t *mctx;
2219689912eSchristos 
2229689912eSchristos 	struct dns_glue *glue;
2239689912eSchristos 
2249689912eSchristos 	qpznode_t *node;
2259689912eSchristos 
2269689912eSchristos 	struct cds_lfht_node ht_node;
2279689912eSchristos 	struct rcu_head rcu_head;
2289689912eSchristos } dns_gluenode_t;
2299689912eSchristos 
2309689912eSchristos /*%
2319689912eSchristos  * Load Context
2329689912eSchristos  */
2339689912eSchristos typedef struct {
2349689912eSchristos 	dns_db_t *db;
2359689912eSchristos 	isc_stdtime_t now;
2369689912eSchristos 	dns_qp_t *tree;
2379689912eSchristos 	dns_qp_t *nsec;
2389689912eSchristos 	dns_qp_t *nsec3;
2399689912eSchristos } qpz_load_t;
2409689912eSchristos 
2419689912eSchristos static dns_dbmethods_t qpdb_zonemethods;
2429689912eSchristos 
2439689912eSchristos #if DNS_DB_NODETRACE
2449689912eSchristos #define qpznode_ref(ptr)   qpznode__ref(ptr, __func__, __FILE__, __LINE__)
2459689912eSchristos #define qpznode_unref(ptr) qpznode__unref(ptr, __func__, __FILE__, __LINE__)
2469689912eSchristos #define qpznode_attach(ptr, ptrp) \
2479689912eSchristos 	qpznode__attach(ptr, ptrp, __func__, __FILE__, __LINE__)
2489689912eSchristos #define qpznode_detach(ptrp) qpznode__detach(ptrp, __func__, __FILE__, __LINE__)
2499689912eSchristos ISC_REFCOUNT_STATIC_TRACE_DECL(qpznode);
2509689912eSchristos #else
2519689912eSchristos ISC_REFCOUNT_STATIC_DECL(qpznode);
2529689912eSchristos #endif
2539689912eSchristos 
2549689912eSchristos /* QP trie methods */
2559689912eSchristos static void
2569689912eSchristos qp_attach(void *uctx, void *pval, uint32_t ival);
2579689912eSchristos static void
2589689912eSchristos qp_detach(void *uctx, void *pval, uint32_t ival);
2599689912eSchristos static size_t
2609689912eSchristos qp_makekey(dns_qpkey_t key, void *uctx, void *pval, uint32_t ival);
2619689912eSchristos static void
2629689912eSchristos qp_triename(void *uctx, char *buf, size_t size);
2639689912eSchristos 
2649689912eSchristos static dns_qpmethods_t qpmethods = {
2659689912eSchristos 	qp_attach,
2669689912eSchristos 	qp_detach,
2679689912eSchristos 	qp_makekey,
2689689912eSchristos 	qp_triename,
2699689912eSchristos };
2709689912eSchristos 
2719689912eSchristos static void
2729689912eSchristos rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp DNS__DB_FLARG);
2739689912eSchristos static isc_result_t
2749689912eSchristos rdatasetiter_first(dns_rdatasetiter_t *iterator DNS__DB_FLARG);
2759689912eSchristos static isc_result_t
2769689912eSchristos rdatasetiter_next(dns_rdatasetiter_t *iterator DNS__DB_FLARG);
2779689912eSchristos static void
2789689912eSchristos rdatasetiter_current(dns_rdatasetiter_t *iterator,
2799689912eSchristos 		     dns_rdataset_t *rdataset DNS__DB_FLARG);
2809689912eSchristos 
2819689912eSchristos static dns_rdatasetitermethods_t rdatasetiter_methods = {
2829689912eSchristos 	rdatasetiter_destroy, rdatasetiter_first, rdatasetiter_next,
2839689912eSchristos 	rdatasetiter_current
2849689912eSchristos };
2859689912eSchristos 
2869689912eSchristos typedef struct qpdb_rdatasetiter {
2879689912eSchristos 	dns_rdatasetiter_t common;
2889689912eSchristos 	dns_slabheader_t *current;
2899689912eSchristos } qpdb_rdatasetiter_t;
2909689912eSchristos 
2919689912eSchristos /*
2929689912eSchristos  * Note that these iterators, unless created with either DNS_DB_NSEC3ONLY
2939689912eSchristos  * or DNS_DB_NONSEC3, will transparently move between the last node of the
2949689912eSchristos  * "regular" QP trie and the root node of the NSEC3 QP trie of the database
2959689912eSchristos  * in question, as if the latter was a successor to the former in lexical
2969689912eSchristos  * order.  The "current" field always holds the address of either
2979689912eSchristos  * "mainiter" or "nsec3iter", depending on which trie is being traversed
2989689912eSchristos  * at given time.
2999689912eSchristos  */
3009689912eSchristos static void
3019689912eSchristos dbiterator_destroy(dns_dbiterator_t **iteratorp DNS__DB_FLARG);
3029689912eSchristos static isc_result_t
3039689912eSchristos dbiterator_first(dns_dbiterator_t *iterator DNS__DB_FLARG);
3049689912eSchristos static isc_result_t
3059689912eSchristos dbiterator_last(dns_dbiterator_t *iterator DNS__DB_FLARG);
3069689912eSchristos static isc_result_t
3079689912eSchristos dbiterator_seek(dns_dbiterator_t *iterator,
3089689912eSchristos 		const dns_name_t *name DNS__DB_FLARG);
3099689912eSchristos static isc_result_t
3109689912eSchristos dbiterator_prev(dns_dbiterator_t *iterator DNS__DB_FLARG);
3119689912eSchristos static isc_result_t
3129689912eSchristos dbiterator_next(dns_dbiterator_t *iterator DNS__DB_FLARG);
3139689912eSchristos static isc_result_t
3149689912eSchristos dbiterator_current(dns_dbiterator_t *iterator, dns_dbnode_t **nodep,
3159689912eSchristos 		   dns_name_t *name DNS__DB_FLARG);
3169689912eSchristos static isc_result_t
3179689912eSchristos dbiterator_pause(dns_dbiterator_t *iterator);
3189689912eSchristos static isc_result_t
3199689912eSchristos dbiterator_origin(dns_dbiterator_t *iterator, dns_name_t *name);
3209689912eSchristos 
3219689912eSchristos static dns_dbiteratormethods_t dbiterator_methods = {
3229689912eSchristos 	dbiterator_destroy, dbiterator_first, dbiterator_last,
3239689912eSchristos 	dbiterator_seek,    dbiterator_prev,  dbiterator_next,
3249689912eSchristos 	dbiterator_current, dbiterator_pause, dbiterator_origin
3259689912eSchristos };
3269689912eSchristos 
3279689912eSchristos typedef struct qpdb_dbiterator {
3289689912eSchristos 	dns_dbiterator_t common;
3299689912eSchristos 	isc_result_t result;
3309689912eSchristos 	dns_qpsnap_t *tsnap;	/* main tree snapshot */
3319689912eSchristos 	dns_qpsnap_t *nsnap;	/* nsec3 tree snapshot */
3329689912eSchristos 	dns_qpiter_t *current;	/* current iterator, which is one of: */
3339689912eSchristos 	dns_qpiter_t mainiter;	/* - main tree iterator */
3349689912eSchristos 	dns_qpiter_t nsec3iter; /* - nsec3 tree iterator */
3359689912eSchristos 	qpznode_t *node;
3369689912eSchristos 	enum { full, nonsec3, nsec3only } nsec3mode;
3379689912eSchristos } qpdb_dbiterator_t;
3389689912eSchristos 
3399689912eSchristos /*%
3409689912eSchristos  * 'init_count' is used to initialize 'newheader->count' which inturn
3419689912eSchristos  * is used to determine where in the cycle rrset-order cyclic starts.
3429689912eSchristos  * We don't lock this as we don't care about simultaneous updates.
3439689912eSchristos  */
3449689912eSchristos static atomic_uint_fast16_t init_count = 0;
3459689912eSchristos 
3469689912eSchristos /*
3479689912eSchristos  * Locking
3489689912eSchristos  *
3499689912eSchristos  * If a routine is going to lock more than one lock in this module, then
3509689912eSchristos  * the locking must be done in the following order:
3519689912eSchristos  *
3529689912eSchristos  *      Node Lock       (Only one from the set may be locked at one time by
3539689912eSchristos  *                       any caller)
3549689912eSchristos  *
3559689912eSchristos  *      Database Lock
3569689912eSchristos  *
3579689912eSchristos  * Failure to follow this hierarchy can result in deadlock.
3589689912eSchristos  */
3599689912eSchristos 
3609689912eSchristos /*%
3619689912eSchristos  * Return which RRset should be resigned sooner.  If the RRsets have the
3629689912eSchristos  * same signing time, prefer the other RRset over the SOA RRset.
3639689912eSchristos  */
3649689912eSchristos static bool
3659689912eSchristos resign_sooner(void *v1, void *v2) {
3669689912eSchristos 	dns_slabheader_t *h1 = v1;
3679689912eSchristos 	dns_slabheader_t *h2 = v2;
3689689912eSchristos 
3699689912eSchristos 	return h1->resign < h2->resign ||
3709689912eSchristos 	       (h1->resign == h2->resign && h1->resign_lsb < h2->resign_lsb) ||
3719689912eSchristos 	       (h1->resign == h2->resign && h1->resign_lsb == h2->resign_lsb &&
3729689912eSchristos 		h2->type == DNS_SIGTYPE(dns_rdatatype_soa));
3739689912eSchristos }
3749689912eSchristos 
3759689912eSchristos /*%
3769689912eSchristos  * This function sets the heap index into the header.
3779689912eSchristos  */
3789689912eSchristos static void
3799689912eSchristos set_index(void *what, unsigned int idx) {
3809689912eSchristos 	dns_slabheader_t *h = what;
3819689912eSchristos 
3829689912eSchristos 	h->heap_index = idx;
3839689912eSchristos }
3849689912eSchristos 
3859689912eSchristos static void
3869689912eSchristos free_gluenode(dns_gluenode_t *gluenode);
3879689912eSchristos 
3889689912eSchristos static void
3899689912eSchristos free_gluetable(struct cds_lfht *glue_table) {
3909689912eSchristos 	struct cds_lfht_iter iter;
3919689912eSchristos 	dns_gluenode_t *gluenode = NULL;
3929689912eSchristos 
3939689912eSchristos 	rcu_read_lock();
3949689912eSchristos 	cds_lfht_for_each_entry(glue_table, &iter, gluenode, ht_node) {
3959689912eSchristos 		INSIST(!cds_lfht_del(glue_table, &gluenode->ht_node));
3969689912eSchristos 		free_gluenode(gluenode);
3979689912eSchristos 	}
3989689912eSchristos 	rcu_read_unlock();
3999689912eSchristos 
4009689912eSchristos 	cds_lfht_destroy(glue_table, NULL);
4019689912eSchristos }
4029689912eSchristos 
4039689912eSchristos static void
4049689912eSchristos free_db_rcu(struct rcu_head *rcu_head) {
4059689912eSchristos 	qpzonedb_t *qpdb = caa_container_of(rcu_head, qpzonedb_t, rcu_head);
4069689912eSchristos 
4079689912eSchristos 	if (dns_name_dynamic(&qpdb->common.origin)) {
4089689912eSchristos 		dns_name_free(&qpdb->common.origin, qpdb->common.mctx);
4099689912eSchristos 	}
4109689912eSchristos 	for (int i = 0; i < qpdb->node_lock_count; i++) {
4119689912eSchristos 		isc_refcount_destroy(&qpdb->node_locks[i].references);
4129689912eSchristos 		NODE_DESTROYLOCK(&qpdb->node_locks[i].lock);
4139689912eSchristos 	}
4149689912eSchristos 
4159689912eSchristos 	isc_heap_destroy(&qpdb->heap);
4169689912eSchristos 
4179689912eSchristos 	if (qpdb->gluecachestats != NULL) {
4189689912eSchristos 		isc_stats_detach(&qpdb->gluecachestats);
4199689912eSchristos 	}
4209689912eSchristos 
4219689912eSchristos 	isc_mem_cput(qpdb->common.mctx, qpdb->node_locks, qpdb->node_lock_count,
4229689912eSchristos 		     sizeof(db_nodelock_t));
4239689912eSchristos 	isc_refcount_destroy(&qpdb->common.references);
4249689912eSchristos 	if (qpdb->loop != NULL) {
4259689912eSchristos 		isc_loop_detach(&qpdb->loop);
4269689912eSchristos 	}
4279689912eSchristos 
4289689912eSchristos 	isc_rwlock_destroy(&qpdb->lock);
4299689912eSchristos 	qpdb->common.magic = 0;
4309689912eSchristos 	qpdb->common.impmagic = 0;
4319689912eSchristos 
4329689912eSchristos 	if (qpdb->common.update_listeners != NULL) {
4339689912eSchristos 		INSIST(!cds_lfht_destroy(qpdb->common.update_listeners, NULL));
4349689912eSchristos 	}
4359689912eSchristos 
4369689912eSchristos 	isc_mem_putanddetach(&qpdb->common.mctx, qpdb, sizeof(*qpdb));
4379689912eSchristos }
4389689912eSchristos 
4399689912eSchristos static void
4409689912eSchristos free_qpdb(qpzonedb_t *qpdb, bool log) {
4419689912eSchristos 	REQUIRE(qpdb->future_version == NULL);
4429689912eSchristos 
4439689912eSchristos 	isc_refcount_decrementz(&qpdb->current_version->references);
4449689912eSchristos 
4459689912eSchristos 	isc_refcount_destroy(&qpdb->current_version->references);
4469689912eSchristos 	UNLINK(qpdb->open_versions, qpdb->current_version, link);
4479689912eSchristos 	isc_rwlock_destroy(&qpdb->current_version->rwlock);
4489689912eSchristos 	isc_mem_put(qpdb->common.mctx, qpdb->current_version,
4499689912eSchristos 		    sizeof(*qpdb->current_version));
4509689912eSchristos 
4519689912eSchristos 	dns_qpmulti_destroy(&qpdb->tree);
4529689912eSchristos 	dns_qpmulti_destroy(&qpdb->nsec);
4539689912eSchristos 	dns_qpmulti_destroy(&qpdb->nsec3);
4549689912eSchristos 
4559689912eSchristos 	if (log) {
4569689912eSchristos 		char buf[DNS_NAME_FORMATSIZE];
4579689912eSchristos 		if (dns_name_dynamic(&qpdb->common.origin)) {
4589689912eSchristos 			dns_name_format(&qpdb->common.origin, buf, sizeof(buf));
4599689912eSchristos 		} else {
4609689912eSchristos 			strlcpy(buf, "<UNKNOWN>", sizeof(buf));
4619689912eSchristos 		}
4629689912eSchristos 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
4639689912eSchristos 			      DNS_LOGMODULE_DB, ISC_LOG_DEBUG(1),
4649689912eSchristos 			      "called free_qpdb(%s)", buf);
4659689912eSchristos 	}
4669689912eSchristos 
4679689912eSchristos 	call_rcu(&qpdb->rcu_head, free_db_rcu);
4689689912eSchristos }
4699689912eSchristos 
4709689912eSchristos static void
4719689912eSchristos qpdb_destroy(dns_db_t *arg) {
4729689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)arg;
4739689912eSchristos 	unsigned int inactive = 0;
4749689912eSchristos 
4759689912eSchristos 	if (qpdb->origin != NULL) {
4769689912eSchristos 		qpznode_detach(&qpdb->origin);
4779689912eSchristos 	}
4789689912eSchristos 	if (qpdb->nsec3_origin != NULL) {
4799689912eSchristos 		qpznode_detach(&qpdb->nsec3_origin);
4809689912eSchristos 	}
4819689912eSchristos 
4829689912eSchristos 	/*
4839689912eSchristos 	 * The current version's glue table needs to be freed early
4849689912eSchristos 	 * so the nodes are dereferenced before we check the active
4859689912eSchristos 	 * node count below.
4869689912eSchristos 	 */
4879689912eSchristos 	if (qpdb->current_version != NULL) {
4889689912eSchristos 		free_gluetable(qpdb->current_version->glue_table);
4899689912eSchristos 	}
4909689912eSchristos 
4919689912eSchristos 	/*
4929689912eSchristos 	 * Even though there are no external direct references, there still
4939689912eSchristos 	 * may be nodes in use.
4949689912eSchristos 	 */
4959689912eSchristos 	for (int i = 0; i < qpdb->node_lock_count; i++) {
4969689912eSchristos 		isc_rwlocktype_t nodelock = isc_rwlocktype_none;
4979689912eSchristos 		NODE_WRLOCK(&qpdb->node_locks[i].lock, &nodelock);
4989689912eSchristos 		qpdb->node_locks[i].exiting = true;
4999689912eSchristos 		if (isc_refcount_current(&qpdb->node_locks[i].references) == 0)
5009689912eSchristos 		{
5019689912eSchristos 			inactive++;
5029689912eSchristos 		}
5039689912eSchristos 		NODE_UNLOCK(&qpdb->node_locks[i].lock, &nodelock);
5049689912eSchristos 	}
5059689912eSchristos 
5069689912eSchristos 	if (inactive != 0) {
5079689912eSchristos 		bool want_free = false;
5089689912eSchristos 
5099689912eSchristos 		RWLOCK(&qpdb->lock, isc_rwlocktype_write);
5109689912eSchristos 		qpdb->active -= inactive;
5119689912eSchristos 		if (qpdb->active == 0) {
5129689912eSchristos 			want_free = true;
5139689912eSchristos 		}
5149689912eSchristos 		RWUNLOCK(&qpdb->lock, isc_rwlocktype_write);
5159689912eSchristos 
5169689912eSchristos 		if (want_free) {
5179689912eSchristos 			char buf[DNS_NAME_FORMATSIZE];
5189689912eSchristos 			if (dns_name_dynamic(&qpdb->common.origin)) {
5199689912eSchristos 				dns_name_format(&qpdb->common.origin, buf,
5209689912eSchristos 						sizeof(buf));
5219689912eSchristos 			} else {
5229689912eSchristos 				strlcpy(buf, "<UNKNOWN>", sizeof(buf));
5239689912eSchristos 			}
5249689912eSchristos 			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
5259689912eSchristos 				      DNS_LOGMODULE_DB, ISC_LOG_DEBUG(1),
5269689912eSchristos 				      "calling free_qpdb(%s)", buf);
5279689912eSchristos 			free_qpdb(qpdb, true);
5289689912eSchristos 		}
5299689912eSchristos 	}
5309689912eSchristos }
5319689912eSchristos 
5329689912eSchristos static qpznode_t *
5339689912eSchristos new_qpznode(qpzonedb_t *qpdb, const dns_name_t *name) {
5349689912eSchristos 	qpznode_t *newdata = isc_mem_get(qpdb->common.mctx, sizeof(*newdata));
5359689912eSchristos 	*newdata = (qpznode_t){
5369689912eSchristos 		.name = DNS_NAME_INITEMPTY,
5379689912eSchristos 		.references = ISC_REFCOUNT_INITIALIZER(1),
5389689912eSchristos 	};
5399689912eSchristos 	newdata->locknum = dns_name_hash(name) % qpdb->node_lock_count;
5409689912eSchristos 	dns_name_dupwithoffsets(name, qpdb->common.mctx, &newdata->name);
5419689912eSchristos 	isc_mem_attach(qpdb->common.mctx, &newdata->mctx);
5429689912eSchristos 
5439689912eSchristos #if DNS_DB_NODETRACE
5449689912eSchristos 	fprintf(stderr, "new_qpznode:%s:%s:%d:%p->references = 1\n", __func__,
5459689912eSchristos 		__FILE__, __LINE__ + 1, name);
5469689912eSchristos #endif
5479689912eSchristos 	return newdata;
5489689912eSchristos }
5499689912eSchristos 
5509689912eSchristos static qpz_version_t *
5519689912eSchristos allocate_version(isc_mem_t *mctx, uint32_t serial, unsigned int references,
5529689912eSchristos 		 bool writer) {
5539689912eSchristos 	qpz_version_t *version = isc_mem_get(mctx, sizeof(*version));
5549689912eSchristos 	*version = (qpz_version_t){
5559689912eSchristos 		.serial = serial,
5569689912eSchristos 		.writer = writer,
5579689912eSchristos 		.changed_list = ISC_LIST_INITIALIZER,
5589689912eSchristos 		.resigned_list = ISC_LIST_INITIALIZER,
5599689912eSchristos 		.link = ISC_LINK_INITIALIZER,
5609689912eSchristos 		.glue_table = cds_lfht_new(GLUETABLE_INIT_SIZE,
5619689912eSchristos 					   GLUETABLE_MIN_SIZE, 0,
5629689912eSchristos 					   CDS_LFHT_AUTO_RESIZE, NULL),
5639689912eSchristos 	};
5649689912eSchristos 
5659689912eSchristos 	isc_rwlock_init(&version->rwlock);
5669689912eSchristos 	isc_refcount_init(&version->references, references);
5679689912eSchristos 
5689689912eSchristos 	return version;
5699689912eSchristos }
5709689912eSchristos 
5719689912eSchristos isc_result_t
5729689912eSchristos dns__qpzone_create(isc_mem_t *mctx, const dns_name_t *origin, dns_dbtype_t type,
5739689912eSchristos 		   dns_rdataclass_t rdclass, unsigned int argc ISC_ATTR_UNUSED,
5749689912eSchristos 		   char **argv ISC_ATTR_UNUSED, void *driverarg ISC_ATTR_UNUSED,
5759689912eSchristos 		   dns_db_t **dbp) {
5769689912eSchristos 	qpzonedb_t *qpdb = NULL;
5779689912eSchristos 	isc_result_t result;
5789689912eSchristos 	dns_qp_t *qp = NULL;
5799689912eSchristos 
5809689912eSchristos 	qpdb = isc_mem_get(mctx, sizeof(*qpdb));
5819689912eSchristos 	*qpdb = (qpzonedb_t){
5829689912eSchristos 		.common.origin = DNS_NAME_INITEMPTY,
5839689912eSchristos 		.common.rdclass = rdclass,
5849689912eSchristos 		.node_lock_count = DEFAULT_NODE_LOCK_COUNT,
5859689912eSchristos 		.current_serial = 1,
5869689912eSchristos 		.least_serial = 1,
5879689912eSchristos 		.next_serial = 2,
5889689912eSchristos 		.open_versions = ISC_LIST_INITIALIZER,
5899689912eSchristos 	};
5909689912eSchristos 
5919689912eSchristos 	isc_refcount_init(&qpdb->common.references, 1);
5929689912eSchristos 
5939689912eSchristos 	qpdb->common.methods = &qpdb_zonemethods;
5949689912eSchristos 	if (type == dns_dbtype_stub) {
5959689912eSchristos 		qpdb->common.attributes |= DNS_DBATTR_STUB;
5969689912eSchristos 	}
5979689912eSchristos 
5989689912eSchristos 	isc_rwlock_init(&qpdb->lock);
5999689912eSchristos 
6009689912eSchristos 	qpdb->node_locks = isc_mem_cget(mctx, qpdb->node_lock_count,
6019689912eSchristos 					sizeof(db_nodelock_t));
6029689912eSchristos 
6039689912eSchristos 	qpdb->common.update_listeners = cds_lfht_new(16, 16, 0, 0, NULL);
6049689912eSchristos 
6059689912eSchristos 	isc_heap_create(mctx, resign_sooner, set_index, 0, &qpdb->heap);
6069689912eSchristos 
6079689912eSchristos 	qpdb->active = qpdb->node_lock_count;
6089689912eSchristos 
6099689912eSchristos 	for (int i = 0; i < qpdb->node_lock_count; i++) {
6109689912eSchristos 		NODE_INITLOCK(&qpdb->node_locks[i].lock);
6119689912eSchristos 		isc_refcount_init(&qpdb->node_locks[i].references, 0);
6129689912eSchristos 		qpdb->node_locks[i].exiting = false;
6139689912eSchristos 	}
6149689912eSchristos 
6159689912eSchristos 	/*
6169689912eSchristos 	 * Attach to the mctx.  The database will persist so long as there
6179689912eSchristos 	 * are references to it, and attaching to the mctx ensures that our
6189689912eSchristos 	 * mctx won't disappear out from under us.
6199689912eSchristos 	 */
6209689912eSchristos 	isc_mem_attach(mctx, &qpdb->common.mctx);
6219689912eSchristos 
6229689912eSchristos 	/*
6239689912eSchristos 	 * Make a copy of the origin name.
6249689912eSchristos 	 */
6259689912eSchristos 	dns_name_dupwithoffsets(origin, mctx, &qpdb->common.origin);
6269689912eSchristos 
6279689912eSchristos 	dns_qpmulti_create(mctx, &qpmethods, qpdb, &qpdb->tree);
6289689912eSchristos 	dns_qpmulti_create(mctx, &qpmethods, qpdb, &qpdb->nsec);
6299689912eSchristos 	dns_qpmulti_create(mctx, &qpmethods, qpdb, &qpdb->nsec3);
6309689912eSchristos 
6319689912eSchristos 	/*
6329689912eSchristos 	 * Version initialization.
6339689912eSchristos 	 */
6349689912eSchristos 	qpdb->current_version = allocate_version(mctx, 1, 1, false);
6359689912eSchristos 	qpdb->current_version->qpdb = qpdb;
6369689912eSchristos 
6379689912eSchristos 	/*
6389689912eSchristos 	 * In order to set the node callback bit correctly in zone databases,
6399689912eSchristos 	 * we need to know if the node has the origin name of the zone.
6409689912eSchristos 	 * In loading_addrdataset() we could simply compare the new name
6419689912eSchristos 	 * to the origin name, but this is expensive.  Also, we don't know the
6429689912eSchristos 	 * node name in addrdataset(), so we need another way of knowing the
6439689912eSchristos 	 * zone's top.
6449689912eSchristos 	 *
6459689912eSchristos 	 * We now explicitly create a node for the zone's origin, and then
6469689912eSchristos 	 * we simply remember the node data's address.
6479689912eSchristos 	 */
6489689912eSchristos 
6499689912eSchristos 	dns_qpmulti_write(qpdb->tree, &qp);
6509689912eSchristos 	qpdb->origin = new_qpznode(qpdb, &qpdb->common.origin);
6519689912eSchristos 	result = dns_qp_insert(qp, qpdb->origin, 0);
6529689912eSchristos 	qpdb->origin->nsec = DNS_DB_NSEC_NORMAL;
6539689912eSchristos 	dns_qpmulti_commit(qpdb->tree, &qp);
6549689912eSchristos 
6559689912eSchristos 	if (result != ISC_R_SUCCESS) {
6569689912eSchristos 		INSIST(result != ISC_R_EXISTS);
6579689912eSchristos 		free_qpdb(qpdb, false);
6589689912eSchristos 		return result;
6599689912eSchristos 	}
6609689912eSchristos 
6619689912eSchristos 	/*
6629689912eSchristos 	 * Add an apex node to the NSEC3 tree so that NSEC3 searches
6639689912eSchristos 	 * return partial matches when there is only a single NSEC3
6649689912eSchristos 	 * record in the tree.
6659689912eSchristos 	 */
6669689912eSchristos 	dns_qpmulti_write(qpdb->nsec3, &qp);
6679689912eSchristos 	qpdb->nsec3_origin = new_qpznode(qpdb, &qpdb->common.origin);
6689689912eSchristos 	qpdb->nsec3_origin->nsec = DNS_DB_NSEC_NSEC3;
6699689912eSchristos 	result = dns_qp_insert(qp, qpdb->nsec3_origin, 0);
6709689912eSchristos 	dns_qpmulti_commit(qpdb->nsec3, &qp);
6719689912eSchristos 
6729689912eSchristos 	if (result != ISC_R_SUCCESS) {
6739689912eSchristos 		INSIST(result != ISC_R_EXISTS);
6749689912eSchristos 		free_qpdb(qpdb, false);
6759689912eSchristos 		return result;
6769689912eSchristos 	}
6779689912eSchristos 
6789689912eSchristos 	/*
6799689912eSchristos 	 * Keep the current version in the open list so that list operation
6809689912eSchristos 	 * won't happen in normal lookup operations.
6819689912eSchristos 	 */
6829689912eSchristos 	PREPEND(qpdb->open_versions, qpdb->current_version, link);
6839689912eSchristos 
6849689912eSchristos 	qpdb->common.magic = DNS_DB_MAGIC;
6859689912eSchristos 	qpdb->common.impmagic = QPZONE_DB_MAGIC;
6869689912eSchristos 
6879689912eSchristos 	*dbp = (dns_db_t *)qpdb;
6889689912eSchristos 
6899689912eSchristos 	return ISC_R_SUCCESS;
6909689912eSchristos }
6919689912eSchristos 
6929689912eSchristos static void
6939689912eSchristos newref(qpzonedb_t *qpdb, qpznode_t *node DNS__DB_FLARG) {
6949689912eSchristos 	uint_fast32_t refs;
6959689912eSchristos 
6969689912eSchristos 	qpznode_ref(node);
6979689912eSchristos 	refs = isc_refcount_increment0(&node->erefs);
6989689912eSchristos #if DNS_DB_NODETRACE
6999689912eSchristos 	fprintf(stderr, "incr:node:%s:%s:%u:%p->erefs = %" PRIuFAST32 "\n",
7009689912eSchristos 		func, file, line, node, refs + 1);
7019689912eSchristos #else
7029689912eSchristos 	UNUSED(refs);
7039689912eSchristos #endif
7049689912eSchristos 
7059689912eSchristos 	if (refs == 0) {
7069689912eSchristos 		/* this is the first reference to the node */
7079689912eSchristos 		refs = isc_refcount_increment0(
7089689912eSchristos 			&qpdb->node_locks[node->locknum].references);
7099689912eSchristos #if DNS_DB_NODETRACE
7109689912eSchristos 		fprintf(stderr,
7119689912eSchristos 			"incr:nodelock:%s:%s:%u:%p:%p->references = "
7129689912eSchristos 			"%" PRIuFAST32 "\n",
7139689912eSchristos 			func, file, line, node,
7149689912eSchristos 			&qpdb->node_locks[node->locknum], refs + 1);
7159689912eSchristos #else
7169689912eSchristos 		UNUSED(refs);
7179689912eSchristos #endif
7189689912eSchristos 	}
7199689912eSchristos }
7209689912eSchristos 
7219689912eSchristos static void
7229689912eSchristos clean_zone_node(qpznode_t *node, uint32_t least_serial) {
7239689912eSchristos 	dns_slabheader_t *current = NULL, *dcurrent = NULL;
7249689912eSchristos 	dns_slabheader_t *down_next = NULL, *dparent = NULL;
7259689912eSchristos 	dns_slabheader_t *top_prev = NULL, *top_next = NULL;
7269689912eSchristos 	bool still_dirty = false;
7279689912eSchristos 
7289689912eSchristos 	/*
7299689912eSchristos 	 * Caller must be holding the node lock.
7309689912eSchristos 	 */
7319689912eSchristos 	REQUIRE(least_serial != 0);
7329689912eSchristos 
7339689912eSchristos 	for (current = node->data; current != NULL; current = top_next) {
7349689912eSchristos 		top_next = current->next;
7359689912eSchristos 
7369689912eSchristos 		/*
7379689912eSchristos 		 * First, we clean up any instances of multiple rdatasets
7389689912eSchristos 		 * with the same serial number, or that have the IGNORE
7399689912eSchristos 		 * attribute.
7409689912eSchristos 		 */
7419689912eSchristos 		dparent = current;
7429689912eSchristos 		for (dcurrent = current->down; dcurrent != NULL;
7439689912eSchristos 		     dcurrent = down_next)
7449689912eSchristos 		{
7459689912eSchristos 			down_next = dcurrent->down;
7469689912eSchristos 			INSIST(dcurrent->serial <= dparent->serial);
7479689912eSchristos 			if (dcurrent->serial == dparent->serial ||
7489689912eSchristos 			    IGNORE(dcurrent))
7499689912eSchristos 			{
7509689912eSchristos 				if (down_next != NULL) {
7519689912eSchristos 					down_next->next = dparent;
7529689912eSchristos 				}
7539689912eSchristos 				dparent->down = down_next;
7549689912eSchristos 				dns_slabheader_destroy(&dcurrent);
7559689912eSchristos 			} else {
7569689912eSchristos 				dparent = dcurrent;
7579689912eSchristos 			}
7589689912eSchristos 		}
7599689912eSchristos 
7609689912eSchristos 		/*
7619689912eSchristos 		 * We've now eliminated all IGNORE datasets with the possible
7629689912eSchristos 		 * exception of current, which we now check.
7639689912eSchristos 		 */
7649689912eSchristos 		if (IGNORE(current)) {
7659689912eSchristos 			down_next = current->down;
7669689912eSchristos 			if (down_next == NULL) {
7679689912eSchristos 				if (top_prev != NULL) {
7689689912eSchristos 					top_prev->next = current->next;
7699689912eSchristos 				} else {
7709689912eSchristos 					node->data = current->next;
7719689912eSchristos 				}
7729689912eSchristos 				dns_slabheader_destroy(&current);
7739689912eSchristos 				/*
7749689912eSchristos 				 * current no longer exists, so we can
7759689912eSchristos 				 * just continue with the loop.
7769689912eSchristos 				 */
7779689912eSchristos 				continue;
7789689912eSchristos 			} else {
7799689912eSchristos 				/*
7809689912eSchristos 				 * Pull up current->down, making it the new
7819689912eSchristos 				 * current.
7829689912eSchristos 				 */
7839689912eSchristos 				if (top_prev != NULL) {
7849689912eSchristos 					top_prev->next = down_next;
7859689912eSchristos 				} else {
7869689912eSchristos 					node->data = down_next;
7879689912eSchristos 				}
7889689912eSchristos 				down_next->next = top_next;
7899689912eSchristos 				dns_slabheader_destroy(&current);
7909689912eSchristos 				current = down_next;
7919689912eSchristos 			}
7929689912eSchristos 		}
7939689912eSchristos 
7949689912eSchristos 		/*
7959689912eSchristos 		 * We now try to find the first down node less than the
7969689912eSchristos 		 * least serial.
7979689912eSchristos 		 */
7989689912eSchristos 		dparent = current;
7999689912eSchristos 		for (dcurrent = current->down; dcurrent != NULL;
8009689912eSchristos 		     dcurrent = down_next)
8019689912eSchristos 		{
8029689912eSchristos 			down_next = dcurrent->down;
8039689912eSchristos 			if (dcurrent->serial < least_serial) {
8049689912eSchristos 				break;
8059689912eSchristos 			}
8069689912eSchristos 			dparent = dcurrent;
8079689912eSchristos 		}
8089689912eSchristos 
8099689912eSchristos 		/*
8109689912eSchristos 		 * If there is a such an rdataset, delete it and any older
8119689912eSchristos 		 * versions.
8129689912eSchristos 		 */
8139689912eSchristos 		if (dcurrent != NULL) {
8149689912eSchristos 			do {
8159689912eSchristos 				down_next = dcurrent->down;
8169689912eSchristos 				INSIST(dcurrent->serial <= least_serial);
8179689912eSchristos 				dns_slabheader_destroy(&dcurrent);
8189689912eSchristos 				dcurrent = down_next;
8199689912eSchristos 			} while (dcurrent != NULL);
8209689912eSchristos 			dparent->down = NULL;
8219689912eSchristos 		}
8229689912eSchristos 
8239689912eSchristos 		/*
8249689912eSchristos 		 * Note.  The serial number of 'current' might be less than
8259689912eSchristos 		 * least_serial too, but we cannot delete it because it is
8269689912eSchristos 		 * the most recent version.
8279689912eSchristos 		 */
8289689912eSchristos 		if (current->down != NULL) {
8299689912eSchristos 			still_dirty = true;
8309689912eSchristos 		}
8319689912eSchristos 		top_prev = current;
8329689912eSchristos 	}
8339689912eSchristos 	if (!still_dirty) {
8349689912eSchristos 		node->dirty = false;
8359689912eSchristos 	}
8369689912eSchristos }
8379689912eSchristos 
8389689912eSchristos /*
8399689912eSchristos  * Caller must be holding the node lock; either the read or write lock.
8409689912eSchristos  * Note that the lock must be held even when node references are
8419689912eSchristos  * atomically modified; in that case the decrement operation itself does not
8429689912eSchristos  * have to be protected, but we must avoid a race condition where multiple
8439689912eSchristos  * threads are decreasing the reference to zero simultaneously and at least
8449689912eSchristos  * one of them is going to free the node.
8459689912eSchristos  *
8469689912eSchristos  * This decrements both the internal and external node reference counters.
8479689912eSchristos  * If the external reference count drops to zero, then the node lock
8489689912eSchristos  * reference count is also decremented.
8499689912eSchristos  *
8509689912eSchristos  * (NOTE: Decrementing the reference count of a node to zero does
8519689912eSchristos  * not mean it will be immediately freed.)
8529689912eSchristos  */
8539689912eSchristos static void
8549689912eSchristos decref(qpzonedb_t *qpdb, qpznode_t *node, uint32_t least_serial,
8559689912eSchristos        isc_rwlocktype_t *nlocktypep DNS__DB_FLARG) {
8569689912eSchristos 	db_nodelock_t *nodelock = NULL;
8579689912eSchristos 	int bucket = node->locknum;
8589689912eSchristos 	uint_fast32_t refs;
8599689912eSchristos 
8609689912eSchristos 	REQUIRE(*nlocktypep != isc_rwlocktype_none);
8619689912eSchristos 
8629689912eSchristos 	nodelock = &qpdb->node_locks[bucket];
8639689912eSchristos 
8649689912eSchristos 	/* Handle easy and typical case first. */
8659689912eSchristos 	if (!node->dirty && (node->data != NULL || node == qpdb->origin ||
8669689912eSchristos 			     node == qpdb->nsec3_origin))
8679689912eSchristos 	{
8689689912eSchristos 		refs = isc_refcount_decrement(&node->erefs);
8699689912eSchristos #if DNS_DB_NODETRACE
8709689912eSchristos 		fprintf(stderr,
8719689912eSchristos 			"decr:node:%s:%s:%u:%p->erefs = %" PRIuFAST32 "\n",
8729689912eSchristos 			func, file, line, node, refs - 1);
8739689912eSchristos #else
8749689912eSchristos 		UNUSED(refs);
8759689912eSchristos #endif
8769689912eSchristos 		if (refs == 1) {
8779689912eSchristos 			refs = isc_refcount_decrement(&nodelock->references);
8789689912eSchristos #if DNS_DB_NODETRACE
8799689912eSchristos 			fprintf(stderr,
8809689912eSchristos 				"decr:nodelock:%s:%s:%u:%p:%p->references = "
8819689912eSchristos 				"%" PRIuFAST32 "\n",
8829689912eSchristos 				func, file, line, node, nodelock, refs - 1);
8839689912eSchristos #else
8849689912eSchristos 			UNUSED(refs);
8859689912eSchristos #endif
8869689912eSchristos 		}
8879689912eSchristos 		goto done;
8889689912eSchristos 	}
8899689912eSchristos 
8909689912eSchristos 	/* Upgrade the lock? */
8919689912eSchristos 	if (*nlocktypep == isc_rwlocktype_read) {
8929689912eSchristos 		NODE_FORCEUPGRADE(&nodelock->lock, nlocktypep);
8939689912eSchristos 	}
8949689912eSchristos 
8959689912eSchristos 	refs = isc_refcount_decrement(&node->erefs);
8969689912eSchristos #if DNS_DB_NODETRACE
8979689912eSchristos 	fprintf(stderr, "decr:node:%s:%s:%u:%p->erefs = %" PRIuFAST32 "\n",
8989689912eSchristos 		func, file, line, node, refs - 1);
8999689912eSchristos #endif
9009689912eSchristos 	if (refs == 1) {
9019689912eSchristos 		if (node->dirty) {
9029689912eSchristos 			if (least_serial == 0) {
9039689912eSchristos 				/*
9049689912eSchristos 				 * Caller doesn't know the least serial.
9059689912eSchristos 				 * Get it.
9069689912eSchristos 				 */
9079689912eSchristos 				RWLOCK(&qpdb->lock, isc_rwlocktype_read);
9089689912eSchristos 				least_serial = qpdb->least_serial;
9099689912eSchristos 				RWUNLOCK(&qpdb->lock, isc_rwlocktype_read);
9109689912eSchristos 			}
9119689912eSchristos 			clean_zone_node(node, least_serial);
9129689912eSchristos 		}
9139689912eSchristos 
9149689912eSchristos 		refs = isc_refcount_decrement(&nodelock->references);
9159689912eSchristos #if DNS_DB_NODETRACE
9169689912eSchristos 		fprintf(stderr,
9179689912eSchristos 			"decr:nodelock:%s:%s:%u:%p:%p->references = "
9189689912eSchristos 			"%" PRIuFAST32 "\n",
9199689912eSchristos 			func, file, line, node, nodelock, refs - 1);
9209689912eSchristos #else
9219689912eSchristos 		UNUSED(refs);
9229689912eSchristos #endif
9239689912eSchristos 	}
9249689912eSchristos 
9259689912eSchristos done:
9269689912eSchristos 	qpznode_unref(node);
9279689912eSchristos }
9289689912eSchristos 
9299689912eSchristos static void
9309689912eSchristos bindrdataset(qpzonedb_t *qpdb, qpznode_t *node, dns_slabheader_t *header,
9319689912eSchristos 	     isc_stdtime_t now, dns_rdataset_t *rdataset DNS__DB_FLARG) {
9329689912eSchristos 	if (rdataset == NULL) {
9339689912eSchristos 		return;
9349689912eSchristos 	}
9359689912eSchristos 
9369689912eSchristos 	newref(qpdb, node DNS__DB_FLARG_PASS);
9379689912eSchristos 
9389689912eSchristos 	INSIST(rdataset->methods == NULL); /* We must be disassociated. */
9399689912eSchristos 
9409689912eSchristos 	rdataset->methods = &dns_rdataslab_rdatasetmethods;
9419689912eSchristos 	rdataset->rdclass = qpdb->common.rdclass;
9429689912eSchristos 	rdataset->type = DNS_TYPEPAIR_TYPE(header->type);
9439689912eSchristos 	rdataset->covers = DNS_TYPEPAIR_COVERS(header->type);
9449689912eSchristos 	rdataset->ttl = header->ttl - now;
9459689912eSchristos 	rdataset->trust = header->trust;
9469689912eSchristos 
9479689912eSchristos 	if (OPTOUT(header)) {
9489689912eSchristos 		rdataset->attributes |= DNS_RDATASETATTR_OPTOUT;
9499689912eSchristos 	}
9509689912eSchristos 
9519689912eSchristos 	rdataset->count = atomic_fetch_add_relaxed(&header->count, 1);
9529689912eSchristos 
9539689912eSchristos 	rdataset->slab.db = (dns_db_t *)qpdb;
9549689912eSchristos 	rdataset->slab.node = (dns_dbnode_t *)node;
9559689912eSchristos 	rdataset->slab.raw = dns_slabheader_raw(header);
9569689912eSchristos 	rdataset->slab.iter_pos = NULL;
9579689912eSchristos 	rdataset->slab.iter_count = 0;
9589689912eSchristos 
9599689912eSchristos 	/*
9609689912eSchristos 	 * Add noqname proof.
9619689912eSchristos 	 */
9629689912eSchristos 	rdataset->slab.noqname = header->noqname;
9639689912eSchristos 	if (header->noqname != NULL) {
9649689912eSchristos 		rdataset->attributes |= DNS_RDATASETATTR_NOQNAME;
9659689912eSchristos 	}
9669689912eSchristos 	rdataset->slab.closest = header->closest;
9679689912eSchristos 	if (header->closest != NULL) {
9689689912eSchristos 		rdataset->attributes |= DNS_RDATASETATTR_CLOSEST;
9699689912eSchristos 	}
9709689912eSchristos 
9719689912eSchristos 	/*
9729689912eSchristos 	 * Copy out re-signing information.
9739689912eSchristos 	 */
9749689912eSchristos 	if (RESIGN(header)) {
9759689912eSchristos 		rdataset->attributes |= DNS_RDATASETATTR_RESIGN;
9769689912eSchristos 		rdataset->resign = (header->resign << 1) | header->resign_lsb;
9779689912eSchristos 	} else {
9789689912eSchristos 		rdataset->resign = 0;
9799689912eSchristos 	}
9809689912eSchristos }
9819689912eSchristos 
9829689912eSchristos static void
9839689912eSchristos setnsec3parameters(dns_db_t *db, qpz_version_t *version) {
9849689912eSchristos 	qpznode_t *node = NULL;
9859689912eSchristos 	dns_rdata_nsec3param_t nsec3param;
9869689912eSchristos 	dns_rdata_t rdata = DNS_RDATA_INIT;
9879689912eSchristos 	isc_region_t region;
9889689912eSchristos 	isc_result_t result;
9899689912eSchristos 	dns_slabheader_t *header = NULL, *header_next = NULL;
9909689912eSchristos 	unsigned char *raw; /* RDATASLAB */
9919689912eSchristos 	unsigned int count, length;
9929689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)db;
9939689912eSchristos 	isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
9949689912eSchristos 
9959689912eSchristos 	version->havensec3 = false;
9969689912eSchristos 	node = qpdb->origin;
9979689912eSchristos 	NODE_RDLOCK(&(qpdb->node_locks[node->locknum].lock), &nlocktype);
9989689912eSchristos 	for (header = node->data; header != NULL; header = header_next) {
9999689912eSchristos 		header_next = header->next;
10009689912eSchristos 		do {
10019689912eSchristos 			if (header->serial <= version->serial &&
10029689912eSchristos 			    !IGNORE(header))
10039689912eSchristos 			{
10049689912eSchristos 				if (NONEXISTENT(header)) {
10059689912eSchristos 					header = NULL;
10069689912eSchristos 				}
10079689912eSchristos 				break;
10089689912eSchristos 			} else {
10099689912eSchristos 				header = header->down;
10109689912eSchristos 			}
10119689912eSchristos 		} while (header != NULL);
10129689912eSchristos 
10139689912eSchristos 		if (header != NULL &&
10149689912eSchristos 		    (header->type == dns_rdatatype_nsec3param))
10159689912eSchristos 		{
10169689912eSchristos 			/*
10179689912eSchristos 			 * Find an NSEC3PARAM with a supported algorithm.
10189689912eSchristos 			 */
10199689912eSchristos 			raw = dns_slabheader_raw(header);
10209689912eSchristos 			count = raw[0] * 256 + raw[1]; /* count */
10219689912eSchristos 			raw += DNS_RDATASET_COUNT + DNS_RDATASET_LENGTH;
10229689912eSchristos 			while (count-- > 0U) {
10239689912eSchristos 				length = raw[0] * 256 + raw[1];
10249689912eSchristos 				raw += DNS_RDATASET_ORDER + DNS_RDATASET_LENGTH;
10259689912eSchristos 				region.base = raw;
10269689912eSchristos 				region.length = length;
10279689912eSchristos 				raw += length;
10289689912eSchristos 				dns_rdata_fromregion(
10299689912eSchristos 					&rdata, qpdb->common.rdclass,
10309689912eSchristos 					dns_rdatatype_nsec3param, &region);
10319689912eSchristos 				result = dns_rdata_tostruct(&rdata, &nsec3param,
10329689912eSchristos 							    NULL);
10339689912eSchristos 				INSIST(result == ISC_R_SUCCESS);
10349689912eSchristos 				dns_rdata_reset(&rdata);
10359689912eSchristos 
10369689912eSchristos 				if (nsec3param.hash != DNS_NSEC3_UNKNOWNALG &&
10379689912eSchristos 				    !dns_nsec3_supportedhash(nsec3param.hash))
10389689912eSchristos 				{
10399689912eSchristos 					continue;
10409689912eSchristos 				}
10419689912eSchristos 
10429689912eSchristos 				if (nsec3param.flags != 0) {
10439689912eSchristos 					continue;
10449689912eSchristos 				}
10459689912eSchristos 
10469689912eSchristos 				memmove(version->salt, nsec3param.salt,
10479689912eSchristos 					nsec3param.salt_length);
10489689912eSchristos 				version->hash = nsec3param.hash;
10499689912eSchristos 				version->salt_length = nsec3param.salt_length;
10509689912eSchristos 				version->iterations = nsec3param.iterations;
10519689912eSchristos 				version->flags = nsec3param.flags;
10529689912eSchristos 				version->havensec3 = true;
10539689912eSchristos 				/*
10549689912eSchristos 				 * Look for a better algorithm than the
10559689912eSchristos 				 * unknown test algorithm.
10569689912eSchristos 				 */
10579689912eSchristos 				if (nsec3param.hash != DNS_NSEC3_UNKNOWNALG) {
10589689912eSchristos 					goto unlock;
10599689912eSchristos 				}
10609689912eSchristos 			}
10619689912eSchristos 		}
10629689912eSchristos 	}
10639689912eSchristos unlock:
10649689912eSchristos 	NODE_UNLOCK(&(qpdb->node_locks[node->locknum].lock), &nlocktype);
10659689912eSchristos }
10669689912eSchristos 
10679689912eSchristos static void
10689689912eSchristos cleanup_nondirty(qpz_version_t *version, qpz_changedlist_t *cleanup_list) {
10699689912eSchristos 	qpz_changed_t *changed = NULL, *next_changed = NULL;
10709689912eSchristos 
10719689912eSchristos 	/*
10729689912eSchristos 	 * If the changed record is dirty, then an update created multiple
10739689912eSchristos 	 * versions of a given rdataset.  We keep this list until we're the
10749689912eSchristos 	 * least open version, at which point it's safe to get rid of any
10759689912eSchristos 	 * older versions.
10769689912eSchristos 	 *
10779689912eSchristos 	 * If the changed record isn't dirty, then we don't need it anymore
10789689912eSchristos 	 * since we're committing and not rolling back.
10799689912eSchristos 	 *
10809689912eSchristos 	 * The caller must be holding the database lock.
10819689912eSchristos 	 */
10829689912eSchristos 	for (changed = HEAD(version->changed_list); changed != NULL;
10839689912eSchristos 	     changed = next_changed)
10849689912eSchristos 	{
10859689912eSchristos 		next_changed = NEXT(changed, link);
10869689912eSchristos 		if (!changed->dirty) {
10879689912eSchristos 			UNLINK(version->changed_list, changed, link);
10889689912eSchristos 			APPEND(*cleanup_list, changed, link);
10899689912eSchristos 		}
10909689912eSchristos 	}
10919689912eSchristos }
10929689912eSchristos 
10939689912eSchristos static void
10949689912eSchristos setsecure(dns_db_t *db, qpz_version_t *version, dns_dbnode_t *origin) {
10959689912eSchristos 	dns_rdataset_t keyset;
10969689912eSchristos 	dns_rdataset_t nsecset, signsecset;
10979689912eSchristos 	bool haszonekey = false;
10989689912eSchristos 	bool hasnsec = false;
10999689912eSchristos 	isc_result_t result;
11009689912eSchristos 
11019689912eSchristos 	dns_rdataset_init(&keyset);
11029689912eSchristos 	result = dns_db_findrdataset(db, origin, version, dns_rdatatype_dnskey,
11039689912eSchristos 				     0, 0, &keyset, NULL);
11049689912eSchristos 	if (result == ISC_R_SUCCESS) {
11059689912eSchristos 		result = dns_rdataset_first(&keyset);
11069689912eSchristos 		while (result == ISC_R_SUCCESS) {
11079689912eSchristos 			dns_rdata_t keyrdata = DNS_RDATA_INIT;
11089689912eSchristos 			dns_rdataset_current(&keyset, &keyrdata);
11099689912eSchristos 			if (dns_zonekey_iszonekey(&keyrdata)) {
11109689912eSchristos 				haszonekey = true;
11119689912eSchristos 				break;
11129689912eSchristos 			}
11139689912eSchristos 			result = dns_rdataset_next(&keyset);
11149689912eSchristos 		}
11159689912eSchristos 		dns_rdataset_disassociate(&keyset);
11169689912eSchristos 	}
11179689912eSchristos 	if (!haszonekey) {
11189689912eSchristos 		version->secure = false;
11199689912eSchristos 		version->havensec3 = false;
11209689912eSchristos 		return;
11219689912eSchristos 	}
11229689912eSchristos 
11239689912eSchristos 	dns_rdataset_init(&nsecset);
11249689912eSchristos 	dns_rdataset_init(&signsecset);
11259689912eSchristos 	result = dns_db_findrdataset(db, origin, version, dns_rdatatype_nsec, 0,
11269689912eSchristos 				     0, &nsecset, &signsecset);
11279689912eSchristos 	if (result == ISC_R_SUCCESS) {
11289689912eSchristos 		if (dns_rdataset_isassociated(&signsecset)) {
11299689912eSchristos 			hasnsec = true;
11309689912eSchristos 			dns_rdataset_disassociate(&signsecset);
11319689912eSchristos 		}
11329689912eSchristos 		dns_rdataset_disassociate(&nsecset);
11339689912eSchristos 	}
11349689912eSchristos 
11359689912eSchristos 	setnsec3parameters(db, version);
11369689912eSchristos 
11379689912eSchristos 	/*
11389689912eSchristos 	 * Do we have a valid NSEC/NSEC3 chain?
11399689912eSchristos 	 */
11409689912eSchristos 	if (version->havensec3 || hasnsec) {
11419689912eSchristos 		version->secure = true;
11429689912eSchristos 	} else {
11439689912eSchristos 		version->secure = false;
11449689912eSchristos 	}
11459689912eSchristos }
11469689912eSchristos 
11479689912eSchristos static void
11489689912eSchristos currentversion(dns_db_t *db, dns_dbversion_t **versionp) {
11499689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)db;
11509689912eSchristos 	qpz_version_t *version = NULL;
11519689912eSchristos 
11529689912eSchristos 	REQUIRE(VALID_QPZONE(qpdb));
11539689912eSchristos 
11549689912eSchristos 	RWLOCK(&qpdb->lock, isc_rwlocktype_read);
11559689912eSchristos 	version = qpdb->current_version;
11569689912eSchristos 	isc_refcount_increment(&version->references);
11579689912eSchristos 	RWUNLOCK(&qpdb->lock, isc_rwlocktype_read);
11589689912eSchristos 
11599689912eSchristos 	*versionp = (dns_dbversion_t *)version;
11609689912eSchristos }
11619689912eSchristos 
11629689912eSchristos static void
11639689912eSchristos attachversion(dns_db_t *db, dns_dbversion_t *source,
11649689912eSchristos 	      dns_dbversion_t **targetp) {
11659689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)db;
11669689912eSchristos 	qpz_version_t *version = source;
11679689912eSchristos 
11689689912eSchristos 	REQUIRE(VALID_QPZONE(qpdb));
11699689912eSchristos 	INSIST(version != NULL && version->qpdb == qpdb);
11709689912eSchristos 
11719689912eSchristos 	isc_refcount_increment(&version->references);
11729689912eSchristos 
11739689912eSchristos 	*targetp = version;
11749689912eSchristos }
11759689912eSchristos 
11769689912eSchristos static isc_result_t
11779689912eSchristos newversion(dns_db_t *db, dns_dbversion_t **versionp) {
11789689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)db;
11799689912eSchristos 	qpz_version_t *version = NULL;
11809689912eSchristos 
11819689912eSchristos 	REQUIRE(VALID_QPZONE(qpdb));
11829689912eSchristos 	REQUIRE(versionp != NULL && *versionp == NULL);
11839689912eSchristos 	REQUIRE(qpdb->future_version == NULL);
11849689912eSchristos 
11859689912eSchristos 	RWLOCK(&qpdb->lock, isc_rwlocktype_write);
11869689912eSchristos 	INSIST(qpdb->next_serial != 0);
11879689912eSchristos 	version = allocate_version(qpdb->common.mctx, qpdb->next_serial, 1,
11889689912eSchristos 				   true);
11899689912eSchristos 	version->qpdb = qpdb;
11909689912eSchristos 	version->secure = qpdb->current_version->secure;
11919689912eSchristos 	version->havensec3 = qpdb->current_version->havensec3;
11929689912eSchristos 	if (version->havensec3) {
11939689912eSchristos 		version->flags = qpdb->current_version->flags;
11949689912eSchristos 		version->iterations = qpdb->current_version->iterations;
11959689912eSchristos 		version->hash = qpdb->current_version->hash;
11969689912eSchristos 		version->salt_length = qpdb->current_version->salt_length;
11979689912eSchristos 		memmove(version->salt, qpdb->current_version->salt,
11989689912eSchristos 			version->salt_length);
11999689912eSchristos 	}
12009689912eSchristos 
12019689912eSchristos 	version->records = qpdb->current_version->records;
12029689912eSchristos 	version->xfrsize = qpdb->current_version->xfrsize;
12039689912eSchristos 
12049689912eSchristos 	qpdb->next_serial++;
12059689912eSchristos 	qpdb->future_version = version;
12069689912eSchristos 	RWUNLOCK(&qpdb->lock, isc_rwlocktype_write);
12079689912eSchristos 
12089689912eSchristos 	*versionp = version;
12099689912eSchristos 
12109689912eSchristos 	return ISC_R_SUCCESS;
12119689912eSchristos }
12129689912eSchristos 
12139689912eSchristos static void
12149689912eSchristos resigninsert(qpzonedb_t *qpdb, dns_slabheader_t *newheader) {
12159689912eSchristos 	REQUIRE(newheader->heap_index == 0);
12169689912eSchristos 	REQUIRE(!ISC_LINK_LINKED(newheader, link));
12179689912eSchristos 
12189689912eSchristos 	RWLOCK(&qpdb->lock, isc_rwlocktype_write);
12199689912eSchristos 	isc_heap_insert(qpdb->heap, newheader);
12209689912eSchristos 	RWUNLOCK(&qpdb->lock, isc_rwlocktype_write);
12219689912eSchristos 
12229689912eSchristos 	newheader->heap = qpdb->heap;
12239689912eSchristos }
12249689912eSchristos 
12259689912eSchristos static void
12269689912eSchristos resigndelete(qpzonedb_t *qpdb, qpz_version_t *version,
12279689912eSchristos 	     dns_slabheader_t *header DNS__DB_FLARG) {
12289689912eSchristos 	if (header == NULL || header->heap_index == 0) {
12299689912eSchristos 		return;
12309689912eSchristos 	}
12319689912eSchristos 
12329689912eSchristos 	RWLOCK(&qpdb->lock, isc_rwlocktype_write);
12339689912eSchristos 	isc_heap_delete(qpdb->heap, header->heap_index);
12349689912eSchristos 	RWUNLOCK(&qpdb->lock, isc_rwlocktype_write);
12359689912eSchristos 
12369689912eSchristos 	header->heap_index = 0;
12379689912eSchristos 	newref(qpdb, HEADERNODE(header) DNS__DB_FLARG_PASS);
12389689912eSchristos 	ISC_LIST_APPEND(version->resigned_list, header, link);
12399689912eSchristos }
12409689912eSchristos 
12419689912eSchristos static void
12429689912eSchristos make_least_version(qpzonedb_t *qpdb, qpz_version_t *version,
12439689912eSchristos 		   qpz_changedlist_t *cleanup_list) {
12449689912eSchristos 	qpdb->least_serial = version->serial;
12459689912eSchristos 	*cleanup_list = version->changed_list;
12469689912eSchristos 	ISC_LIST_INIT(version->changed_list);
12479689912eSchristos }
12489689912eSchristos 
12499689912eSchristos static void
12509689912eSchristos rollback_node(qpznode_t *node, uint32_t serial) {
12519689912eSchristos 	dns_slabheader_t *header = NULL, *dcurrent = NULL;
12529689912eSchristos 	bool make_dirty = false;
12539689912eSchristos 
12549689912eSchristos 	/*
12559689912eSchristos 	 * We set the IGNORE attribute on rdatasets with serial number
12569689912eSchristos 	 * 'serial'.  When the reference count goes to zero, these rdatasets
12579689912eSchristos 	 * will be cleaned up; until that time, they will be ignored.
12589689912eSchristos 	 */
12599689912eSchristos 	for (header = node->data; header != NULL; header = header->next) {
12609689912eSchristos 		if (header->serial == serial) {
12619689912eSchristos 			DNS_SLABHEADER_SETATTR(header,
12629689912eSchristos 					       DNS_SLABHEADERATTR_IGNORE);
12639689912eSchristos 			make_dirty = true;
12649689912eSchristos 		}
12659689912eSchristos 		for (dcurrent = header->down; dcurrent != NULL;
12669689912eSchristos 		     dcurrent = dcurrent->down)
12679689912eSchristos 		{
12689689912eSchristos 			if (dcurrent->serial == serial) {
12699689912eSchristos 				DNS_SLABHEADER_SETATTR(
12709689912eSchristos 					dcurrent, DNS_SLABHEADERATTR_IGNORE);
12719689912eSchristos 				make_dirty = true;
12729689912eSchristos 			}
12739689912eSchristos 		}
12749689912eSchristos 	}
12759689912eSchristos 	if (make_dirty) {
12769689912eSchristos 		node->dirty = true;
12779689912eSchristos 	}
12789689912eSchristos }
12799689912eSchristos 
12809689912eSchristos static void
12819689912eSchristos closeversion(dns_db_t *db, dns_dbversion_t **versionp,
12829689912eSchristos 	     bool commit DNS__DB_FLARG) {
12839689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)db;
12849689912eSchristos 	qpz_version_t *version = NULL, *cleanup_version = NULL;
12859689912eSchristos 	qpz_version_t *least_greater = NULL;
12869689912eSchristos 	qpznode_t *node = NULL;
12879689912eSchristos 	bool rollback = false;
12889689912eSchristos 	qpz_changed_t *changed = NULL, *next_changed = NULL;
12899689912eSchristos 	qpz_changedlist_t cleanup_list;
12909689912eSchristos 	dns_slabheaderlist_t resigned_list;
12919689912eSchristos 	dns_slabheader_t *header = NULL;
12929689912eSchristos 	uint32_t serial, least_serial;
12939689912eSchristos 
12949689912eSchristos 	REQUIRE(VALID_QPZONE(qpdb));
12959689912eSchristos 	version = (qpz_version_t *)*versionp;
12969689912eSchristos 	INSIST(version->qpdb == qpdb);
12979689912eSchristos 
12989689912eSchristos 	if (isc_refcount_decrement(&version->references) > 1) {
12999689912eSchristos 		*versionp = NULL;
13009689912eSchristos 		return;
13019689912eSchristos 	}
13029689912eSchristos 
13039689912eSchristos 	ISC_LIST_INIT(cleanup_list);
13049689912eSchristos 	ISC_LIST_INIT(resigned_list);
13059689912eSchristos 
13069689912eSchristos 	/*
13079689912eSchristos 	 * Update the zone's secure status in version before making
13089689912eSchristos 	 * it the current version.
13099689912eSchristos 	 */
13109689912eSchristos 	if (version->writer && commit) {
13119689912eSchristos 		setsecure(db, version, qpdb->origin);
13129689912eSchristos 	}
13139689912eSchristos 
13149689912eSchristos 	RWLOCK(&qpdb->lock, isc_rwlocktype_write);
13159689912eSchristos 	serial = version->serial;
13169689912eSchristos 	if (version->writer) {
13179689912eSchristos 		if (commit) {
13189689912eSchristos 			unsigned int cur_ref;
13199689912eSchristos 			qpz_version_t *cur_version = NULL;
13209689912eSchristos 
13219689912eSchristos 			INSIST(version == qpdb->future_version);
13229689912eSchristos 			/*
13239689912eSchristos 			 * The current version is going to be replaced.
13249689912eSchristos 			 * Release the (likely last) reference to it from the
13259689912eSchristos 			 * DB itself and unlink it from the open list.
13269689912eSchristos 			 */
13279689912eSchristos 			cur_version = qpdb->current_version;
13289689912eSchristos 			cur_ref = isc_refcount_decrement(
13299689912eSchristos 				&cur_version->references);
13309689912eSchristos 			if (cur_ref == 1) {
13319689912eSchristos 				(void)isc_refcount_current(
13329689912eSchristos 					&cur_version->references);
13339689912eSchristos 				if (cur_version->serial == qpdb->least_serial) {
13349689912eSchristos 					INSIST(EMPTY(
13359689912eSchristos 						cur_version->changed_list));
13369689912eSchristos 				}
13379689912eSchristos 				UNLINK(qpdb->open_versions, cur_version, link);
13389689912eSchristos 			}
13399689912eSchristos 			if (EMPTY(qpdb->open_versions)) {
13409689912eSchristos 				/*
13419689912eSchristos 				 * We're going to become the least open
13429689912eSchristos 				 * version.
13439689912eSchristos 				 */
13449689912eSchristos 				make_least_version(qpdb, version,
13459689912eSchristos 						   &cleanup_list);
13469689912eSchristos 			} else {
13479689912eSchristos 				/*
13489689912eSchristos 				 * Some other open version is the
13499689912eSchristos 				 * least version.  We can't cleanup
13509689912eSchristos 				 * records that were changed in this
13519689912eSchristos 				 * version because the older versions
13529689912eSchristos 				 * may still be in use by an open
13539689912eSchristos 				 * version.
13549689912eSchristos 				 *
13559689912eSchristos 				 * We can, however, discard the
13569689912eSchristos 				 * changed records for things that
13579689912eSchristos 				 * we've added that didn't exist in
13589689912eSchristos 				 * prior versions.
13599689912eSchristos 				 */
13609689912eSchristos 				cleanup_nondirty(version, &cleanup_list);
13619689912eSchristos 			}
13629689912eSchristos 			/*
13639689912eSchristos 			 * If the (soon to be former) current version
13649689912eSchristos 			 * isn't being used by anyone, we can clean
13659689912eSchristos 			 * it up.
13669689912eSchristos 			 */
13679689912eSchristos 			if (cur_ref == 1) {
13689689912eSchristos 				cleanup_version = cur_version;
13699689912eSchristos 				APPENDLIST(version->changed_list,
13709689912eSchristos 					   cleanup_version->changed_list, link);
13719689912eSchristos 			}
13729689912eSchristos 			/*
13739689912eSchristos 			 * Become the current version.
13749689912eSchristos 			 */
13759689912eSchristos 			version->writer = false;
13769689912eSchristos 			qpdb->current_version = version;
13779689912eSchristos 			qpdb->current_serial = version->serial;
13789689912eSchristos 			qpdb->future_version = NULL;
13799689912eSchristos 
13809689912eSchristos 			/*
13819689912eSchristos 			 * Keep the current version in the open list, and
13829689912eSchristos 			 * gain a reference for the DB itself (see the DB
13839689912eSchristos 			 * creation function below).  This must be the only
13849689912eSchristos 			 * case where we need to increment the counter from
13859689912eSchristos 			 * zero and need to use isc_refcount_increment0().
13869689912eSchristos 			 */
13879689912eSchristos 			INSIST(isc_refcount_increment0(&version->references) ==
13889689912eSchristos 			       0);
13899689912eSchristos 			PREPEND(qpdb->open_versions, qpdb->current_version,
13909689912eSchristos 				link);
13919689912eSchristos 			resigned_list = version->resigned_list;
13929689912eSchristos 			ISC_LIST_INIT(version->resigned_list);
13939689912eSchristos 		} else {
13949689912eSchristos 			/*
13959689912eSchristos 			 * We're rolling back this transaction.
13969689912eSchristos 			 */
13979689912eSchristos 			cleanup_list = version->changed_list;
13989689912eSchristos 			ISC_LIST_INIT(version->changed_list);
13999689912eSchristos 			resigned_list = version->resigned_list;
14009689912eSchristos 			ISC_LIST_INIT(version->resigned_list);
14019689912eSchristos 			rollback = true;
14029689912eSchristos 			cleanup_version = version;
14039689912eSchristos 			qpdb->future_version = NULL;
14049689912eSchristos 		}
14059689912eSchristos 	} else {
14069689912eSchristos 		if (version != qpdb->current_version) {
14079689912eSchristos 			/*
14089689912eSchristos 			 * There are no external or internal references
14099689912eSchristos 			 * to this version and it can be cleaned up.
14109689912eSchristos 			 */
14119689912eSchristos 			cleanup_version = version;
14129689912eSchristos 
14139689912eSchristos 			/*
14149689912eSchristos 			 * Find the version with the least serial
14159689912eSchristos 			 * number greater than ours.
14169689912eSchristos 			 */
14179689912eSchristos 			least_greater = PREV(version, link);
14189689912eSchristos 			if (least_greater == NULL) {
14199689912eSchristos 				least_greater = qpdb->current_version;
14209689912eSchristos 			}
14219689912eSchristos 
14229689912eSchristos 			INSIST(version->serial < least_greater->serial);
14239689912eSchristos 			/*
14249689912eSchristos 			 * Is this the least open version?
14259689912eSchristos 			 */
14269689912eSchristos 			if (version->serial == qpdb->least_serial) {
14279689912eSchristos 				/*
14289689912eSchristos 				 * Yes.  Install the new least open
14299689912eSchristos 				 * version.
14309689912eSchristos 				 */
14319689912eSchristos 				make_least_version(qpdb, least_greater,
14329689912eSchristos 						   &cleanup_list);
14339689912eSchristos 			} else {
14349689912eSchristos 				/*
14359689912eSchristos 				 * Add any unexecuted cleanups to
14369689912eSchristos 				 * those of the least greater version.
14379689912eSchristos 				 */
14389689912eSchristos 				APPENDLIST(least_greater->changed_list,
14399689912eSchristos 					   version->changed_list, link);
14409689912eSchristos 			}
14419689912eSchristos 		} else if (version->serial == qpdb->least_serial) {
14429689912eSchristos 			INSIST(EMPTY(version->changed_list));
14439689912eSchristos 		}
14449689912eSchristos 		UNLINK(qpdb->open_versions, version, link);
14459689912eSchristos 	}
14469689912eSchristos 	least_serial = qpdb->least_serial;
14479689912eSchristos 	RWUNLOCK(&qpdb->lock, isc_rwlocktype_write);
14489689912eSchristos 
14499689912eSchristos 	if (cleanup_version != NULL) {
14509689912eSchristos 		isc_refcount_destroy(&cleanup_version->references);
14519689912eSchristos 		INSIST(EMPTY(cleanup_version->changed_list));
14529689912eSchristos 		free_gluetable(cleanup_version->glue_table);
14539689912eSchristos 		isc_rwlock_destroy(&cleanup_version->rwlock);
14549689912eSchristos 		isc_mem_put(qpdb->common.mctx, cleanup_version,
14559689912eSchristos 			    sizeof(*cleanup_version));
14569689912eSchristos 	}
14579689912eSchristos 
14589689912eSchristos 	/*
14599689912eSchristos 	 * Commit/rollback re-signed headers.
14609689912eSchristos 	 */
14619689912eSchristos 	for (header = HEAD(resigned_list); header != NULL;
14629689912eSchristos 	     header = HEAD(resigned_list))
14639689912eSchristos 	{
14649689912eSchristos 		isc_rwlock_t *lock = NULL;
14659689912eSchristos 		isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
14669689912eSchristos 
14679689912eSchristos 		ISC_LIST_UNLINK(resigned_list, header, link);
14689689912eSchristos 
14699689912eSchristos 		lock = &qpdb->node_locks[HEADERNODE(header)->locknum].lock;
14709689912eSchristos 		NODE_WRLOCK(lock, &nlocktype);
14719689912eSchristos 		if (rollback && !IGNORE(header)) {
14729689912eSchristos 			resigninsert(qpdb, header);
14739689912eSchristos 		}
14749689912eSchristos 		decref(qpdb, HEADERNODE(header), least_serial,
14759689912eSchristos 		       &nlocktype DNS__DB_FLARG_PASS);
14769689912eSchristos 		NODE_UNLOCK(lock, &nlocktype);
14779689912eSchristos 	}
14789689912eSchristos 
14799689912eSchristos 	if (EMPTY(cleanup_list)) {
14809689912eSchristos 		*versionp = NULL;
14819689912eSchristos 		return;
14829689912eSchristos 	}
14839689912eSchristos 
14849689912eSchristos 	for (changed = HEAD(cleanup_list); changed != NULL;
14859689912eSchristos 	     changed = next_changed)
14869689912eSchristos 	{
14879689912eSchristos 		isc_rwlock_t *lock = NULL;
14889689912eSchristos 		isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
14899689912eSchristos 
14909689912eSchristos 		next_changed = NEXT(changed, link);
14919689912eSchristos 		node = changed->node;
14929689912eSchristos 		lock = &qpdb->node_locks[node->locknum].lock;
14939689912eSchristos 
14949689912eSchristos 		NODE_WRLOCK(lock, &nlocktype);
14959689912eSchristos 		if (rollback) {
14969689912eSchristos 			rollback_node(node, serial);
14979689912eSchristos 		}
14989689912eSchristos 		decref(qpdb, node, least_serial, &nlocktype DNS__DB_FILELINE);
14999689912eSchristos 
15009689912eSchristos 		NODE_UNLOCK(lock, &nlocktype);
15019689912eSchristos 
15029689912eSchristos 		isc_mem_put(qpdb->common.mctx, changed, sizeof(*changed));
15039689912eSchristos 	}
15049689912eSchristos 
15059689912eSchristos 	*versionp = NULL;
15069689912eSchristos }
15079689912eSchristos 
15089689912eSchristos static isc_result_t
15099689912eSchristos findrdataset(dns_db_t *db, dns_dbnode_t *dbnode, dns_dbversion_t *dbversion,
15109689912eSchristos 	     dns_rdatatype_t type, dns_rdatatype_t covers,
15119689912eSchristos 	     isc_stdtime_t now ISC_ATTR_UNUSED, dns_rdataset_t *rdataset,
15129689912eSchristos 	     dns_rdataset_t *sigrdataset DNS__DB_FLARG) {
15139689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)db;
15149689912eSchristos 	qpznode_t *node = (qpznode_t *)dbnode;
15159689912eSchristos 	dns_slabheader_t *header = NULL, *header_next = NULL;
15169689912eSchristos 	dns_slabheader_t *found = NULL, *foundsig = NULL;
15179689912eSchristos 	uint32_t serial;
15189689912eSchristos 	qpz_version_t *version = dbversion;
15199689912eSchristos 	bool close_version = false;
15209689912eSchristos 	dns_typepair_t matchtype, sigmatchtype;
15219689912eSchristos 	isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
15229689912eSchristos 
15239689912eSchristos 	REQUIRE(VALID_QPZONE(qpdb));
15249689912eSchristos 	REQUIRE(type != dns_rdatatype_any);
15259689912eSchristos 	INSIST(version == NULL || version->qpdb == qpdb);
15269689912eSchristos 
15279689912eSchristos 	if (version == NULL) {
15289689912eSchristos 		currentversion(db, (dns_dbversion_t **)&version);
15299689912eSchristos 		close_version = true;
15309689912eSchristos 	}
15319689912eSchristos 	serial = version->serial;
15329689912eSchristos 
15339689912eSchristos 	NODE_RDLOCK(&qpdb->node_locks[node->locknum].lock, &nlocktype);
15349689912eSchristos 
15359689912eSchristos 	matchtype = DNS_TYPEPAIR_VALUE(type, covers);
15369689912eSchristos 	if (covers == 0) {
15379689912eSchristos 		sigmatchtype = DNS_SIGTYPE(type);
15389689912eSchristos 	} else {
15399689912eSchristos 		sigmatchtype = 0;
15409689912eSchristos 	}
15419689912eSchristos 
15429689912eSchristos 	for (header = node->data; header != NULL; header = header_next) {
15439689912eSchristos 		header_next = header->next;
15449689912eSchristos 		do {
15459689912eSchristos 			if (header->serial <= serial && !IGNORE(header)) {
15469689912eSchristos 				if (NONEXISTENT(header)) {
15479689912eSchristos 					header = NULL;
15489689912eSchristos 				}
15499689912eSchristos 				break;
15509689912eSchristos 			} else {
15519689912eSchristos 				header = header->down;
15529689912eSchristos 			}
15539689912eSchristos 		} while (header != NULL);
15549689912eSchristos 		if (header != NULL) {
15559689912eSchristos 			/*
15569689912eSchristos 			 * We have an active, extant rdataset.  If it's a
15579689912eSchristos 			 * type we're looking for, remember it.
15589689912eSchristos 			 */
15599689912eSchristos 			if (header->type == matchtype) {
15609689912eSchristos 				found = header;
15619689912eSchristos 				if (foundsig != NULL) {
15629689912eSchristos 					break;
15639689912eSchristos 				}
15649689912eSchristos 			} else if (header->type == sigmatchtype) {
15659689912eSchristos 				foundsig = header;
15669689912eSchristos 				if (found != NULL) {
15679689912eSchristos 					break;
15689689912eSchristos 				}
15699689912eSchristos 			}
15709689912eSchristos 		}
15719689912eSchristos 	}
15729689912eSchristos 	if (found != NULL) {
15739689912eSchristos 		bindrdataset(qpdb, node, found, 0, rdataset DNS__DB_FLARG_PASS);
15749689912eSchristos 		if (foundsig != NULL) {
15759689912eSchristos 			bindrdataset(qpdb, node, foundsig, 0,
15769689912eSchristos 				     sigrdataset DNS__DB_FLARG_PASS);
15779689912eSchristos 		}
15789689912eSchristos 	}
15799689912eSchristos 
15809689912eSchristos 	NODE_UNLOCK(&qpdb->node_locks[node->locknum].lock, &nlocktype);
15819689912eSchristos 
15829689912eSchristos 	if (close_version) {
15839689912eSchristos 		closeversion(db, (dns_dbversion_t **)&version,
15849689912eSchristos 			     false DNS__DB_FLARG_PASS);
15859689912eSchristos 	}
15869689912eSchristos 
15879689912eSchristos 	if (found == NULL) {
15889689912eSchristos 		return ISC_R_NOTFOUND;
15899689912eSchristos 	}
15909689912eSchristos 
15919689912eSchristos 	return ISC_R_SUCCESS;
15929689912eSchristos }
15939689912eSchristos 
15949689912eSchristos static bool
15959689912eSchristos delegating_type(qpzonedb_t *qpdb, qpznode_t *node, dns_typepair_t type) {
15969689912eSchristos 	return type == dns_rdatatype_dname ||
15979689912eSchristos 	       (type == dns_rdatatype_ns &&
15989689912eSchristos 		(node != qpdb->origin || IS_STUB(qpdb)));
15999689912eSchristos }
16009689912eSchristos 
16019689912eSchristos static void
16029689912eSchristos loading_addnode(qpz_load_t *loadctx, const dns_name_t *name,
16039689912eSchristos 		dns_rdatatype_t type, dns_rdatatype_t covers,
16049689912eSchristos 		qpznode_t **nodep) {
16059689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)loadctx->db;
16069689912eSchristos 	isc_result_t result;
16079689912eSchristos 	qpznode_t *node = NULL, *nsecnode = NULL;
16089689912eSchristos 
16099689912eSchristos 	if (type == dns_rdatatype_nsec3 || covers == dns_rdatatype_nsec3) {
16109689912eSchristos 		result = dns_qp_getname(loadctx->nsec3, name, (void **)&node,
16119689912eSchristos 					NULL);
16129689912eSchristos 		if (result == ISC_R_SUCCESS) {
16139689912eSchristos 			*nodep = node;
16149689912eSchristos 		} else {
16159689912eSchristos 			node = new_qpznode(qpdb, name);
16169689912eSchristos 			result = dns_qp_insert(loadctx->nsec3, node, 0);
16179689912eSchristos 			INSIST(result == ISC_R_SUCCESS);
16189689912eSchristos 			node->nsec = DNS_DB_NSEC_NSEC3;
16199689912eSchristos 			*nodep = node;
16209689912eSchristos 			qpznode_detach(&node);
16219689912eSchristos 		}
16229689912eSchristos 		return;
16239689912eSchristos 	}
16249689912eSchristos 
16259689912eSchristos 	result = dns_qp_getname(loadctx->tree, name, (void **)&node, NULL);
16269689912eSchristos 	if (result == ISC_R_SUCCESS) {
16279689912eSchristos 		if (type == dns_rdatatype_nsec &&
16289689912eSchristos 		    node->nsec == DNS_DB_NSEC_HAS_NSEC)
16299689912eSchristos 		{
16309689912eSchristos 			goto done;
16319689912eSchristos 		}
16329689912eSchristos 	} else {
16339689912eSchristos 		INSIST(node == NULL);
16349689912eSchristos 		node = new_qpznode(qpdb, name);
16359689912eSchristos 		result = dns_qp_insert(loadctx->tree, node, 0);
16369689912eSchristos 		INSIST(result == ISC_R_SUCCESS);
16379689912eSchristos 		qpznode_unref(node);
16389689912eSchristos 	}
16399689912eSchristos 	if (type != dns_rdatatype_nsec) {
16409689912eSchristos 		goto done;
16419689912eSchristos 	}
16429689912eSchristos 
16439689912eSchristos 	/*
16449689912eSchristos 	 * We're adding an NSEC record, so create a node in the nsec tree
16459689912eSchristos 	 * too. This tree speeds searches for closest NSECs that would
16469689912eSchristos 	 * otherwise need to examine many irrelevant nodes in large TLDs.
16479689912eSchristos 	 */
16489689912eSchristos 	nsecnode = new_qpznode(qpdb, name);
16499689912eSchristos 	result = dns_qp_insert(loadctx->nsec, nsecnode, 0);
16509689912eSchristos 	node->nsec = DNS_DB_NSEC_HAS_NSEC;
16519689912eSchristos 	if (result == ISC_R_SUCCESS) {
16529689912eSchristos 		nsecnode->nsec = DNS_DB_NSEC_NSEC;
16539689912eSchristos 	}
16549689912eSchristos 	qpznode_detach(&nsecnode);
16559689912eSchristos 
16569689912eSchristos done:
16579689912eSchristos 	*nodep = node;
16589689912eSchristos }
16599689912eSchristos 
16609689912eSchristos static bool
16619689912eSchristos cname_and_other(qpznode_t *node, uint32_t serial) {
16629689912eSchristos 	dns_slabheader_t *header = NULL, *header_next = NULL;
16639689912eSchristos 	bool cname = false, other = false;
16649689912eSchristos 	dns_rdatatype_t rdtype;
16659689912eSchristos 
16669689912eSchristos 	/*
16679689912eSchristos 	 * Look for CNAME and "other data" rdatasets active in our version.
16689689912eSchristos 	 * ("Other data" is any rdataset whose type is not KEY, NSEC, SIG
16699689912eSchristos 	 * or RRSIG.
16709689912eSchristos 	 */
16719689912eSchristos 	for (header = node->data; header != NULL; header = header_next) {
16729689912eSchristos 		header_next = header->next;
16739689912eSchristos 
16749689912eSchristos 		if (!prio_type(header->type)) {
16759689912eSchristos 			/*
16769689912eSchristos 			 * CNAME is in the priority list, so if we are done
16779689912eSchristos 			 * with priority types, we know there will not be a
16789689912eSchristos 			 * CNAME, and are safe to skip the rest.
16799689912eSchristos 			 */
16809689912eSchristos 			return false;
16819689912eSchristos 		}
16829689912eSchristos 
16839689912eSchristos 		rdtype = DNS_TYPEPAIR_TYPE(header->type);
16849689912eSchristos 		if (rdtype == dns_rdatatype_cname) {
16859689912eSchristos 			do {
16869689912eSchristos 				if (header->serial <= serial && !IGNORE(header))
16879689912eSchristos 				{
16889689912eSchristos 					if (NONEXISTENT(header)) {
16899689912eSchristos 						header = NULL;
16909689912eSchristos 					}
16919689912eSchristos 					break;
16929689912eSchristos 				}
16939689912eSchristos 				header = header->down;
16949689912eSchristos 			} while (header != NULL);
16959689912eSchristos 			if (header != NULL) {
16969689912eSchristos 				cname = true;
16979689912eSchristos 			}
16989689912eSchristos 		} else if (rdtype != dns_rdatatype_key &&
16999689912eSchristos 			   rdtype != dns_rdatatype_sig &&
17009689912eSchristos 			   rdtype != dns_rdatatype_nsec &&
17019689912eSchristos 			   rdtype != dns_rdatatype_rrsig)
17029689912eSchristos 		{
17039689912eSchristos 			do {
17049689912eSchristos 				if (header->serial <= serial && !IGNORE(header))
17059689912eSchristos 				{
17069689912eSchristos 					if (NONEXISTENT(header)) {
17079689912eSchristos 						header = NULL;
17089689912eSchristos 					}
17099689912eSchristos 					break;
17109689912eSchristos 				}
17119689912eSchristos 				header = header->down;
17129689912eSchristos 			} while (header != NULL);
17139689912eSchristos 			if (header != NULL) {
17149689912eSchristos 				other = true;
17159689912eSchristos 			}
17169689912eSchristos 		}
17179689912eSchristos 
17189689912eSchristos 		if (cname && other) {
17199689912eSchristos 			return true;
17209689912eSchristos 		}
17219689912eSchristos 	}
17229689912eSchristos 
17239689912eSchristos 	return false;
17249689912eSchristos }
17259689912eSchristos 
17269689912eSchristos static qpz_changed_t *
17279689912eSchristos add_changed(dns_slabheader_t *header, qpz_version_t *version DNS__DB_FLARG) {
17289689912eSchristos 	qpz_changed_t *changed = NULL;
17299689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)header->db;
17309689912eSchristos 	qpznode_t *node = (qpznode_t *)header->node;
17319689912eSchristos 
17329689912eSchristos 	changed = isc_mem_get(qpdb->common.mctx, sizeof(*changed));
17339689912eSchristos 
17349689912eSchristos 	RWLOCK(&qpdb->lock, isc_rwlocktype_write);
17359689912eSchristos 	REQUIRE(version->writer);
17369689912eSchristos 
17379689912eSchristos 	*changed = (qpz_changed_t){ .node = node };
17389689912eSchristos 	ISC_LIST_INITANDAPPEND(version->changed_list, changed, link);
17399689912eSchristos 	newref(qpdb, node DNS__DB_FLARG_PASS);
17409689912eSchristos 	RWUNLOCK(&qpdb->lock, isc_rwlocktype_write);
17419689912eSchristos 
17429689912eSchristos 	return changed;
17439689912eSchristos }
17449689912eSchristos 
17459689912eSchristos static uint64_t
17469689912eSchristos recordsize(dns_slabheader_t *header, unsigned int namelen) {
17479689912eSchristos 	return dns_rdataslab_rdatasize((unsigned char *)header,
17489689912eSchristos 				       sizeof(*header)) +
17499689912eSchristos 	       sizeof(dns_ttl_t) + sizeof(dns_rdatatype_t) +
17509689912eSchristos 	       sizeof(dns_rdataclass_t) + namelen;
17519689912eSchristos }
17529689912eSchristos 
17539689912eSchristos static void
17549689912eSchristos maybe_update_recordsandsize(bool add, qpz_version_t *version,
17559689912eSchristos 			    dns_slabheader_t *header, unsigned int namelen) {
17569689912eSchristos 	unsigned char *hdr = (unsigned char *)header;
17579689912eSchristos 	size_t hdrsize = sizeof(*header);
17589689912eSchristos 
17599689912eSchristos 	if (NONEXISTENT(header)) {
17609689912eSchristos 		return;
17619689912eSchristos 	}
17629689912eSchristos 
17639689912eSchristos 	RWLOCK(&version->rwlock, isc_rwlocktype_write);
17649689912eSchristos 	if (add) {
17659689912eSchristos 		version->records += dns_rdataslab_count(hdr, hdrsize);
17669689912eSchristos 		version->xfrsize += recordsize(header, namelen);
17679689912eSchristos 	} else {
17689689912eSchristos 		version->records -= dns_rdataslab_count(hdr, hdrsize);
17699689912eSchristos 		version->xfrsize -= recordsize(header, namelen);
17709689912eSchristos 	}
17719689912eSchristos 	RWUNLOCK(&version->rwlock, isc_rwlocktype_write);
17729689912eSchristos }
17739689912eSchristos 
17749689912eSchristos static isc_result_t
17759689912eSchristos add(qpzonedb_t *qpdb, qpznode_t *node, const dns_name_t *nodename,
17769689912eSchristos     qpz_version_t *version, dns_slabheader_t *newheader, unsigned int options,
17779689912eSchristos     bool loading, dns_rdataset_t *addedrdataset,
17789689912eSchristos     isc_stdtime_t now DNS__DB_FLARG) {
17799689912eSchristos 	qpz_changed_t *changed = NULL;
17809689912eSchristos 	dns_slabheader_t *topheader = NULL, *topheader_prev = NULL;
17819689912eSchristos 	dns_slabheader_t *prioheader = NULL;
17829689912eSchristos 	dns_slabheader_t *header = NULL;
17839689912eSchristos 	unsigned char *merged = NULL;
17849689912eSchristos 	isc_result_t result;
17859689912eSchristos 	bool merge = false;
17869689912eSchristos 	uint32_t ntypes;
17879689912eSchristos 
17889689912eSchristos 	if ((options & DNS_DBADD_MERGE) != 0) {
17899689912eSchristos 		REQUIRE(version != NULL);
17909689912eSchristos 		merge = true;
17919689912eSchristos 	}
17929689912eSchristos 
17939689912eSchristos 	if (!loading) {
17949689912eSchristos 		/*
17959689912eSchristos 		 * We always add a changed record, even if no changes end up
17969689912eSchristos 		 * being made to this node, because it's harmless and
17979689912eSchristos 		 * simplifies the code.
17989689912eSchristos 		 */
17999689912eSchristos 		changed = add_changed(newheader, version DNS__DB_FLARG_PASS);
18009689912eSchristos 	}
18019689912eSchristos 
18029689912eSchristos 	ntypes = 0;
18039689912eSchristos 	for (topheader = node->data; topheader != NULL;
18049689912eSchristos 	     topheader = topheader->next)
18059689912eSchristos 	{
18069689912eSchristos 		++ntypes;
18079689912eSchristos 		if (prio_type(topheader->type)) {
18089689912eSchristos 			prioheader = topheader;
18099689912eSchristos 		}
18109689912eSchristos 		if (topheader->type == newheader->type) {
18119689912eSchristos 			break;
18129689912eSchristos 		}
18139689912eSchristos 		topheader_prev = topheader;
18149689912eSchristos 	}
18159689912eSchristos 
18169689912eSchristos 	/*
18179689912eSchristos 	 * If topheader isn't NULL, we've found the right type.  There may be
18189689912eSchristos 	 * IGNORE rdatasets between the top of the chain and the first real
18199689912eSchristos 	 * data.  We skip over them.
18209689912eSchristos 	 */
18219689912eSchristos 	header = topheader;
18229689912eSchristos 	while (header != NULL && IGNORE(header)) {
18239689912eSchristos 		header = header->down;
18249689912eSchristos 	}
18259689912eSchristos 	if (header != NULL) {
18269689912eSchristos 		/*
18279689912eSchristos 		 * If 'merge' is true and header isn't empty/nonexistent,
18289689912eSchristos 		 * we'll try to create a new rdataset that is the union
18299689912eSchristos 		 * of 'newheader' and 'header'.
18309689912eSchristos 		 */
18319689912eSchristos 		if (merge && !NONEXISTENT(header)) {
18329689912eSchristos 			unsigned int flags = 0;
18339689912eSchristos 			INSIST(version->serial >= header->serial);
18349689912eSchristos 			merged = NULL;
18359689912eSchristos 			result = ISC_R_SUCCESS;
18369689912eSchristos 
18379689912eSchristos 			if ((options & DNS_DBADD_EXACT) != 0) {
18389689912eSchristos 				flags |= DNS_RDATASLAB_EXACT;
18399689912eSchristos 			}
18409689912eSchristos 			if ((options & DNS_DBADD_EXACTTTL) != 0 &&
18419689912eSchristos 			    newheader->ttl != header->ttl)
18429689912eSchristos 			{
18439689912eSchristos 				result = DNS_R_NOTEXACT;
18449689912eSchristos 			} else if (newheader->ttl != header->ttl) {
18459689912eSchristos 				flags |= DNS_RDATASLAB_FORCE;
18469689912eSchristos 			}
18479689912eSchristos 			if (result == ISC_R_SUCCESS) {
18489689912eSchristos 				result = dns_rdataslab_merge(
18499689912eSchristos 					(unsigned char *)header,
18509689912eSchristos 					(unsigned char *)newheader,
18519689912eSchristos 					(unsigned int)(sizeof(*newheader)),
18529689912eSchristos 					qpdb->common.mctx, qpdb->common.rdclass,
18539689912eSchristos 					(dns_rdatatype_t)header->type, flags,
18549689912eSchristos 					qpdb->maxrrperset, &merged);
18559689912eSchristos 			}
18569689912eSchristos 			if (result == ISC_R_SUCCESS) {
18579689912eSchristos 				/*
18589689912eSchristos 				 * If 'header' has the same serial number as
18599689912eSchristos 				 * we do, we could clean it up now if we knew
18609689912eSchristos 				 * that our caller had no references to it.
18619689912eSchristos 				 * We don't know this, however, so we leave it
18629689912eSchristos 				 * alone.  It will get cleaned up when
18639689912eSchristos 				 * clean_zone_node() runs.
18649689912eSchristos 				 */
18659689912eSchristos 				dns_slabheader_destroy(&newheader);
18669689912eSchristos 				newheader = (dns_slabheader_t *)merged;
18679689912eSchristos 				dns_slabheader_reset(newheader,
18689689912eSchristos 						     (dns_db_t *)qpdb,
18699689912eSchristos 						     (dns_dbnode_t *)node);
18709689912eSchristos 				dns_slabheader_copycase(newheader, header);
18719689912eSchristos 				if (loading && RESIGN(newheader) &&
18729689912eSchristos 				    RESIGN(header) &&
18739689912eSchristos 				    resign_sooner(header, newheader))
18749689912eSchristos 				{
18759689912eSchristos 					newheader->resign = header->resign;
18769689912eSchristos 					newheader->resign_lsb =
18779689912eSchristos 						header->resign_lsb;
18789689912eSchristos 				}
18799689912eSchristos 			} else {
18809689912eSchristos 				if (result == DNS_R_TOOMANYRECORDS) {
18819689912eSchristos 					dns__db_logtoomanyrecords(
18829689912eSchristos 						(dns_db_t *)qpdb, nodename,
18839689912eSchristos 						(dns_rdatatype_t)header->type,
18849689912eSchristos 						"updating", qpdb->maxrrperset);
18859689912eSchristos 				}
18869689912eSchristos 				dns_slabheader_destroy(&newheader);
18879689912eSchristos 				return result;
18889689912eSchristos 			}
18899689912eSchristos 		}
18909689912eSchristos 
18919689912eSchristos 		INSIST(version->serial >= topheader->serial);
18929689912eSchristos 		if (loading) {
18939689912eSchristos 			newheader->down = NULL;
18949689912eSchristos 			if (RESIGN(newheader)) {
18959689912eSchristos 				resigninsert(qpdb, newheader);
18969689912eSchristos 				/* resigndelete not needed here */
18979689912eSchristos 			}
18989689912eSchristos 
18999689912eSchristos 			/*
19009689912eSchristos 			 * There are no other references to 'header' when
19019689912eSchristos 			 * loading, so we MAY clean up 'header' now.
19029689912eSchristos 			 * Since we don't generate changed records when
19039689912eSchristos 			 * loading, we MUST clean up 'header' now.
19049689912eSchristos 			 */
19059689912eSchristos 			if (topheader_prev != NULL) {
19069689912eSchristos 				topheader_prev->next = newheader;
19079689912eSchristos 			} else {
19089689912eSchristos 				node->data = newheader;
19099689912eSchristos 			}
19109689912eSchristos 			newheader->next = topheader->next;
19119689912eSchristos 			maybe_update_recordsandsize(false, version, header,
19129689912eSchristos 						    nodename->length);
19139689912eSchristos 			dns_slabheader_destroy(&header);
19149689912eSchristos 		} else {
19159689912eSchristos 			if (RESIGN(newheader)) {
19169689912eSchristos 				resigninsert(qpdb, newheader);
19179689912eSchristos 				resigndelete(qpdb, version,
19189689912eSchristos 					     header DNS__DB_FLARG_PASS);
19199689912eSchristos 			}
19209689912eSchristos 			if (topheader_prev != NULL) {
19219689912eSchristos 				topheader_prev->next = newheader;
19229689912eSchristos 			} else {
19239689912eSchristos 				node->data = newheader;
19249689912eSchristos 			}
19259689912eSchristos 			newheader->next = topheader->next;
19269689912eSchristos 			newheader->down = topheader;
19279689912eSchristos 			topheader->next = newheader;
19289689912eSchristos 			node->dirty = true;
19299689912eSchristos 			if (changed != NULL) {
19309689912eSchristos 				changed->dirty = true;
19319689912eSchristos 			}
19329689912eSchristos 			maybe_update_recordsandsize(false, version, header,
19339689912eSchristos 						    nodename->length);
19349689912eSchristos 		}
19359689912eSchristos 	} else {
19369689912eSchristos 		/*
19379689912eSchristos 		 * No non-IGNORED rdatasets of the given type exist at
19389689912eSchristos 		 * this node.
19399689912eSchristos 		 *
19409689912eSchristos 		 * If we're trying to delete the type, don't bother.
19419689912eSchristos 		 */
19429689912eSchristos 		if (NONEXISTENT(newheader)) {
19439689912eSchristos 			dns_slabheader_destroy(&newheader);
19449689912eSchristos 			return DNS_R_UNCHANGED;
19459689912eSchristos 		}
19469689912eSchristos 
19479689912eSchristos 		if (RESIGN(newheader)) {
19489689912eSchristos 			resigninsert(qpdb, newheader);
19499689912eSchristos 			resigndelete(qpdb, version, header DNS__DB_FLARG_PASS);
19509689912eSchristos 		}
19519689912eSchristos 
19529689912eSchristos 		if (topheader != NULL) {
19539689912eSchristos 			/*
19549689912eSchristos 			 * We have a list of rdatasets of the given type,
19559689912eSchristos 			 * but they're all marked IGNORE.  We simply insert
19569689912eSchristos 			 * the new rdataset at the head of the list.
19579689912eSchristos 			 *
19589689912eSchristos 			 * Ignored rdatasets cannot occur during loading, so
19599689912eSchristos 			 * we INSIST on it.
19609689912eSchristos 			 */
19619689912eSchristos 			INSIST(!loading);
19629689912eSchristos 			INSIST(version->serial >= topheader->serial);
19639689912eSchristos 			if (topheader_prev != NULL) {
19649689912eSchristos 				topheader_prev->next = newheader;
19659689912eSchristos 			} else {
19669689912eSchristos 				node->data = newheader;
19679689912eSchristos 			}
19689689912eSchristos 			newheader->next = topheader->next;
19699689912eSchristos 			newheader->down = topheader;
19709689912eSchristos 			topheader->next = newheader;
19719689912eSchristos 			if (changed != NULL) {
19729689912eSchristos 				changed->dirty = true;
19739689912eSchristos 			}
19749689912eSchristos 			node->dirty = true;
19759689912eSchristos 		} else {
19769689912eSchristos 			/*
19779689912eSchristos 			 * No rdatasets of the given type exist at the node.
19789689912eSchristos 			 */
19799689912eSchristos 
19809689912eSchristos 			if (qpdb->maxtypepername > 0 &&
19819689912eSchristos 			    ntypes >= qpdb->maxtypepername)
19829689912eSchristos 			{
19839689912eSchristos 				dns_slabheader_destroy(&newheader);
19849689912eSchristos 				return DNS_R_TOOMANYRECORDS;
19859689912eSchristos 			}
19869689912eSchristos 
19879689912eSchristos 			INSIST(newheader->down == NULL);
19889689912eSchristos 
19899689912eSchristos 			if (prio_type(newheader->type)) {
19909689912eSchristos 				/* This is a priority type, prepend it */
19919689912eSchristos 				newheader->next = node->data;
19929689912eSchristos 				node->data = newheader;
19939689912eSchristos 			} else if (prioheader != NULL) {
19949689912eSchristos 				/* Append after the priority headers */
19959689912eSchristos 				newheader->next = prioheader->next;
19969689912eSchristos 				prioheader->next = newheader;
19979689912eSchristos 			} else {
19989689912eSchristos 				/* There were no priority headers */
19999689912eSchristos 				newheader->next = node->data;
20009689912eSchristos 				node->data = newheader;
20019689912eSchristos 			}
20029689912eSchristos 		}
20039689912eSchristos 	}
20049689912eSchristos 
20059689912eSchristos 	maybe_update_recordsandsize(true, version, newheader, nodename->length);
20069689912eSchristos 
20079689912eSchristos 	/*
20089689912eSchristos 	 * Check if the node now contains CNAME and other data.
20099689912eSchristos 	 */
20109689912eSchristos 	if (cname_and_other(node, version->serial)) {
20119689912eSchristos 		return DNS_R_CNAMEANDOTHER;
20129689912eSchristos 	}
20139689912eSchristos 
20149689912eSchristos 	if (addedrdataset != NULL) {
20159689912eSchristos 		bindrdataset(qpdb, node, newheader, now,
20169689912eSchristos 			     addedrdataset DNS__DB_FLARG_PASS);
20179689912eSchristos 	}
20189689912eSchristos 
20199689912eSchristos 	return ISC_R_SUCCESS;
20209689912eSchristos }
20219689912eSchristos 
20229689912eSchristos static void
20239689912eSchristos wildcardmagic(qpzonedb_t *qpdb, dns_qp_t *qp, const dns_name_t *name) {
20249689912eSchristos 	isc_result_t result;
20259689912eSchristos 	dns_name_t foundname;
20269689912eSchristos 	dns_offsets_t offsets;
20279689912eSchristos 	unsigned int n;
20289689912eSchristos 	qpznode_t *node = NULL;
20299689912eSchristos 
20309689912eSchristos 	dns_name_init(&foundname, offsets);
20319689912eSchristos 	n = dns_name_countlabels(name);
20329689912eSchristos 	INSIST(n >= 2);
20339689912eSchristos 	n--;
20349689912eSchristos 	dns_name_getlabelsequence(name, 1, n, &foundname);
20359689912eSchristos 
20369689912eSchristos 	/* insert an empty node, if needed, to hold the wildcard bit */
20379689912eSchristos 	result = dns_qp_getname(qp, &foundname, (void **)&node, NULL);
20389689912eSchristos 	if (result != ISC_R_SUCCESS) {
20399689912eSchristos 		INSIST(node == NULL);
20409689912eSchristos 		node = new_qpznode(qpdb, &foundname);
20419689912eSchristos 		result = dns_qp_insert(qp, node, 0);
20429689912eSchristos 		INSIST(result == ISC_R_SUCCESS);
20439689912eSchristos 		qpznode_unref(node);
20449689912eSchristos 	}
20459689912eSchristos 
20469689912eSchristos 	node->wild = true;
20479689912eSchristos }
20489689912eSchristos 
20499689912eSchristos static void
20509689912eSchristos addwildcards(qpzonedb_t *qpdb, dns_qp_t *qp, const dns_name_t *name) {
20519689912eSchristos 	dns_name_t foundname;
20529689912eSchristos 	dns_offsets_t offsets;
20539689912eSchristos 	unsigned int n, l, i;
20549689912eSchristos 
20559689912eSchristos 	dns_name_init(&foundname, offsets);
20569689912eSchristos 	n = dns_name_countlabels(name);
20579689912eSchristos 	l = dns_name_countlabels(&qpdb->common.origin);
20589689912eSchristos 	i = l + 1;
20599689912eSchristos 	while (i < n) {
20609689912eSchristos 		dns_name_getlabelsequence(name, n - i, i, &foundname);
20619689912eSchristos 		if (dns_name_iswildcard(&foundname)) {
20629689912eSchristos 			wildcardmagic(qpdb, qp, &foundname);
20639689912eSchristos 		}
20649689912eSchristos 
20659689912eSchristos 		i++;
20669689912eSchristos 	}
20679689912eSchristos }
20689689912eSchristos 
20699689912eSchristos static isc_result_t
20709689912eSchristos loading_addrdataset(void *arg, const dns_name_t *name,
20719689912eSchristos 		    dns_rdataset_t *rdataset DNS__DB_FLARG) {
20729689912eSchristos 	qpz_load_t *loadctx = arg;
20739689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)loadctx->db;
20749689912eSchristos 	qpznode_t *node = NULL;
20759689912eSchristos 	isc_result_t result = ISC_R_SUCCESS;
20769689912eSchristos 	isc_region_t region;
20779689912eSchristos 	dns_slabheader_t *newheader = NULL;
20789689912eSchristos 	isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
20799689912eSchristos 
20809689912eSchristos 	REQUIRE(rdataset->rdclass == qpdb->common.rdclass);
20819689912eSchristos 
20829689912eSchristos 	/*
20839689912eSchristos 	 * SOA records are only allowed at top of zone.
20849689912eSchristos 	 */
20859689912eSchristos 	if (rdataset->type == dns_rdatatype_soa &&
20869689912eSchristos 	    !dns_name_equal(name, &qpdb->common.origin))
20879689912eSchristos 	{
20889689912eSchristos 		return DNS_R_NOTZONETOP;
20899689912eSchristos 	}
20909689912eSchristos 
20919689912eSchristos 	if (rdataset->type != dns_rdatatype_nsec3 &&
20929689912eSchristos 	    rdataset->covers != dns_rdatatype_nsec3)
20939689912eSchristos 	{
20949689912eSchristos 		addwildcards(qpdb, loadctx->tree, name);
20959689912eSchristos 	}
20969689912eSchristos 
20979689912eSchristos 	if (dns_name_iswildcard(name)) {
20989689912eSchristos 		if (rdataset->type == dns_rdatatype_ns) {
20999689912eSchristos 			/*
21009689912eSchristos 			 * NS owners cannot legally be wild cards.
21019689912eSchristos 			 */
21029689912eSchristos 			return DNS_R_INVALIDNS;
21039689912eSchristos 		}
21049689912eSchristos 
21059689912eSchristos 		if (rdataset->type == dns_rdatatype_nsec3) {
21069689912eSchristos 			/*
21079689912eSchristos 			 * NSEC3 owners cannot legally be wild cards.
21089689912eSchristos 			 */
21099689912eSchristos 			return DNS_R_INVALIDNSEC3;
21109689912eSchristos 		}
21119689912eSchristos 
21129689912eSchristos 		wildcardmagic(qpdb, loadctx->tree, name);
21139689912eSchristos 	}
21149689912eSchristos 
21159689912eSchristos 	loading_addnode(loadctx, name, rdataset->type, rdataset->covers, &node);
21169689912eSchristos 	result = dns_rdataslab_fromrdataset(rdataset, qpdb->common.mctx,
21179689912eSchristos 					    &region, sizeof(dns_slabheader_t),
21189689912eSchristos 					    qpdb->maxrrperset);
21199689912eSchristos 	if (result != ISC_R_SUCCESS) {
21209689912eSchristos 		if (result == DNS_R_TOOMANYRECORDS) {
21219689912eSchristos 			dns__db_logtoomanyrecords((dns_db_t *)qpdb, name,
21229689912eSchristos 						  rdataset->type, "adding",
21239689912eSchristos 						  qpdb->maxrrperset);
21249689912eSchristos 		}
21259689912eSchristos 		return result;
21269689912eSchristos 	}
21279689912eSchristos 
21289689912eSchristos 	newheader = (dns_slabheader_t *)region.base;
21299689912eSchristos 	*newheader = (dns_slabheader_t){
21309689912eSchristos 		.type = DNS_TYPEPAIR_VALUE(rdataset->type, rdataset->covers),
21319689912eSchristos 		.ttl = rdataset->ttl + loadctx->now,
21329689912eSchristos 		.trust = rdataset->trust,
21339689912eSchristos 		.node = node,
21349689912eSchristos 		.serial = 1,
21359689912eSchristos 		.count = 1,
21369689912eSchristos 	};
21379689912eSchristos 
21389689912eSchristos 	dns_slabheader_reset(newheader, (dns_db_t *)qpdb, node);
21399689912eSchristos 	dns_slabheader_setownercase(newheader, name);
21409689912eSchristos 
21419689912eSchristos 	if ((rdataset->attributes & DNS_RDATASETATTR_RESIGN) != 0) {
21429689912eSchristos 		DNS_SLABHEADER_SETATTR(newheader, DNS_SLABHEADERATTR_RESIGN);
21439689912eSchristos 		newheader->resign =
21449689912eSchristos 			(isc_stdtime_t)(dns_time64_from32(rdataset->resign) >>
21459689912eSchristos 					1);
21469689912eSchristos 		newheader->resign_lsb = rdataset->resign & 0x1;
21479689912eSchristos 	}
21489689912eSchristos 
21499689912eSchristos 	NODE_WRLOCK(&qpdb->node_locks[node->locknum].lock, &nlocktype);
21509689912eSchristos 	result = add(qpdb, node, name, qpdb->current_version, newheader,
21519689912eSchristos 		     DNS_DBADD_MERGE, true, NULL, 0 DNS__DB_FLARG_PASS);
21529689912eSchristos 	NODE_UNLOCK(&qpdb->node_locks[node->locknum].lock, &nlocktype);
21539689912eSchristos 
21549689912eSchristos 	if (result == ISC_R_SUCCESS &&
21559689912eSchristos 	    delegating_type(qpdb, node, rdataset->type))
21569689912eSchristos 	{
21579689912eSchristos 		node->delegating = true;
21589689912eSchristos 	} else if (result == DNS_R_UNCHANGED) {
21599689912eSchristos 		result = ISC_R_SUCCESS;
21609689912eSchristos 	}
21619689912eSchristos 
21629689912eSchristos 	return result;
21639689912eSchristos }
21649689912eSchristos 
21659689912eSchristos static void
21669689912eSchristos loading_setup(void *arg) {
21679689912eSchristos 	qpz_load_t *loadctx = arg;
21689689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)loadctx->db;
21699689912eSchristos 
21709689912eSchristos 	dns_qpmulti_write(qpdb->tree, &loadctx->tree);
21719689912eSchristos 	dns_qpmulti_write(qpdb->nsec, &loadctx->nsec);
21729689912eSchristos 	dns_qpmulti_write(qpdb->nsec3, &loadctx->nsec3);
21739689912eSchristos }
21749689912eSchristos 
21759689912eSchristos static void
21769689912eSchristos loading_commit(void *arg) {
21779689912eSchristos 	qpz_load_t *loadctx = arg;
21789689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)loadctx->db;
21799689912eSchristos 
21809689912eSchristos 	if (loadctx->tree != NULL) {
21819689912eSchristos 		dns_qp_compact(loadctx->tree, DNS_QPGC_MAYBE);
21829689912eSchristos 		dns_qpmulti_commit(qpdb->tree, &loadctx->tree);
21839689912eSchristos 	}
21849689912eSchristos 	if (loadctx->nsec != NULL) {
21859689912eSchristos 		dns_qp_compact(loadctx->nsec, DNS_QPGC_MAYBE);
21869689912eSchristos 		dns_qpmulti_commit(qpdb->nsec, &loadctx->nsec);
21879689912eSchristos 	}
21889689912eSchristos 	if (loadctx->nsec3 != NULL) {
21899689912eSchristos 		dns_qp_compact(loadctx->nsec3, DNS_QPGC_MAYBE);
21909689912eSchristos 		dns_qpmulti_commit(qpdb->nsec3, &loadctx->nsec3);
21919689912eSchristos 	}
21929689912eSchristos }
21939689912eSchristos 
21949689912eSchristos static isc_result_t
21959689912eSchristos beginload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
21969689912eSchristos 	qpz_load_t *loadctx = NULL;
21979689912eSchristos 	qpzonedb_t *qpdb = NULL;
21989689912eSchristos 	qpdb = (qpzonedb_t *)db;
21999689912eSchristos 
22009689912eSchristos 	REQUIRE(DNS_CALLBACK_VALID(callbacks));
22019689912eSchristos 	REQUIRE(VALID_QPZONE(qpdb));
22029689912eSchristos 
22039689912eSchristos 	loadctx = isc_mem_get(qpdb->common.mctx, sizeof(*loadctx));
22049689912eSchristos 	*loadctx = (qpz_load_t){ .db = db };
22059689912eSchristos 
22069689912eSchristos 	RWLOCK(&qpdb->lock, isc_rwlocktype_write);
22079689912eSchristos 
22089689912eSchristos 	REQUIRE((qpdb->attributes & (QPDB_ATTR_LOADED | QPDB_ATTR_LOADING)) ==
22099689912eSchristos 		0);
22109689912eSchristos 	qpdb->attributes |= QPDB_ATTR_LOADING;
22119689912eSchristos 
22129689912eSchristos 	RWUNLOCK(&qpdb->lock, isc_rwlocktype_write);
22139689912eSchristos 
22149689912eSchristos 	callbacks->add = loading_addrdataset;
22159689912eSchristos 	callbacks->setup = loading_setup;
22169689912eSchristos 	callbacks->commit = loading_commit;
22179689912eSchristos 	callbacks->add_private = loadctx;
22189689912eSchristos 
22199689912eSchristos 	return ISC_R_SUCCESS;
22209689912eSchristos }
22219689912eSchristos 
22229689912eSchristos static isc_result_t
22239689912eSchristos endload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
22249689912eSchristos 	qpz_load_t *loadctx = NULL;
22259689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)db;
22269689912eSchristos 
22279689912eSchristos 	REQUIRE(VALID_QPZONE(qpdb));
22289689912eSchristos 	REQUIRE(DNS_CALLBACK_VALID(callbacks));
22299689912eSchristos 	loadctx = callbacks->add_private;
22309689912eSchristos 	REQUIRE(loadctx != NULL);
22319689912eSchristos 	REQUIRE(loadctx->db == db);
22329689912eSchristos 
22339689912eSchristos 	RWLOCK(&qpdb->lock, isc_rwlocktype_write);
22349689912eSchristos 
22359689912eSchristos 	REQUIRE((qpdb->attributes & QPDB_ATTR_LOADING) != 0);
22369689912eSchristos 	REQUIRE((qpdb->attributes & QPDB_ATTR_LOADED) == 0);
22379689912eSchristos 
22389689912eSchristos 	qpdb->attributes &= ~QPDB_ATTR_LOADING;
22399689912eSchristos 	qpdb->attributes |= QPDB_ATTR_LOADED;
22409689912eSchristos 
22419689912eSchristos 	if (qpdb->origin != NULL) {
22429689912eSchristos 		dns_dbversion_t *version = qpdb->current_version;
22439689912eSchristos 		RWUNLOCK(&qpdb->lock, isc_rwlocktype_write);
22449689912eSchristos 		setsecure(db, version, qpdb->origin);
22459689912eSchristos 	} else {
22469689912eSchristos 		RWUNLOCK(&qpdb->lock, isc_rwlocktype_write);
22479689912eSchristos 	}
22489689912eSchristos 
22499689912eSchristos 	callbacks->add = NULL;
22509689912eSchristos 	callbacks->setup = NULL;
22519689912eSchristos 	callbacks->commit = NULL;
22529689912eSchristos 	callbacks->add_private = NULL;
22539689912eSchristos 
22549689912eSchristos 	isc_mem_put(qpdb->common.mctx, loadctx, sizeof(*loadctx));
22559689912eSchristos 
22569689912eSchristos 	return ISC_R_SUCCESS;
22579689912eSchristos }
22589689912eSchristos 
22599689912eSchristos static bool
22609689912eSchristos issecure(dns_db_t *db) {
22619689912eSchristos 	qpzonedb_t *qpdb = NULL;
22629689912eSchristos 	bool secure;
22639689912eSchristos 
22649689912eSchristos 	qpdb = (qpzonedb_t *)db;
22659689912eSchristos 
22669689912eSchristos 	REQUIRE(VALID_QPZONE(qpdb));
22679689912eSchristos 
22689689912eSchristos 	RWLOCK(&qpdb->lock, isc_rwlocktype_read);
22699689912eSchristos 	secure = qpdb->current_version->secure;
22709689912eSchristos 	RWUNLOCK(&qpdb->lock, isc_rwlocktype_read);
22719689912eSchristos 
22729689912eSchristos 	return secure;
22739689912eSchristos }
22749689912eSchristos 
22759689912eSchristos static isc_result_t
22769689912eSchristos getnsec3parameters(dns_db_t *db, dns_dbversion_t *dbversion, dns_hash_t *hash,
22779689912eSchristos 		   uint8_t *flags, uint16_t *iterations, unsigned char *salt,
22789689912eSchristos 		   size_t *salt_length) {
22799689912eSchristos 	qpzonedb_t *qpdb = NULL;
22809689912eSchristos 	isc_result_t result = ISC_R_NOTFOUND;
22819689912eSchristos 	qpz_version_t *version = dbversion;
22829689912eSchristos 
22839689912eSchristos 	qpdb = (qpzonedb_t *)db;
22849689912eSchristos 
22859689912eSchristos 	REQUIRE(VALID_QPZONE(qpdb));
22869689912eSchristos 	INSIST(version == NULL || version->qpdb == qpdb);
22879689912eSchristos 
22889689912eSchristos 	RWLOCK(&qpdb->lock, isc_rwlocktype_read);
22899689912eSchristos 	if (version == NULL) {
22909689912eSchristos 		version = qpdb->current_version;
22919689912eSchristos 	}
22929689912eSchristos 
22939689912eSchristos 	if (version->havensec3) {
22949689912eSchristos 		if (hash != NULL) {
22959689912eSchristos 			*hash = version->hash;
22969689912eSchristos 		}
22979689912eSchristos 		if (salt != NULL && salt_length != NULL) {
22989689912eSchristos 			REQUIRE(*salt_length >= version->salt_length);
22999689912eSchristos 			memmove(salt, version->salt, version->salt_length);
23009689912eSchristos 		}
23019689912eSchristos 		if (salt_length != NULL) {
23029689912eSchristos 			*salt_length = version->salt_length;
23039689912eSchristos 		}
23049689912eSchristos 		if (iterations != NULL) {
23059689912eSchristos 			*iterations = version->iterations;
23069689912eSchristos 		}
23079689912eSchristos 		if (flags != NULL) {
23089689912eSchristos 			*flags = version->flags;
23099689912eSchristos 		}
23109689912eSchristos 		result = ISC_R_SUCCESS;
23119689912eSchristos 	}
23129689912eSchristos 	RWUNLOCK(&qpdb->lock, isc_rwlocktype_read);
23139689912eSchristos 
23149689912eSchristos 	return result;
23159689912eSchristos }
23169689912eSchristos 
23179689912eSchristos static isc_result_t
23189689912eSchristos getsize(dns_db_t *db, dns_dbversion_t *dbversion, uint64_t *records,
23199689912eSchristos 	uint64_t *xfrsize) {
23209689912eSchristos 	qpzonedb_t *qpdb = NULL;
23219689912eSchristos 	qpz_version_t *version = dbversion;
23229689912eSchristos 	isc_result_t result = ISC_R_SUCCESS;
23239689912eSchristos 
23249689912eSchristos 	qpdb = (qpzonedb_t *)db;
23259689912eSchristos 
23269689912eSchristos 	REQUIRE(VALID_QPZONE(qpdb));
23279689912eSchristos 	INSIST(version == NULL || version->qpdb == qpdb);
23289689912eSchristos 
23299689912eSchristos 	RWLOCK(&qpdb->lock, isc_rwlocktype_read);
23309689912eSchristos 	if (version == NULL) {
23319689912eSchristos 		version = qpdb->current_version;
23329689912eSchristos 	}
23339689912eSchristos 
23349689912eSchristos 	RWLOCK(&version->rwlock, isc_rwlocktype_read);
23359689912eSchristos 	SET_IF_NOT_NULL(records, version->records);
23369689912eSchristos 
23379689912eSchristos 	SET_IF_NOT_NULL(xfrsize, version->xfrsize);
23389689912eSchristos 	RWUNLOCK(&version->rwlock, isc_rwlocktype_read);
23399689912eSchristos 	RWUNLOCK(&qpdb->lock, isc_rwlocktype_read);
23409689912eSchristos 
23419689912eSchristos 	return result;
23429689912eSchristos }
23439689912eSchristos 
23449689912eSchristos static isc_result_t
23459689912eSchristos setsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, isc_stdtime_t resign) {
23469689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)db;
23479689912eSchristos 	dns_slabheader_t *header = NULL, oldheader;
23489689912eSchristos 	isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
23499689912eSchristos 
23509689912eSchristos 	REQUIRE(VALID_QPZONE(qpdb));
23519689912eSchristos 	REQUIRE(rdataset != NULL);
23529689912eSchristos 	REQUIRE(rdataset->methods == &dns_rdataslab_rdatasetmethods);
23539689912eSchristos 
23549689912eSchristos 	header = dns_slabheader_fromrdataset(rdataset);
23559689912eSchristos 
23569689912eSchristos 	NODE_WRLOCK(&qpdb->node_locks[HEADERNODE(header)->locknum].lock,
23579689912eSchristos 		    &nlocktype);
23589689912eSchristos 
23599689912eSchristos 	oldheader = *header;
23609689912eSchristos 
23619689912eSchristos 	/*
23629689912eSchristos 	 * Only break the heap invariant (by adjusting resign and resign_lsb)
23639689912eSchristos 	 * if we are going to be restoring it by calling isc_heap_increased
23649689912eSchristos 	 * or isc_heap_decreased.
23659689912eSchristos 	 */
23669689912eSchristos 	if (resign != 0) {
23679689912eSchristos 		header->resign = (isc_stdtime_t)(dns_time64_from32(resign) >>
23689689912eSchristos 						 1);
23699689912eSchristos 		header->resign_lsb = resign & 0x1;
23709689912eSchristos 	}
23719689912eSchristos 	if (header->heap_index != 0) {
23729689912eSchristos 		INSIST(RESIGN(header));
23739689912eSchristos 		RWLOCK(&qpdb->lock, isc_rwlocktype_write);
23749689912eSchristos 		if (resign == 0) {
23759689912eSchristos 			isc_heap_delete(qpdb->heap, header->heap_index);
23769689912eSchristos 			header->heap_index = 0;
23779689912eSchristos 			header->heap = NULL;
23789689912eSchristos 		} else if (resign_sooner(header, &oldheader)) {
23799689912eSchristos 			isc_heap_increased(qpdb->heap, header->heap_index);
23809689912eSchristos 		} else if (resign_sooner(&oldheader, header)) {
23819689912eSchristos 			isc_heap_decreased(qpdb->heap, header->heap_index);
23829689912eSchristos 		}
23839689912eSchristos 		RWUNLOCK(&qpdb->lock, isc_rwlocktype_write);
23849689912eSchristos 	} else if (resign != 0) {
23859689912eSchristos 		DNS_SLABHEADER_SETATTR(header, DNS_SLABHEADERATTR_RESIGN);
23869689912eSchristos 		resigninsert(qpdb, header);
23879689912eSchristos 	}
23889689912eSchristos 	NODE_UNLOCK(&qpdb->node_locks[HEADERNODE(header)->locknum].lock,
23899689912eSchristos 		    &nlocktype);
23909689912eSchristos 	return ISC_R_SUCCESS;
23919689912eSchristos }
23929689912eSchristos 
23939689912eSchristos static isc_result_t
23949689912eSchristos getsigningtime(dns_db_t *db, isc_stdtime_t *resign, dns_name_t *foundname,
23959689912eSchristos 	       dns_typepair_t *typepair) {
23969689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)db;
23979689912eSchristos 	dns_slabheader_t *header = NULL;
23989689912eSchristos 	isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
23999689912eSchristos 	uint16_t locknum;
24009689912eSchristos 	isc_result_t result = ISC_R_NOTFOUND;
24019689912eSchristos 
24029689912eSchristos 	REQUIRE(VALID_QPZONE(qpdb));
24039689912eSchristos 	REQUIRE(resign != NULL);
24049689912eSchristos 	REQUIRE(foundname != NULL);
24059689912eSchristos 	REQUIRE(typepair != NULL);
24069689912eSchristos 
24079689912eSchristos 	RWLOCK(&qpdb->lock, isc_rwlocktype_read);
24089689912eSchristos 	header = isc_heap_element(qpdb->heap, 1);
24099689912eSchristos 	if (header == NULL) {
24109689912eSchristos 		RWUNLOCK(&qpdb->lock, isc_rwlocktype_read);
24119689912eSchristos 		return ISC_R_NOTFOUND;
24129689912eSchristos 	}
24139689912eSchristos 	locknum = HEADERNODE(header)->locknum;
24149689912eSchristos 	RWUNLOCK(&qpdb->lock, isc_rwlocktype_read);
24159689912eSchristos 
24169689912eSchristos again:
24179689912eSchristos 	NODE_RDLOCK(&qpdb->node_locks[locknum].lock, &nlocktype);
24189689912eSchristos 
24199689912eSchristos 	RWLOCK(&qpdb->lock, isc_rwlocktype_read);
24209689912eSchristos 	header = isc_heap_element(qpdb->heap, 1);
24219689912eSchristos 	if (header != NULL && HEADERNODE(header)->locknum != locknum) {
24229689912eSchristos 		RWUNLOCK(&qpdb->lock, isc_rwlocktype_read);
24239689912eSchristos 		NODE_UNLOCK(&qpdb->node_locks[locknum].lock, &nlocktype);
24249689912eSchristos 		locknum = HEADERNODE(header)->locknum;
24259689912eSchristos 		goto again;
24269689912eSchristos 	}
24279689912eSchristos 
24289689912eSchristos 	if (header != NULL) {
24299689912eSchristos 		*resign = RESIGN(header)
24309689912eSchristos 				  ? (header->resign << 1) | header->resign_lsb
24319689912eSchristos 				  : 0;
24329689912eSchristos 		dns_name_copy(&HEADERNODE(header)->name, foundname);
24339689912eSchristos 		*typepair = header->type;
24349689912eSchristos 		result = ISC_R_SUCCESS;
24359689912eSchristos 	}
24369689912eSchristos 	RWUNLOCK(&qpdb->lock, isc_rwlocktype_read);
24379689912eSchristos 	NODE_UNLOCK(&qpdb->node_locks[locknum].lock, &nlocktype);
24389689912eSchristos 
24399689912eSchristos 	return result;
24409689912eSchristos }
24419689912eSchristos 
24429689912eSchristos static isc_result_t
24439689912eSchristos setgluecachestats(dns_db_t *db, isc_stats_t *stats) {
24449689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)db;
24459689912eSchristos 
24469689912eSchristos 	REQUIRE(VALID_QPZONE(qpdb));
24479689912eSchristos 	REQUIRE(!IS_STUB(qpdb));
24489689912eSchristos 	REQUIRE(stats != NULL);
24499689912eSchristos 
24509689912eSchristos 	isc_stats_attach(stats, &qpdb->gluecachestats);
24519689912eSchristos 	return ISC_R_SUCCESS;
24529689912eSchristos }
24539689912eSchristos 
24549689912eSchristos static isc_result_t
24559689912eSchristos findnodeintree(qpzonedb_t *qpdb, const dns_name_t *name, bool create,
24569689912eSchristos 	       bool nsec3, dns_dbnode_t **nodep DNS__DB_FLARG) {
24579689912eSchristos 	isc_result_t result;
24589689912eSchristos 	qpznode_t *node = NULL;
24599689912eSchristos 	dns_qpmulti_t *dbtree = nsec3 ? qpdb->nsec3 : qpdb->tree;
24609689912eSchristos 	dns_qpread_t qpr = { 0 };
24619689912eSchristos 	dns_qp_t *qp = NULL;
24629689912eSchristos 
24639689912eSchristos 	if (create) {
24649689912eSchristos 		dns_qpmulti_write(dbtree, &qp);
24659689912eSchristos 	} else {
24669689912eSchristos 		dns_qpmulti_query(dbtree, &qpr);
24679689912eSchristos 		qp = (dns_qp_t *)&qpr;
24689689912eSchristos 	}
24699689912eSchristos 
24709689912eSchristos 	result = dns_qp_getname(qp, name, (void **)&node, NULL);
24719689912eSchristos 	if (result != ISC_R_SUCCESS) {
24729689912eSchristos 		if (!create) {
24739689912eSchristos 			dns_qpread_destroy(dbtree, &qpr);
24749689912eSchristos 			return result;
24759689912eSchristos 		}
24769689912eSchristos 
24779689912eSchristos 		node = new_qpznode(qpdb, name);
24789689912eSchristos 		result = dns_qp_insert(qp, node, 0);
24799689912eSchristos 		qpznode_unref(node);
24809689912eSchristos 
24819689912eSchristos 		if (result == ISC_R_SUCCESS) {
24829689912eSchristos 			if (nsec3) {
24839689912eSchristos 				node->nsec = DNS_DB_NSEC_NSEC3;
24849689912eSchristos 			} else {
24859689912eSchristos 				addwildcards(qpdb, qp, name);
24869689912eSchristos 				if (dns_name_iswildcard(name)) {
24879689912eSchristos 					wildcardmagic(qpdb, qp, name);
24889689912eSchristos 				}
24899689912eSchristos 			}
24909689912eSchristos 		}
24919689912eSchristos 
24929689912eSchristos 		INSIST(node->nsec == DNS_DB_NSEC_NSEC3 || !nsec3);
24939689912eSchristos 	}
24949689912eSchristos 
24959689912eSchristos 	newref(qpdb, node DNS__DB_FLARG_PASS);
24969689912eSchristos 
24979689912eSchristos 	if (create) {
24989689912eSchristos 		dns_qp_compact(qp, DNS_QPGC_MAYBE);
24999689912eSchristos 		dns_qpmulti_commit(dbtree, &qp);
25009689912eSchristos 	} else {
25019689912eSchristos 		dns_qpread_destroy(dbtree, &qpr);
25029689912eSchristos 	}
25039689912eSchristos 
25049689912eSchristos 	*nodep = (dns_dbnode_t *)node;
25059689912eSchristos 
25069689912eSchristos 	return ISC_R_SUCCESS;
25079689912eSchristos }
25089689912eSchristos 
25099689912eSchristos static isc_result_t
25109689912eSchristos findnode(dns_db_t *db, const dns_name_t *name, bool create,
25119689912eSchristos 	 dns_dbnode_t **nodep DNS__DB_FLARG) {
25129689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)db;
25139689912eSchristos 
25149689912eSchristos 	REQUIRE(VALID_QPZONE(qpdb));
25159689912eSchristos 
25169689912eSchristos 	return findnodeintree(qpdb, name, create, false,
25179689912eSchristos 			      nodep DNS__DB_FLARG_PASS);
25189689912eSchristos }
25199689912eSchristos 
25209689912eSchristos static isc_result_t
25219689912eSchristos findnsec3node(dns_db_t *db, const dns_name_t *name, bool create,
25229689912eSchristos 	      dns_dbnode_t **nodep DNS__DB_FLARG) {
25239689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)db;
25249689912eSchristos 
25259689912eSchristos 	REQUIRE(VALID_QPZONE(qpdb));
25269689912eSchristos 
25279689912eSchristos 	return findnodeintree(qpdb, name, create, true,
25289689912eSchristos 			      nodep DNS__DB_FLARG_PASS);
25299689912eSchristos }
25309689912eSchristos 
25319689912eSchristos static bool
25329689912eSchristos matchparams(dns_slabheader_t *header, qpz_search_t *search) {
25339689912eSchristos 	dns_rdata_t rdata = DNS_RDATA_INIT;
25349689912eSchristos 	dns_rdata_nsec3_t nsec3;
25359689912eSchristos 	unsigned char *raw = NULL;
25369689912eSchristos 	unsigned int rdlen, count;
25379689912eSchristos 	isc_region_t region;
25389689912eSchristos 	isc_result_t result;
25399689912eSchristos 
25409689912eSchristos 	REQUIRE(header->type == dns_rdatatype_nsec3);
25419689912eSchristos 
25429689912eSchristos 	raw = (unsigned char *)header + sizeof(*header);
25439689912eSchristos 	count = raw[0] * 256 + raw[1]; /* count */
25449689912eSchristos 	raw += DNS_RDATASET_COUNT + DNS_RDATASET_LENGTH;
25459689912eSchristos 
25469689912eSchristos 	while (count-- > 0) {
25479689912eSchristos 		rdlen = raw[0] * 256 + raw[1];
25489689912eSchristos 		raw += DNS_RDATASET_ORDER + DNS_RDATASET_LENGTH;
25499689912eSchristos 		region.base = raw;
25509689912eSchristos 		region.length = rdlen;
25519689912eSchristos 		dns_rdata_fromregion(&rdata, search->qpdb->common.rdclass,
25529689912eSchristos 				     dns_rdatatype_nsec3, &region);
25539689912eSchristos 		raw += rdlen;
25549689912eSchristos 		result = dns_rdata_tostruct(&rdata, &nsec3, NULL);
25559689912eSchristos 		INSIST(result == ISC_R_SUCCESS);
25569689912eSchristos 		if (nsec3.hash == search->version->hash &&
25579689912eSchristos 		    nsec3.iterations == search->version->iterations &&
25589689912eSchristos 		    nsec3.salt_length == search->version->salt_length &&
25599689912eSchristos 		    memcmp(nsec3.salt, search->version->salt,
25609689912eSchristos 			   nsec3.salt_length) == 0)
25619689912eSchristos 		{
25629689912eSchristos 			return true;
25639689912eSchristos 		}
25649689912eSchristos 		dns_rdata_reset(&rdata);
25659689912eSchristos 	}
25669689912eSchristos 	return false;
25679689912eSchristos }
25689689912eSchristos 
25699689912eSchristos static isc_result_t
25709689912eSchristos setup_delegation(qpz_search_t *search, dns_dbnode_t **nodep,
25719689912eSchristos 		 dns_name_t *foundname, dns_rdataset_t *rdataset,
25729689912eSchristos 		 dns_rdataset_t *sigrdataset DNS__DB_FLARG) {
25739689912eSchristos 	dns_name_t *zcname = NULL;
25749689912eSchristos 	dns_typepair_t type;
25759689912eSchristos 	qpznode_t *node = NULL;
25769689912eSchristos 
25779689912eSchristos 	REQUIRE(search != NULL);
25789689912eSchristos 	REQUIRE(search->zonecut != NULL);
25799689912eSchristos 	REQUIRE(search->zonecut_header != NULL);
25809689912eSchristos 
25819689912eSchristos 	/*
25829689912eSchristos 	 * The caller MUST NOT be holding any node locks.
25839689912eSchristos 	 */
25849689912eSchristos 
25859689912eSchristos 	node = search->zonecut;
25869689912eSchristos 	type = search->zonecut_header->type;
25879689912eSchristos 
25889689912eSchristos 	/*
25899689912eSchristos 	 * If we have to set foundname, we do it before anything else.
25909689912eSchristos 	 * If we were to set foundname after we had set nodep or bound the
25919689912eSchristos 	 * rdataset, then we'd have to undo that work if dns_name_copy()
25929689912eSchristos 	 * failed.  By setting foundname first, there's nothing to undo if
25939689912eSchristos 	 * we have trouble.
25949689912eSchristos 	 */
25959689912eSchristos 	if (foundname != NULL && search->copy_name) {
25969689912eSchristos 		zcname = dns_fixedname_name(&search->zonecut_name);
25979689912eSchristos 		dns_name_copy(zcname, foundname);
25989689912eSchristos 	}
25999689912eSchristos 	if (nodep != NULL) {
26009689912eSchristos 		/*
26019689912eSchristos 		 * Note that we don't have to increment the node's reference
26029689912eSchristos 		 * count here because we're going to use the reference we
26039689912eSchristos 		 * already have in the search block.
26049689912eSchristos 		 */
26059689912eSchristos 		*nodep = node;
26069689912eSchristos 		search->need_cleanup = false;
26079689912eSchristos 	}
26089689912eSchristos 	if (rdataset != NULL) {
26099689912eSchristos 		isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
26109689912eSchristos 		NODE_RDLOCK(&(search->qpdb->node_locks[node->locknum].lock),
26119689912eSchristos 			    &nlocktype);
26129689912eSchristos 		bindrdataset(search->qpdb, node, search->zonecut_header,
26139689912eSchristos 			     search->now, rdataset DNS__DB_FLARG_PASS);
26149689912eSchristos 		if (sigrdataset != NULL && search->zonecut_sigheader != NULL) {
26159689912eSchristos 			bindrdataset(search->qpdb, node,
26169689912eSchristos 				     search->zonecut_sigheader, search->now,
26179689912eSchristos 				     sigrdataset DNS__DB_FLARG_PASS);
26189689912eSchristos 		}
26199689912eSchristos 		NODE_UNLOCK(&(search->qpdb->node_locks[node->locknum].lock),
26209689912eSchristos 			    &nlocktype);
26219689912eSchristos 	}
26229689912eSchristos 
26239689912eSchristos 	if (type == dns_rdatatype_dname) {
26249689912eSchristos 		return DNS_R_DNAME;
26259689912eSchristos 	}
26269689912eSchristos 	return DNS_R_DELEGATION;
26279689912eSchristos }
26289689912eSchristos 
26299689912eSchristos typedef enum { FORWARD, BACK } direction_t;
26309689912eSchristos 
26319689912eSchristos /*
26329689912eSchristos  * Step backwards or forwards through the database until we find a
26339689912eSchristos  * node with data in it for the desired version. If 'nextname' is not NULL,
26349689912eSchristos  * and we found a predecessor or successor, save the name we found in it.
26359689912eSchristos  * Return true if we found a predecessor or successor.
26369689912eSchristos  */
26379689912eSchristos static bool
26389689912eSchristos step(qpz_search_t *search, dns_qpiter_t *it, direction_t direction,
26399689912eSchristos      dns_name_t *nextname) {
26409689912eSchristos 	dns_fixedname_t fnodename;
26419689912eSchristos 	dns_name_t *nodename = dns_fixedname_initname(&fnodename);
26429689912eSchristos 	qpzonedb_t *qpdb = NULL;
26439689912eSchristos 	qpznode_t *node = NULL;
26449689912eSchristos 	isc_result_t result = ISC_R_SUCCESS;
26459689912eSchristos 	dns_slabheader_t *header = NULL;
26469689912eSchristos 
26479689912eSchristos 	qpdb = search->qpdb;
26489689912eSchristos 
26499689912eSchristos 	result = dns_qpiter_current(it, nodename, (void **)&node, NULL);
26509689912eSchristos 	while (result == ISC_R_SUCCESS) {
26519689912eSchristos 		isc_rwlock_t *nodelock = &qpdb->node_locks[node->locknum].lock;
26529689912eSchristos 		isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
26539689912eSchristos 
26549689912eSchristos 		NODE_RDLOCK(nodelock, &nlocktype);
26559689912eSchristos 		for (header = node->data; header != NULL; header = header->next)
26569689912eSchristos 		{
26579689912eSchristos 			if (header->serial <= search->serial &&
26589689912eSchristos 			    !IGNORE(header) && !NONEXISTENT(header))
26599689912eSchristos 			{
26609689912eSchristos 				break;
26619689912eSchristos 			}
26629689912eSchristos 		}
26639689912eSchristos 		NODE_UNLOCK(nodelock, &nlocktype);
26649689912eSchristos 		if (header != NULL) {
26659689912eSchristos 			break;
26669689912eSchristos 		}
26679689912eSchristos 
26689689912eSchristos 		if (direction == FORWARD) {
26699689912eSchristos 			result = dns_qpiter_next(it, nodename, (void **)&node,
26709689912eSchristos 						 NULL);
26719689912eSchristos 		} else {
26729689912eSchristos 			result = dns_qpiter_prev(it, nodename, (void **)&node,
26739689912eSchristos 						 NULL);
26749689912eSchristos 		}
26759689912eSchristos 	};
26769689912eSchristos 	if (result == ISC_R_SUCCESS) {
26779689912eSchristos 		if (nextname != NULL) {
26789689912eSchristos 			dns_name_copy(nodename, nextname);
26799689912eSchristos 		}
26809689912eSchristos 		return true;
26819689912eSchristos 	}
26829689912eSchristos 
26839689912eSchristos 	return false;
26849689912eSchristos }
26859689912eSchristos 
26869689912eSchristos static bool
26879689912eSchristos activeempty(qpz_search_t *search, dns_qpiter_t *it, const dns_name_t *current) {
26889689912eSchristos 	dns_fixedname_t fnext;
26899689912eSchristos 	dns_name_t *next = dns_fixedname_initname(&fnext);
26909689912eSchristos 
26919689912eSchristos 	/*
26929689912eSchristos 	 * The iterator is currently pointed at the predecessor
26939689912eSchristos 	 * of the name we were searching for. Step the iterator
26949689912eSchristos 	 * forward, then step() will continue forward until it
26959689912eSchristos 	 * finds a node with active data. If that node is a
26969689912eSchristos 	 * subdomain of the one we were looking for, then we're
26979689912eSchristos 	 * at an active empty nonterminal node.
26989689912eSchristos 	 */
26999689912eSchristos 	isc_result_t result = dns_qpiter_next(it, NULL, NULL, NULL);
27009689912eSchristos 	if (result != ISC_R_SUCCESS) {
27019689912eSchristos 		/* An ENT at the end of the zone is impossible */
27029689912eSchristos 		return false;
27039689912eSchristos 	}
27049689912eSchristos 	return step(search, it, FORWARD, next) &&
27059689912eSchristos 	       dns_name_issubdomain(next, current);
27069689912eSchristos }
27079689912eSchristos 
27089689912eSchristos static bool
27099689912eSchristos wildcard_blocked(qpz_search_t *search, const dns_name_t *qname,
27109689912eSchristos 		 dns_name_t *wname) {
27119689912eSchristos 	isc_result_t result;
27129689912eSchristos 	dns_fixedname_t fnext;
27139689912eSchristos 	dns_fixedname_t fprev;
27149689912eSchristos 	dns_name_t *next = NULL, *prev = NULL;
27159689912eSchristos 	dns_name_t name;
27169689912eSchristos 	dns_name_t rname;
27179689912eSchristos 	dns_name_t tname;
27189689912eSchristos 	dns_qpiter_t it;
27199689912eSchristos 	bool check_next = false;
27209689912eSchristos 	bool check_prev = false;
27219689912eSchristos 	unsigned int n;
27229689912eSchristos 
27239689912eSchristos 	dns_name_init(&name, NULL);
27249689912eSchristos 	dns_name_init(&tname, NULL);
27259689912eSchristos 	dns_name_init(&rname, NULL);
27269689912eSchristos 	next = dns_fixedname_initname(&fnext);
27279689912eSchristos 	prev = dns_fixedname_initname(&fprev);
27289689912eSchristos 
27299689912eSchristos 	/*
27309689912eSchristos 	 * The qname seems to have matched a wildcard, but we
27319689912eSchristos 	 * need to find out if there's an empty nonterminal node
27329689912eSchristos 	 * between the wildcard level and the qname.
27339689912eSchristos 	 *
27349689912eSchristos 	 * search->iter should now be pointing at the predecessor
27359689912eSchristos 	 * of the searched-for name. We are using a local copy of the
27369689912eSchristos 	 * iterator so as not to change the state of search->iter.
27379689912eSchristos 	 * step() will walk backward until we find a predecessor with
27389689912eSchristos 	 * data.
27399689912eSchristos 	 */
27409689912eSchristos 	it = search->iter;
27419689912eSchristos 	check_prev = step(search, &it, BACK, prev);
27429689912eSchristos 
27439689912eSchristos 	/* Now reset the iterator and look for a successor with data. */
27449689912eSchristos 	it = search->iter;
27459689912eSchristos 	result = dns_qpiter_next(&it, NULL, NULL, NULL);
27469689912eSchristos 	if (result == ISC_R_SUCCESS) {
27479689912eSchristos 		check_next = step(search, &it, FORWARD, next);
27489689912eSchristos 	}
27499689912eSchristos 
27509689912eSchristos 	if (!check_prev && !check_next) {
27519689912eSchristos 		/* No predecessor or successor was found at all? */
27529689912eSchristos 		return false;
27539689912eSchristos 	}
27549689912eSchristos 
27559689912eSchristos 	dns_name_clone(qname, &rname);
27569689912eSchristos 
27579689912eSchristos 	/*
27589689912eSchristos 	 * Remove the wildcard label to find the terminal name.
27599689912eSchristos 	 */
27609689912eSchristos 	n = dns_name_countlabels(wname);
27619689912eSchristos 	dns_name_getlabelsequence(wname, 1, n - 1, &tname);
27629689912eSchristos 
27639689912eSchristos 	do {
27649689912eSchristos 		if ((check_prev && dns_name_issubdomain(prev, &rname)) ||
27659689912eSchristos 		    (check_next && dns_name_issubdomain(next, &rname)))
27669689912eSchristos 		{
27679689912eSchristos 			return true;
27689689912eSchristos 		}
27699689912eSchristos 
27709689912eSchristos 		/*
27719689912eSchristos 		 * Remove the leftmost label from the qname and check again.
27729689912eSchristos 		 */
27739689912eSchristos 		n = dns_name_countlabels(&rname);
27749689912eSchristos 		dns_name_getlabelsequence(&rname, 1, n - 1, &rname);
27759689912eSchristos 	} while (!dns_name_equal(&rname, &tname));
27769689912eSchristos 
27779689912eSchristos 	return false;
27789689912eSchristos }
27799689912eSchristos 
27809689912eSchristos static isc_result_t
27819689912eSchristos find_wildcard(qpz_search_t *search, qpznode_t **nodep,
27829689912eSchristos 	      const dns_name_t *qname) {
27839689912eSchristos 	dns_slabheader_t *header = NULL;
27849689912eSchristos 	isc_result_t result = ISC_R_NOTFOUND;
27859689912eSchristos 	qpzonedb_t *qpdb = search->qpdb;
27869689912eSchristos 
27879689912eSchristos 	/*
27889689912eSchristos 	 * Examine each ancestor level.  If the level's wild bit
27899689912eSchristos 	 * is set, then construct the corresponding wildcard name and
27909689912eSchristos 	 * search for it.  If the wildcard node exists, and is active in
27919689912eSchristos 	 * this version, we're done.  If not, then we next check to see
27929689912eSchristos 	 * if the ancestor is active in this version.  If so, then there
27939689912eSchristos 	 * can be no possible wildcard match and again we're done.  If not,
27949689912eSchristos 	 * continue the search.
27959689912eSchristos 	 */
27969689912eSchristos 	for (int i = dns_qpchain_length(&search->chain) - 1; i >= 0; i--) {
27979689912eSchristos 		qpznode_t *node = NULL;
27989689912eSchristos 		isc_rwlock_t *lock = NULL;
27999689912eSchristos 		isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
28009689912eSchristos 		bool wild, active;
28019689912eSchristos 
28029689912eSchristos 		dns_qpchain_node(&search->chain, i, NULL, (void **)&node, NULL);
28039689912eSchristos 
28049689912eSchristos 		lock = &qpdb->node_locks[node->locknum].lock;
28059689912eSchristos 		NODE_RDLOCK(lock, &nlocktype);
28069689912eSchristos 		/*
28079689912eSchristos 		 * First we try to figure out if this node is active in
28089689912eSchristos 		 * the search's version.  We do this now, even though we
28099689912eSchristos 		 * may not need the information, because it simplifies the
28109689912eSchristos 		 * locking and code flow.
28119689912eSchristos 		 */
28129689912eSchristos 		for (header = node->data; header != NULL; header = header->next)
28139689912eSchristos 		{
28149689912eSchristos 			if (header->serial <= search->serial &&
28159689912eSchristos 			    !IGNORE(header) && !NONEXISTENT(header))
28169689912eSchristos 			{
28179689912eSchristos 				break;
28189689912eSchristos 			}
28199689912eSchristos 		}
28209689912eSchristos 
28219689912eSchristos 		active = (header != NULL);
28229689912eSchristos 		wild = node->wild;
28239689912eSchristos 		NODE_UNLOCK(lock, &nlocktype);
28249689912eSchristos 
28259689912eSchristos 		if (wild) {
28269689912eSchristos 			qpznode_t *wnode = NULL;
28279689912eSchristos 			dns_fixedname_t fwname;
28289689912eSchristos 			dns_name_t *wname = dns_fixedname_initname(&fwname);
28299689912eSchristos 			dns_qpiter_t wit;
28309689912eSchristos 
28319689912eSchristos 			/*
28329689912eSchristos 			 * Construct the wildcard name for this level.
28339689912eSchristos 			 */
28349689912eSchristos 			result = dns_name_concatenate(dns_wildcardname,
28359689912eSchristos 						      &node->name, wname, NULL);
28369689912eSchristos 			if (result != ISC_R_SUCCESS) {
28379689912eSchristos 				break;
28389689912eSchristos 			}
28399689912eSchristos 
28409689912eSchristos 			result = dns_qp_lookup(&search->qpr, wname, NULL, &wit,
28419689912eSchristos 					       NULL, (void **)&wnode, NULL);
28429689912eSchristos 			if (result == ISC_R_SUCCESS) {
28439689912eSchristos 				/*
28449689912eSchristos 				 * We have found the wildcard node.  If it
28459689912eSchristos 				 * is active in the search's version, we're
28469689912eSchristos 				 * done.
28479689912eSchristos 				 */
28489689912eSchristos 				lock = &qpdb->node_locks[wnode->locknum].lock;
28499689912eSchristos 				NODE_RDLOCK(lock, &nlocktype);
28509689912eSchristos 				for (header = wnode->data; header != NULL;
28519689912eSchristos 				     header = header->next)
28529689912eSchristos 				{
28539689912eSchristos 					if (header->serial <= search->serial &&
28549689912eSchristos 					    !IGNORE(header) &&
28559689912eSchristos 					    !NONEXISTENT(header))
28569689912eSchristos 					{
28579689912eSchristos 						break;
28589689912eSchristos 					}
28599689912eSchristos 				}
28609689912eSchristos 				NODE_UNLOCK(lock, &nlocktype);
28619689912eSchristos 				if (header != NULL ||
28629689912eSchristos 				    activeempty(search, &wit, wname))
28639689912eSchristos 				{
28649689912eSchristos 					if (wildcard_blocked(search, qname,
28659689912eSchristos 							     wname))
28669689912eSchristos 					{
28679689912eSchristos 						return ISC_R_NOTFOUND;
28689689912eSchristos 					}
28699689912eSchristos 
28709689912eSchristos 					/*
28719689912eSchristos 					 * The wildcard node is active!
28729689912eSchristos 					 *
28739689912eSchristos 					 * Note: result is still ISC_R_SUCCESS
28749689912eSchristos 					 * so we don't have to set it.
28759689912eSchristos 					 */
28769689912eSchristos 					*nodep = wnode;
28779689912eSchristos 					break;
28789689912eSchristos 				}
28799689912eSchristos 			} else if (result != ISC_R_NOTFOUND &&
28809689912eSchristos 				   result != DNS_R_PARTIALMATCH)
28819689912eSchristos 			{
28829689912eSchristos 				/*
28839689912eSchristos 				 * An error has occurred.  Bail out.
28849689912eSchristos 				 */
28859689912eSchristos 				break;
28869689912eSchristos 			}
28879689912eSchristos 		}
28889689912eSchristos 
28899689912eSchristos 		if (active) {
28909689912eSchristos 			/*
28919689912eSchristos 			 * The level node is active.  Any wildcarding
28929689912eSchristos 			 * present at higher levels has no
28939689912eSchristos 			 * effect and we're done.
28949689912eSchristos 			 */
28959689912eSchristos 			result = ISC_R_NOTFOUND;
28969689912eSchristos 			break;
28979689912eSchristos 		}
28989689912eSchristos 	}
28999689912eSchristos 
29009689912eSchristos 	return result;
29019689912eSchristos }
29029689912eSchristos 
29039689912eSchristos /*
29049689912eSchristos  * Find node of the NSEC/NSEC3 record that is 'name'.
29059689912eSchristos  */
29069689912eSchristos static isc_result_t
29079689912eSchristos previous_closest_nsec(dns_rdatatype_t type, qpz_search_t *search,
29089689912eSchristos 		      dns_name_t *name, qpznode_t **nodep, dns_qpiter_t *nit,
29099689912eSchristos 		      bool *firstp) {
29109689912eSchristos 	isc_result_t result;
29119689912eSchristos 	dns_qpread_t qpr;
29129689912eSchristos 
29139689912eSchristos 	REQUIRE(nodep != NULL && *nodep == NULL);
29149689912eSchristos 	REQUIRE(type == dns_rdatatype_nsec3 || firstp != NULL);
29159689912eSchristos 
29169689912eSchristos 	if (type == dns_rdatatype_nsec3) {
29179689912eSchristos 		result = dns_qpiter_prev(&search->iter, name, (void **)nodep,
29189689912eSchristos 					 NULL);
29199689912eSchristos 		return result;
29209689912eSchristos 	}
29219689912eSchristos 
29229689912eSchristos 	dns_qpmulti_query(search->qpdb->nsec, &qpr);
29239689912eSchristos 
29249689912eSchristos 	for (;;) {
29259689912eSchristos 		if (*firstp) {
29269689912eSchristos 			/*
29279689912eSchristos 			 * Construct the name of the second node to check.
29289689912eSchristos 			 * It is the first node sought in the NSEC tree.
29299689912eSchristos 			 */
29309689912eSchristos 			*firstp = false;
29319689912eSchristos 			result = dns_qp_lookup(&qpr, name, NULL, nit, NULL,
29329689912eSchristos 					       NULL, NULL);
29339689912eSchristos 			INSIST(result != ISC_R_NOTFOUND);
29349689912eSchristos 			if (result == ISC_R_SUCCESS) {
29359689912eSchristos 				/*
29369689912eSchristos 				 * Since this was the first loop, finding the
29379689912eSchristos 				 * name in the NSEC tree implies that the first
29389689912eSchristos 				 * node checked in the main tree had an
29399689912eSchristos 				 * unacceptable NSEC record.
29409689912eSchristos 				 * Try the previous node in the NSEC tree.
29419689912eSchristos 				 */
29429689912eSchristos 				result = dns_qpiter_prev(nit, name, NULL, NULL);
29439689912eSchristos 			} else if (result == DNS_R_PARTIALMATCH) {
29449689912eSchristos 				/*
29459689912eSchristos 				 * The iterator is already where we want it.
29469689912eSchristos 				 */
29479689912eSchristos 				dns_qpiter_current(nit, name, NULL, NULL);
29489689912eSchristos 				result = ISC_R_SUCCESS;
29499689912eSchristos 			}
29509689912eSchristos 		} else {
29519689912eSchristos 			/*
29529689912eSchristos 			 * This is a second or later trip through the auxiliary
29539689912eSchristos 			 * tree for the name of a third or earlier NSEC node in
29549689912eSchristos 			 * the main tree.  Previous trips through the NSEC tree
29559689912eSchristos 			 * must have found nodes in the main tree with NSEC
29569689912eSchristos 			 * records.  Perhaps they lacked signature records.
29579689912eSchristos 			 */
29589689912eSchristos 			result = dns_qpiter_prev(nit, name, NULL, NULL);
29599689912eSchristos 		}
29609689912eSchristos 		if (result != ISC_R_SUCCESS) {
29619689912eSchristos 			break;
29629689912eSchristos 		}
29639689912eSchristos 
29649689912eSchristos 		*nodep = NULL;
29659689912eSchristos 		result = dns_qp_lookup(&search->qpr, name, NULL, &search->iter,
29669689912eSchristos 				       &search->chain, (void **)nodep, NULL);
29679689912eSchristos 		if (result == ISC_R_SUCCESS) {
29689689912eSchristos 			break;
29699689912eSchristos 		}
29709689912eSchristos 
29719689912eSchristos 		/*
29729689912eSchristos 		 * There should always be a node in the main tree with the
29739689912eSchristos 		 * same name as the node in the auxiliary NSEC tree, except for
29749689912eSchristos 		 * nodes in the auxiliary tree that are awaiting deletion.
29759689912eSchristos 		 */
29769689912eSchristos 		if (result != DNS_R_PARTIALMATCH && result != ISC_R_NOTFOUND) {
29779689912eSchristos 			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
29789689912eSchristos 				      DNS_LOGMODULE_DB, ISC_LOG_ERROR,
29799689912eSchristos 				      "previous_closest_nsec(): %s",
29809689912eSchristos 				      isc_result_totext(result));
29819689912eSchristos 			result = DNS_R_BADDB;
29829689912eSchristos 			break;
29839689912eSchristos 		}
29849689912eSchristos 	}
29859689912eSchristos 
29869689912eSchristos 	dns_qpread_destroy(search->qpdb->nsec, &qpr);
29879689912eSchristos 	return result;
29889689912eSchristos }
29899689912eSchristos 
29909689912eSchristos /*
29919689912eSchristos  * Find the NSEC/NSEC3 which is or before the current point on the
29929689912eSchristos  * search chain.  For NSEC3 records only NSEC3 records that match the
29939689912eSchristos  * current NSEC3PARAM record are considered.
29949689912eSchristos  */
29959689912eSchristos static isc_result_t
29969689912eSchristos find_closest_nsec(qpz_search_t *search, dns_dbnode_t **nodep,
29979689912eSchristos 		  dns_name_t *foundname, dns_rdataset_t *rdataset,
29989689912eSchristos 		  dns_rdataset_t *sigrdataset, bool nsec3,
29999689912eSchristos 		  bool secure DNS__DB_FLARG) {
30009689912eSchristos 	qpznode_t *node = NULL, *prevnode = NULL;
30019689912eSchristos 	dns_slabheader_t *header = NULL, *header_next = NULL;
30029689912eSchristos 	dns_qpiter_t nseciter;
30039689912eSchristos 	bool empty_node;
30049689912eSchristos 	isc_result_t result;
30059689912eSchristos 	dns_fixedname_t fname;
30069689912eSchristos 	dns_name_t *name = dns_fixedname_initname(&fname);
30079689912eSchristos 	dns_rdatatype_t type = dns_rdatatype_nsec;
30089689912eSchristos 	dns_typepair_t sigtype = DNS_SIGTYPE(dns_rdatatype_nsec);
30099689912eSchristos 	bool wraps = false;
30109689912eSchristos 	bool first = true;
30119689912eSchristos 	bool need_sig = secure;
30129689912eSchristos 
30139689912eSchristos 	if (nsec3) {
30149689912eSchristos 		type = dns_rdatatype_nsec3;
30159689912eSchristos 		sigtype = DNS_SIGTYPE(dns_rdatatype_nsec3);
30169689912eSchristos 		wraps = true;
30179689912eSchristos 	}
30189689912eSchristos 
30199689912eSchristos 	/*
30209689912eSchristos 	 * Use the auxiliary tree only starting with the second node in the
30219689912eSchristos 	 * hope that the original node will be right much of the time.
30229689912eSchristos 	 */
30239689912eSchristos 	result = dns_qpiter_current(&search->iter, name, (void **)&node, NULL);
30249689912eSchristos 	if (result != ISC_R_SUCCESS) {
30259689912eSchristos 		return result;
30269689912eSchristos 	}
30279689912eSchristos again:
30289689912eSchristos 	do {
30299689912eSchristos 		dns_slabheader_t *found = NULL, *foundsig = NULL;
30309689912eSchristos 		isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
30319689912eSchristos 		NODE_RDLOCK(&(search->qpdb->node_locks[node->locknum].lock),
30329689912eSchristos 			    &nlocktype);
30339689912eSchristos 		empty_node = true;
30349689912eSchristos 		for (header = node->data; header != NULL; header = header_next)
30359689912eSchristos 		{
30369689912eSchristos 			header_next = header->next;
30379689912eSchristos 			/*
30389689912eSchristos 			 * Look for an active, extant NSEC or RRSIG NSEC.
30399689912eSchristos 			 */
30409689912eSchristos 			do {
30419689912eSchristos 				if (header->serial <= search->serial &&
30429689912eSchristos 				    !IGNORE(header))
30439689912eSchristos 				{
30449689912eSchristos 					if (NONEXISTENT(header)) {
30459689912eSchristos 						header = NULL;
30469689912eSchristos 					}
30479689912eSchristos 					break;
30489689912eSchristos 				} else {
30499689912eSchristos 					header = header->down;
30509689912eSchristos 				}
30519689912eSchristos 			} while (header != NULL);
30529689912eSchristos 			if (header != NULL) {
30539689912eSchristos 				/*
30549689912eSchristos 				 * We now know that there is at least one
30559689912eSchristos 				 * active rdataset at this node.
30569689912eSchristos 				 */
30579689912eSchristos 				empty_node = false;
30589689912eSchristos 				if (header->type == type) {
30599689912eSchristos 					found = header;
30609689912eSchristos 					if (foundsig != NULL) {
30619689912eSchristos 						break;
30629689912eSchristos 					}
30639689912eSchristos 				} else if (header->type == sigtype) {
30649689912eSchristos 					foundsig = header;
30659689912eSchristos 					if (found != NULL) {
30669689912eSchristos 						break;
30679689912eSchristos 					}
30689689912eSchristos 				}
30699689912eSchristos 			}
30709689912eSchristos 		}
30719689912eSchristos 		if (!empty_node) {
30729689912eSchristos 			if (found != NULL && search->version->havensec3 &&
30739689912eSchristos 			    found->type == dns_rdatatype_nsec3 &&
30749689912eSchristos 			    !matchparams(found, search))
30759689912eSchristos 			{
30769689912eSchristos 				empty_node = true;
30779689912eSchristos 				found = NULL;
30789689912eSchristos 				foundsig = NULL;
30799689912eSchristos 				result = previous_closest_nsec(type, search,
30809689912eSchristos 							       name, &prevnode,
30819689912eSchristos 							       NULL, NULL);
30829689912eSchristos 			} else if (found != NULL &&
30839689912eSchristos 				   (foundsig != NULL || !need_sig))
30849689912eSchristos 			{
30859689912eSchristos 				/*
30869689912eSchristos 				 * We've found the right NSEC/NSEC3 record.
30879689912eSchristos 				 *
30889689912eSchristos 				 * Note: for this to really be the right
30899689912eSchristos 				 * NSEC record, it's essential that the NSEC
30909689912eSchristos 				 * records of any nodes obscured by a zone
30919689912eSchristos 				 * cut have been removed; we assume this is
30929689912eSchristos 				 * the case.
30939689912eSchristos 				 */
30949689912eSchristos 				dns_name_copy(name, foundname);
30959689912eSchristos 				if (nodep != NULL) {
30969689912eSchristos 					newref(search->qpdb,
30979689912eSchristos 					       node DNS__DB_FLARG_PASS);
30989689912eSchristos 					*nodep = node;
30999689912eSchristos 				}
31009689912eSchristos 				bindrdataset(search->qpdb, node, found,
31019689912eSchristos 					     search->now,
31029689912eSchristos 					     rdataset DNS__DB_FLARG_PASS);
31039689912eSchristos 				if (foundsig != NULL) {
31049689912eSchristos 					bindrdataset(
31059689912eSchristos 						search->qpdb, node, foundsig,
31069689912eSchristos 						search->now,
31079689912eSchristos 						sigrdataset DNS__DB_FLARG_PASS);
31089689912eSchristos 				}
31099689912eSchristos 			} else if (found == NULL && foundsig == NULL) {
31109689912eSchristos 				/*
31119689912eSchristos 				 * This node is active, but has no NSEC or
31129689912eSchristos 				 * RRSIG NSEC.  That means it's glue or
31139689912eSchristos 				 * other obscured zone data that isn't
31149689912eSchristos 				 * relevant for our search.  Treat the
31159689912eSchristos 				 * node as if it were empty and keep looking.
31169689912eSchristos 				 */
31179689912eSchristos 				empty_node = true;
31189689912eSchristos 				result = previous_closest_nsec(
31199689912eSchristos 					type, search, name, &prevnode,
31209689912eSchristos 					&nseciter, &first);
31219689912eSchristos 			} else {
31229689912eSchristos 				/*
31239689912eSchristos 				 * We found an active node, but either the
31249689912eSchristos 				 * NSEC or the RRSIG NSEC is missing.  This
31259689912eSchristos 				 * shouldn't happen.
31269689912eSchristos 				 */
31279689912eSchristos 				result = DNS_R_BADDB;
31289689912eSchristos 			}
31299689912eSchristos 		} else {
31309689912eSchristos 			/*
31319689912eSchristos 			 * This node isn't active.  We've got to keep
31329689912eSchristos 			 * looking.
31339689912eSchristos 			 */
31349689912eSchristos 			result = previous_closest_nsec(type, search, name,
31359689912eSchristos 						       &prevnode, &nseciter,
31369689912eSchristos 						       &first);
31379689912eSchristos 		}
31389689912eSchristos 		NODE_UNLOCK(&(search->qpdb->node_locks[node->locknum].lock),
31399689912eSchristos 			    &nlocktype);
31409689912eSchristos 		node = prevnode;
31419689912eSchristos 		prevnode = NULL;
31429689912eSchristos 	} while (empty_node && result == ISC_R_SUCCESS);
31439689912eSchristos 
31449689912eSchristos 	if (result == ISC_R_NOMORE && wraps) {
31459689912eSchristos 		result = dns_qpiter_prev(&search->iter, name, (void **)&node,
31469689912eSchristos 					 NULL);
31479689912eSchristos 		if (result == ISC_R_SUCCESS) {
31489689912eSchristos 			wraps = false;
31499689912eSchristos 			goto again;
31509689912eSchristos 		}
31519689912eSchristos 	}
31529689912eSchristos 
31539689912eSchristos 	/*
31549689912eSchristos 	 * If the result is ISC_R_NOMORE, then we got to the beginning of
31559689912eSchristos 	 * the database and didn't find a NSEC record.  This shouldn't
31569689912eSchristos 	 * happen.
31579689912eSchristos 	 */
31589689912eSchristos 	if (result == ISC_R_NOMORE) {
31599689912eSchristos 		result = DNS_R_BADDB;
31609689912eSchristos 	}
31619689912eSchristos 
31629689912eSchristos 	return result;
31639689912eSchristos }
31649689912eSchristos 
31659689912eSchristos static isc_result_t
31669689912eSchristos check_zonecut(qpznode_t *node, void *arg DNS__DB_FLARG) {
31679689912eSchristos 	qpz_search_t *search = arg;
31689689912eSchristos 	dns_slabheader_t *header = NULL, *header_next = NULL;
31699689912eSchristos 	dns_slabheader_t *dname_header = NULL, *sigdname_header = NULL;
31709689912eSchristos 	dns_slabheader_t *ns_header = NULL;
31719689912eSchristos 	dns_slabheader_t *found = NULL;
31729689912eSchristos 	isc_result_t result = DNS_R_CONTINUE;
31739689912eSchristos 	isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
31749689912eSchristos 
31759689912eSchristos 	NODE_RDLOCK(&(search->qpdb->node_locks[node->locknum].lock),
31769689912eSchristos 		    &nlocktype);
31779689912eSchristos 
31789689912eSchristos 	/*
31799689912eSchristos 	 * Look for an NS or DNAME rdataset active in our version.
31809689912eSchristos 	 */
31819689912eSchristos 	for (header = node->data; header != NULL; header = header_next) {
31829689912eSchristos 		header_next = header->next;
31839689912eSchristos 		if (header->type == dns_rdatatype_ns ||
31849689912eSchristos 		    header->type == dns_rdatatype_dname ||
31859689912eSchristos 		    header->type == DNS_SIGTYPE(dns_rdatatype_dname))
31869689912eSchristos 		{
31879689912eSchristos 			do {
31889689912eSchristos 				if (header->serial <= search->serial &&
31899689912eSchristos 				    !IGNORE(header))
31909689912eSchristos 				{
31919689912eSchristos 					if (NONEXISTENT(header)) {
31929689912eSchristos 						header = NULL;
31939689912eSchristos 					}
31949689912eSchristos 					break;
31959689912eSchristos 				} else {
31969689912eSchristos 					header = header->down;
31979689912eSchristos 				}
31989689912eSchristos 			} while (header != NULL);
31999689912eSchristos 			if (header != NULL) {
32009689912eSchristos 				if (header->type == dns_rdatatype_dname) {
32019689912eSchristos 					dname_header = header;
32029689912eSchristos 				} else if (header->type ==
32039689912eSchristos 					   DNS_SIGTYPE(dns_rdatatype_dname))
32049689912eSchristos 				{
32059689912eSchristos 					sigdname_header = header;
32069689912eSchristos 				} else if (node != search->qpdb->origin ||
32079689912eSchristos 					   IS_STUB(search->qpdb))
32089689912eSchristos 				{
32099689912eSchristos 					/*
32109689912eSchristos 					 * We've found an NS rdataset that
32119689912eSchristos 					 * isn't at the origin node.
32129689912eSchristos 					 */
32139689912eSchristos 					ns_header = header;
32149689912eSchristos 				}
32159689912eSchristos 			}
32169689912eSchristos 		}
32179689912eSchristos 	}
32189689912eSchristos 
32199689912eSchristos 	/*
32209689912eSchristos 	 * Did we find anything?
32219689912eSchristos 	 */
32229689912eSchristos 	if (!IS_STUB(search->qpdb) && ns_header != NULL) {
32239689912eSchristos 		/*
32249689912eSchristos 		 * Note that NS has precedence over DNAME if both exist
32259689912eSchristos 		 * in a zone.  Otherwise DNAME take precedence over NS.
32269689912eSchristos 		 */
32279689912eSchristos 		found = ns_header;
32289689912eSchristos 		search->zonecut_sigheader = NULL;
32299689912eSchristos 	} else if (dname_header != NULL) {
32309689912eSchristos 		found = dname_header;
32319689912eSchristos 		search->zonecut_sigheader = sigdname_header;
32329689912eSchristos 	} else if (ns_header != NULL) {
32339689912eSchristos 		found = ns_header;
32349689912eSchristos 		search->zonecut_sigheader = NULL;
32359689912eSchristos 	}
32369689912eSchristos 
32379689912eSchristos 	if (found != NULL) {
32389689912eSchristos 		/*
32399689912eSchristos 		 * We increment the reference count on node to ensure that
32409689912eSchristos 		 * search->zonecut_header will still be valid later.
32419689912eSchristos 		 */
32429689912eSchristos 		newref(search->qpdb, node DNS__DB_FLARG_PASS);
32439689912eSchristos 		search->zonecut = node;
32449689912eSchristos 		search->zonecut_header = found;
32459689912eSchristos 		search->need_cleanup = true;
32469689912eSchristos 		/*
32479689912eSchristos 		 * Since we've found a zonecut, anything beneath it is
32489689912eSchristos 		 * glue and is not subject to wildcard matching, so we
32499689912eSchristos 		 * may clear search->wild.
32509689912eSchristos 		 */
32519689912eSchristos 		search->wild = false;
32529689912eSchristos 		if ((search->options & DNS_DBFIND_GLUEOK) == 0) {
32539689912eSchristos 			/*
32549689912eSchristos 			 * If the caller does not want to find glue, then
32559689912eSchristos 			 * this is the best answer and the search should
32569689912eSchristos 			 * stop now.
32579689912eSchristos 			 */
32589689912eSchristos 			result = DNS_R_PARTIALMATCH;
32599689912eSchristos 		} else {
32609689912eSchristos 			dns_name_t *zcname = NULL;
32619689912eSchristos 
32629689912eSchristos 			/*
32639689912eSchristos 			 * The search will continue beneath the zone cut.
32649689912eSchristos 			 * This may or may not be the best match.  In case it
32659689912eSchristos 			 * is, we need to remember the node name.
32669689912eSchristos 			 */
32679689912eSchristos 			zcname = dns_fixedname_name(&search->zonecut_name);
32689689912eSchristos 			dns_name_copy(&node->name, zcname);
32699689912eSchristos 			search->copy_name = true;
32709689912eSchristos 		}
32719689912eSchristos 	} else {
32729689912eSchristos 		/*
32739689912eSchristos 		 * There is no zonecut at this node which is active in this
32749689912eSchristos 		 * version.
32759689912eSchristos 		 *
32769689912eSchristos 		 * If this is a "wild" node and the caller hasn't disabled
32779689912eSchristos 		 * wildcard matching, remember that we've seen a wild node
32789689912eSchristos 		 * in case we need to go searching for wildcard matches
32799689912eSchristos 		 * later on.
32809689912eSchristos 		 */
32819689912eSchristos 		if (node->wild && (search->options & DNS_DBFIND_NOWILD) == 0) {
32829689912eSchristos 			search->wild = true;
32839689912eSchristos 		}
32849689912eSchristos 	}
32859689912eSchristos 
32869689912eSchristos 	NODE_UNLOCK(&(search->qpdb->node_locks[node->locknum].lock),
32879689912eSchristos 		    &nlocktype);
32889689912eSchristos 
32899689912eSchristos 	return result;
32909689912eSchristos }
32919689912eSchristos 
32929689912eSchristos static isc_result_t
32939689912eSchristos find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
32949689912eSchristos      dns_rdatatype_t type, unsigned int options,
32959689912eSchristos      isc_stdtime_t now ISC_ATTR_UNUSED, dns_dbnode_t **nodep,
32969689912eSchristos      dns_name_t *foundname, dns_rdataset_t *rdataset,
32979689912eSchristos      dns_rdataset_t *sigrdataset DNS__DB_FLARG) {
32989689912eSchristos 	isc_result_t result;
32999689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)db;
33009689912eSchristos 	qpznode_t *node = NULL;
33019689912eSchristos 	qpz_search_t search;
33029689912eSchristos 	bool cname_ok = true, close_version = false;
33039689912eSchristos 	bool maybe_zonecut = false, at_zonecut = false;
33049689912eSchristos 	bool wild = false, empty_node = false;
33059689912eSchristos 	bool nsec3 = false;
33069689912eSchristos 	dns_slabheader_t *header = NULL, *header_next = NULL;
33079689912eSchristos 	dns_slabheader_t *found = NULL, *nsecheader = NULL;
33089689912eSchristos 	dns_slabheader_t *foundsig = NULL, *cnamesig = NULL, *nsecsig = NULL;
33099689912eSchristos 	dns_typepair_t sigtype;
33109689912eSchristos 	bool active;
33119689912eSchristos 	isc_rwlock_t *lock = NULL;
33129689912eSchristos 	isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
33139689912eSchristos 
33149689912eSchristos 	REQUIRE(VALID_QPZONE((qpzonedb_t *)db));
33159689912eSchristos 	INSIST(version == NULL ||
33169689912eSchristos 	       ((qpz_version_t *)version)->qpdb == (qpzonedb_t *)db);
33179689912eSchristos 
33189689912eSchristos 	/*
33199689912eSchristos 	 * If the caller didn't supply a version, attach to the current
33209689912eSchristos 	 * version.
33219689912eSchristos 	 */
33229689912eSchristos 	if (version == NULL) {
33239689912eSchristos 		currentversion(db, &version);
33249689912eSchristos 		close_version = true;
33259689912eSchristos 	}
33269689912eSchristos 
33279689912eSchristos 	search = (qpz_search_t){
33289689912eSchristos 		.qpdb = (qpzonedb_t *)db,
33299689912eSchristos 		.version = version,
33309689912eSchristos 		.serial = ((qpz_version_t *)version)->serial,
33319689912eSchristos 		.options = options,
33329689912eSchristos 	};
33339689912eSchristos 	dns_fixedname_init(&search.zonecut_name);
33349689912eSchristos 
33359689912eSchristos 	if ((options & DNS_DBFIND_FORCENSEC3) != 0) {
33369689912eSchristos 		dns_qpmulti_query(qpdb->nsec3, &search.qpr);
33379689912eSchristos 		nsec3 = true;
33389689912eSchristos 	} else {
33399689912eSchristos 		dns_qpmulti_query(qpdb->tree, &search.qpr);
33409689912eSchristos 	}
33419689912eSchristos 
33429689912eSchristos 	/*
33439689912eSchristos 	 * Search down from the root of the tree.
33449689912eSchristos 	 */
33459689912eSchristos 	result = dns_qp_lookup(&search.qpr, name, NULL, &search.iter,
33469689912eSchristos 			       &search.chain, (void **)&node, NULL);
33479689912eSchristos 	if (result != ISC_R_NOTFOUND) {
33489689912eSchristos 		dns_name_copy(&node->name, foundname);
33499689912eSchristos 	}
33509689912eSchristos 
33519689912eSchristos 	/*
33529689912eSchristos 	 * Check the QP chain to see if there's a node above us with a
33539689912eSchristos 	 * active DNAME or NS rdatasets.
33549689912eSchristos 	 *
33559689912eSchristos 	 * We're only interested in nodes above QNAME, so if the result
33569689912eSchristos 	 * was success, then we skip the last item in the chain.
33579689912eSchristos 	 */
33589689912eSchristos 	unsigned int clen = dns_qpchain_length(&search.chain);
33599689912eSchristos 	if (result == ISC_R_SUCCESS) {
33609689912eSchristos 		clen--;
33619689912eSchristos 	}
33629689912eSchristos 	for (unsigned int i = 0; i < clen && search.zonecut == NULL; i++) {
33639689912eSchristos 		qpznode_t *n = NULL;
33649689912eSchristos 		isc_result_t tresult;
33659689912eSchristos 
33669689912eSchristos 		dns_qpchain_node(&search.chain, i, NULL, (void **)&n, NULL);
33679689912eSchristos 		tresult = check_zonecut(n, &search DNS__DB_FLARG_PASS);
33689689912eSchristos 		if (tresult != DNS_R_CONTINUE) {
33699689912eSchristos 			result = tresult;
33709689912eSchristos 			search.chain.len = i - 1;
33719689912eSchristos 			node = n;
33729689912eSchristos 			if (foundname != NULL) {
33739689912eSchristos 				dns_name_copy(&node->name, foundname);
33749689912eSchristos 			}
33759689912eSchristos 		}
33769689912eSchristos 	}
33779689912eSchristos 
33789689912eSchristos 	if (result == DNS_R_PARTIALMATCH) {
33799689912eSchristos 	partial_match:
33809689912eSchristos 		if (search.zonecut != NULL) {
33819689912eSchristos 			result = setup_delegation(
33829689912eSchristos 				&search, nodep, foundname, rdataset,
33839689912eSchristos 				sigrdataset DNS__DB_FLARG_PASS);
33849689912eSchristos 			goto tree_exit;
33859689912eSchristos 		}
33869689912eSchristos 
33879689912eSchristos 		if (search.wild) {
33889689912eSchristos 			/*
33899689912eSchristos 			 * At least one of the levels in the search chain
33909689912eSchristos 			 * potentially has a wildcard.  For each such level,
33919689912eSchristos 			 * we must see if there's a matching wildcard active
33929689912eSchristos 			 * in the current version.
33939689912eSchristos 			 */
33949689912eSchristos 			result = find_wildcard(&search, &node, name);
33959689912eSchristos 			if (result == ISC_R_SUCCESS) {
33969689912eSchristos 				dns_name_copy(name, foundname);
33979689912eSchristos 				wild = true;
33989689912eSchristos 				goto found;
33999689912eSchristos 			} else if (result != ISC_R_NOTFOUND) {
34009689912eSchristos 				goto tree_exit;
34019689912eSchristos 			}
34029689912eSchristos 		}
34039689912eSchristos 
34049689912eSchristos 		active = false;
34059689912eSchristos 		if (!nsec3) {
34069689912eSchristos 			/*
34079689912eSchristos 			 * The NSEC3 tree won't have empty nodes,
34089689912eSchristos 			 * so it isn't necessary to check for them.
34099689912eSchristos 			 */
34109689912eSchristos 			dns_qpiter_t iter = search.iter;
34119689912eSchristos 			active = activeempty(&search, &iter, name);
34129689912eSchristos 		}
34139689912eSchristos 
34149689912eSchristos 		/*
34159689912eSchristos 		 * If we're here, then the name does not exist, is not
34169689912eSchristos 		 * beneath a zonecut, and there's no matching wildcard.
34179689912eSchristos 		 */
34189689912eSchristos 		if ((search.version->secure && !search.version->havensec3) ||
34199689912eSchristos 		    nsec3)
34209689912eSchristos 		{
34219689912eSchristos 			result = find_closest_nsec(
34229689912eSchristos 				&search, nodep, foundname, rdataset,
34239689912eSchristos 				sigrdataset, nsec3,
34249689912eSchristos 				search.version->secure DNS__DB_FLARG_PASS);
34259689912eSchristos 			if (result == ISC_R_SUCCESS) {
34269689912eSchristos 				result = active ? DNS_R_EMPTYNAME
34279689912eSchristos 						: DNS_R_NXDOMAIN;
34289689912eSchristos 			}
34299689912eSchristos 		} else {
34309689912eSchristos 			result = active ? DNS_R_EMPTYNAME : DNS_R_NXDOMAIN;
34319689912eSchristos 		}
34329689912eSchristos 		goto tree_exit;
34339689912eSchristos 	} else if (result != ISC_R_SUCCESS) {
34349689912eSchristos 		goto tree_exit;
34359689912eSchristos 	}
34369689912eSchristos 
34379689912eSchristos found:
34389689912eSchristos 	/*
34399689912eSchristos 	 * We have found a node whose name is the desired name, or we
34409689912eSchristos 	 * have matched a wildcard.
34419689912eSchristos 	 */
34429689912eSchristos 
34439689912eSchristos 	lock = &search.qpdb->node_locks[node->locknum].lock;
34449689912eSchristos 	NODE_RDLOCK(lock, &nlocktype);
34459689912eSchristos 
34469689912eSchristos 	if (search.zonecut != NULL) {
34479689912eSchristos 		/*
34489689912eSchristos 		 * If we're beneath a zone cut, we don't want to look for
34499689912eSchristos 		 * CNAMEs because they're not legitimate zone glue.
34509689912eSchristos 		 */
34519689912eSchristos 		cname_ok = false;
34529689912eSchristos 	} else {
34539689912eSchristos 		/*
34549689912eSchristos 		 * The node may be a zone cut itself.  If it might be one,
34559689912eSchristos 		 * make sure we check for it later.
34569689912eSchristos 		 *
34579689912eSchristos 		 * DS records live above the zone cut in ordinary zone so
34589689912eSchristos 		 * we want to ignore any referral.
34599689912eSchristos 		 *
34609689912eSchristos 		 * Stub zones don't have anything "above" the delegation so
34619689912eSchristos 		 * we always return a referral.
34629689912eSchristos 		 */
34639689912eSchristos 		if (node->delegating && ((node != search.qpdb->origin &&
34649689912eSchristos 					  !dns_rdatatype_atparent(type)) ||
34659689912eSchristos 					 IS_STUB(search.qpdb)))
34669689912eSchristos 		{
34679689912eSchristos 			maybe_zonecut = true;
34689689912eSchristos 		}
34699689912eSchristos 	}
34709689912eSchristos 
34719689912eSchristos 	/*
34729689912eSchristos 	 * Certain DNSSEC types are not subject to CNAME matching
34739689912eSchristos 	 * (RFC4035, section 2.5 and RFC3007).
34749689912eSchristos 	 *
34759689912eSchristos 	 * We don't check for RRSIG, because we don't store RRSIG records
34769689912eSchristos 	 * directly.
34779689912eSchristos 	 */
34789689912eSchristos 	if (type == dns_rdatatype_key || type == dns_rdatatype_nsec) {
34799689912eSchristos 		cname_ok = false;
34809689912eSchristos 	}
34819689912eSchristos 
34829689912eSchristos 	/*
34839689912eSchristos 	 * We now go looking for rdata...
34849689912eSchristos 	 */
34859689912eSchristos 
34869689912eSchristos 	sigtype = DNS_SIGTYPE(type);
34879689912eSchristos 	empty_node = true;
34889689912eSchristos 	for (header = node->data; header != NULL; header = header_next) {
34899689912eSchristos 		header_next = header->next;
34909689912eSchristos 		/*
34919689912eSchristos 		 * Look for an active, extant rdataset.
34929689912eSchristos 		 */
34939689912eSchristos 		do {
34949689912eSchristos 			if (header->serial <= search.serial && !IGNORE(header))
34959689912eSchristos 			{
34969689912eSchristos 				if (NONEXISTENT(header)) {
34979689912eSchristos 					header = NULL;
34989689912eSchristos 				}
34999689912eSchristos 				break;
35009689912eSchristos 			} else {
35019689912eSchristos 				header = header->down;
35029689912eSchristos 			}
35039689912eSchristos 		} while (header != NULL);
35049689912eSchristos 		if (header != NULL) {
35059689912eSchristos 			/*
35069689912eSchristos 			 * We now know that there is at least one active
35079689912eSchristos 			 * rdataset at this node.
35089689912eSchristos 			 */
35099689912eSchristos 			empty_node = false;
35109689912eSchristos 
35119689912eSchristos 			/*
35129689912eSchristos 			 * Do special zone cut handling, if requested.
35139689912eSchristos 			 */
35149689912eSchristos 			if (maybe_zonecut && header->type == dns_rdatatype_ns) {
35159689912eSchristos 				/*
35169689912eSchristos 				 * We increment the reference count on node to
35179689912eSchristos 				 * ensure that search->zonecut_header will
35189689912eSchristos 				 * still be valid later.
35199689912eSchristos 				 */
35209689912eSchristos 				newref(search.qpdb, node DNS__DB_FLARG_PASS);
35219689912eSchristos 				search.zonecut = node;
35229689912eSchristos 				search.zonecut_header = header;
35239689912eSchristos 				search.zonecut_sigheader = NULL;
35249689912eSchristos 				search.need_cleanup = true;
35259689912eSchristos 				maybe_zonecut = false;
35269689912eSchristos 				at_zonecut = true;
35279689912eSchristos 				/*
35289689912eSchristos 				 * It is not clear if KEY should still be
35299689912eSchristos 				 * allowed at the parent side of the zone
35309689912eSchristos 				 * cut or not.  It is needed for RFC3007
35319689912eSchristos 				 * validated updates.
35329689912eSchristos 				 */
35339689912eSchristos 				if ((search.options & DNS_DBFIND_GLUEOK) == 0 &&
35349689912eSchristos 				    type != dns_rdatatype_nsec &&
35359689912eSchristos 				    type != dns_rdatatype_key)
35369689912eSchristos 				{
35379689912eSchristos 					/*
35389689912eSchristos 					 * Glue is not OK, but any answer we
35399689912eSchristos 					 * could return would be glue.  Return
35409689912eSchristos 					 * the delegation.
35419689912eSchristos 					 */
35429689912eSchristos 					found = NULL;
35439689912eSchristos 					break;
35449689912eSchristos 				}
35459689912eSchristos 				if (found != NULL && foundsig != NULL) {
35469689912eSchristos 					break;
35479689912eSchristos 				}
35489689912eSchristos 			}
35499689912eSchristos 
35509689912eSchristos 			/*
35519689912eSchristos 			 * If the NSEC3 record doesn't match the chain
35529689912eSchristos 			 * we are using behave as if it isn't here.
35539689912eSchristos 			 */
35549689912eSchristos 			if (header->type == dns_rdatatype_nsec3 &&
35559689912eSchristos 			    !matchparams(header, &search))
35569689912eSchristos 			{
35579689912eSchristos 				NODE_UNLOCK(lock, &nlocktype);
35589689912eSchristos 				goto partial_match;
35599689912eSchristos 			}
35609689912eSchristos 			/*
35619689912eSchristos 			 * If we found a type we were looking for,
35629689912eSchristos 			 * remember it.
35639689912eSchristos 			 */
35649689912eSchristos 			if (header->type == type || type == dns_rdatatype_any ||
35659689912eSchristos 			    (header->type == dns_rdatatype_cname && cname_ok))
35669689912eSchristos 			{
35679689912eSchristos 				/*
35689689912eSchristos 				 * We've found the answer!
35699689912eSchristos 				 */
35709689912eSchristos 				found = header;
35719689912eSchristos 				if (header->type == dns_rdatatype_cname &&
35729689912eSchristos 				    cname_ok)
35739689912eSchristos 				{
35749689912eSchristos 					/*
35759689912eSchristos 					 * We may be finding a CNAME instead
35769689912eSchristos 					 * of the desired type.
35779689912eSchristos 					 *
35789689912eSchristos 					 * If we've already got the CNAME RRSIG,
35799689912eSchristos 					 * use it, otherwise change sigtype
35809689912eSchristos 					 * so that we find it.
35819689912eSchristos 					 */
35829689912eSchristos 					if (cnamesig != NULL) {
35839689912eSchristos 						foundsig = cnamesig;
35849689912eSchristos 					} else {
35859689912eSchristos 						sigtype = DNS_SIGTYPE(
35869689912eSchristos 							dns_rdatatype_cname);
35879689912eSchristos 					}
35889689912eSchristos 				}
35899689912eSchristos 				/*
35909689912eSchristos 				 * If we've got all we need, end the search.
35919689912eSchristos 				 */
35929689912eSchristos 				if (!maybe_zonecut && foundsig != NULL) {
35939689912eSchristos 					break;
35949689912eSchristos 				}
35959689912eSchristos 			} else if (header->type == sigtype) {
35969689912eSchristos 				/*
35979689912eSchristos 				 * We've found the RRSIG rdataset for our
35989689912eSchristos 				 * target type.  Remember it.
35999689912eSchristos 				 */
36009689912eSchristos 				foundsig = header;
36019689912eSchristos 				/*
36029689912eSchristos 				 * If we've got all we need, end the search.
36039689912eSchristos 				 */
36049689912eSchristos 				if (!maybe_zonecut && found != NULL) {
36059689912eSchristos 					break;
36069689912eSchristos 				}
36079689912eSchristos 			} else if (header->type == dns_rdatatype_nsec &&
36089689912eSchristos 				   !search.version->havensec3)
36099689912eSchristos 			{
36109689912eSchristos 				/*
36119689912eSchristos 				 * Remember a NSEC rdataset even if we're
36129689912eSchristos 				 * not specifically looking for it, because
36139689912eSchristos 				 * we might need it later.
36149689912eSchristos 				 */
36159689912eSchristos 				nsecheader = header;
36169689912eSchristos 			} else if (header->type ==
36179689912eSchristos 					   DNS_SIGTYPE(dns_rdatatype_nsec) &&
36189689912eSchristos 				   !search.version->havensec3)
36199689912eSchristos 			{
36209689912eSchristos 				/*
36219689912eSchristos 				 * If we need the NSEC rdataset, we'll also
36229689912eSchristos 				 * need its signature.
36239689912eSchristos 				 */
36249689912eSchristos 				nsecsig = header;
36259689912eSchristos 			} else if (cname_ok &&
36269689912eSchristos 				   header->type ==
36279689912eSchristos 					   DNS_SIGTYPE(dns_rdatatype_cname))
36289689912eSchristos 			{
36299689912eSchristos 				/*
36309689912eSchristos 				 * If we get a CNAME match, we'll also need
36319689912eSchristos 				 * its signature.
36329689912eSchristos 				 */
36339689912eSchristos 				cnamesig = header;
36349689912eSchristos 			}
36359689912eSchristos 		}
36369689912eSchristos 	}
36379689912eSchristos 
36389689912eSchristos 	if (empty_node) {
36399689912eSchristos 		/*
36409689912eSchristos 		 * We have an exact match for the name, but there are no
36419689912eSchristos 		 * active rdatasets in the desired version.  That means that
36429689912eSchristos 		 * this node doesn't exist in the desired version, and that
36439689912eSchristos 		 * we really have a partial match.
36449689912eSchristos 		 */
36459689912eSchristos 		if (!wild) {
36469689912eSchristos 			NODE_UNLOCK(lock, &nlocktype);
36479689912eSchristos 			goto partial_match;
36489689912eSchristos 		}
36499689912eSchristos 	}
36509689912eSchristos 
36519689912eSchristos 	/*
36529689912eSchristos 	 * If we didn't find what we were looking for...
36539689912eSchristos 	 */
36549689912eSchristos 	if (found == NULL) {
36559689912eSchristos 		if (search.zonecut != NULL) {
36569689912eSchristos 			/*
36579689912eSchristos 			 * We were trying to find glue at a node beneath a
36589689912eSchristos 			 * zone cut, but didn't.
36599689912eSchristos 			 *
36609689912eSchristos 			 * Return the delegation.
36619689912eSchristos 			 */
36629689912eSchristos 			NODE_UNLOCK(lock, &nlocktype);
36639689912eSchristos 			result = setup_delegation(
36649689912eSchristos 				&search, nodep, foundname, rdataset,
36659689912eSchristos 				sigrdataset DNS__DB_FLARG_PASS);
36669689912eSchristos 			goto tree_exit;
36679689912eSchristos 		}
36689689912eSchristos 		/*
36699689912eSchristos 		 * The desired type doesn't exist.
36709689912eSchristos 		 */
36719689912eSchristos 		result = DNS_R_NXRRSET;
36729689912eSchristos 		if (search.version->secure && !search.version->havensec3 &&
36739689912eSchristos 		    (nsecheader == NULL || nsecsig == NULL))
36749689912eSchristos 		{
36759689912eSchristos 			/*
36769689912eSchristos 			 * The zone is secure but there's no NSEC,
36779689912eSchristos 			 * or the NSEC has no signature!
36789689912eSchristos 			 */
36799689912eSchristos 			if (!wild) {
36809689912eSchristos 				result = DNS_R_BADDB;
36819689912eSchristos 				goto node_exit;
36829689912eSchristos 			}
36839689912eSchristos 
36849689912eSchristos 			NODE_UNLOCK(lock, &nlocktype);
36859689912eSchristos 			result = find_closest_nsec(
36869689912eSchristos 				&search, nodep, foundname, rdataset,
36879689912eSchristos 				sigrdataset, false,
36889689912eSchristos 				search.version->secure DNS__DB_FLARG_PASS);
36899689912eSchristos 			if (result == ISC_R_SUCCESS) {
36909689912eSchristos 				result = DNS_R_EMPTYWILD;
36919689912eSchristos 			}
36929689912eSchristos 			goto tree_exit;
36939689912eSchristos 		}
36949689912eSchristos 		if (nodep != NULL) {
36959689912eSchristos 			newref(search.qpdb, node DNS__DB_FLARG_PASS);
36969689912eSchristos 			*nodep = node;
36979689912eSchristos 		}
36989689912eSchristos 		if (search.version->secure && !search.version->havensec3) {
36999689912eSchristos 			bindrdataset(search.qpdb, node, nsecheader, 0,
37009689912eSchristos 				     rdataset DNS__DB_FLARG_PASS);
37019689912eSchristos 			if (nsecsig != NULL) {
37029689912eSchristos 				bindrdataset(search.qpdb, node, nsecsig, 0,
37039689912eSchristos 					     sigrdataset DNS__DB_FLARG_PASS);
37049689912eSchristos 			}
37059689912eSchristos 		}
37069689912eSchristos 		if (wild) {
37079689912eSchristos 			foundname->attributes.wildcard = true;
37089689912eSchristos 		}
37099689912eSchristos 		goto node_exit;
37109689912eSchristos 	}
37119689912eSchristos 
37129689912eSchristos 	/*
37139689912eSchristos 	 * We found what we were looking for, or we found a CNAME.
37149689912eSchristos 	 */
37159689912eSchristos 	if (type != found->type && type != dns_rdatatype_any &&
37169689912eSchristos 	    found->type == dns_rdatatype_cname)
37179689912eSchristos 	{
37189689912eSchristos 		/*
37199689912eSchristos 		 * We weren't doing an ANY query and we found a CNAME instead
37209689912eSchristos 		 * of the type we were looking for, so we need to indicate
37219689912eSchristos 		 * that result to the caller.
37229689912eSchristos 		 */
37239689912eSchristos 		result = DNS_R_CNAME;
37249689912eSchristos 	} else if (search.zonecut != NULL) {
37259689912eSchristos 		/*
37269689912eSchristos 		 * If we're beneath a zone cut, we must indicate that the
37279689912eSchristos 		 * result is glue, unless we're actually at the zone cut
37289689912eSchristos 		 * and the type is NSEC or KEY.
37299689912eSchristos 		 */
37309689912eSchristos 		if (search.zonecut == node) {
37319689912eSchristos 			/*
37329689912eSchristos 			 * It is not clear if KEY should still be
37339689912eSchristos 			 * allowed at the parent side of the zone
37349689912eSchristos 			 * cut or not.  It is needed for RFC3007
37359689912eSchristos 			 * validated updates.
37369689912eSchristos 			 */
37379689912eSchristos 			if (type == dns_rdatatype_nsec ||
37389689912eSchristos 			    type == dns_rdatatype_nsec3 ||
37399689912eSchristos 			    type == dns_rdatatype_key)
37409689912eSchristos 			{
37419689912eSchristos 				result = ISC_R_SUCCESS;
37429689912eSchristos 			} else if (type == dns_rdatatype_any) {
37439689912eSchristos 				result = DNS_R_ZONECUT;
37449689912eSchristos 			} else {
37459689912eSchristos 				result = DNS_R_GLUE;
37469689912eSchristos 			}
37479689912eSchristos 		} else {
37489689912eSchristos 			result = DNS_R_GLUE;
37499689912eSchristos 		}
37509689912eSchristos 	} else {
37519689912eSchristos 		/*
37529689912eSchristos 		 * An ordinary successful query!
37539689912eSchristos 		 */
37549689912eSchristos 		result = ISC_R_SUCCESS;
37559689912eSchristos 	}
37569689912eSchristos 
37579689912eSchristos 	if (nodep != NULL) {
37589689912eSchristos 		if (!at_zonecut) {
37599689912eSchristos 			newref(search.qpdb, node DNS__DB_FLARG_PASS);
37609689912eSchristos 		} else {
37619689912eSchristos 			search.need_cleanup = false;
37629689912eSchristos 		}
37639689912eSchristos 		*nodep = node;
37649689912eSchristos 	}
37659689912eSchristos 
37669689912eSchristos 	if (type != dns_rdatatype_any) {
37679689912eSchristos 		bindrdataset(search.qpdb, node, found, 0,
37689689912eSchristos 			     rdataset DNS__DB_FLARG_PASS);
37699689912eSchristos 		if (foundsig != NULL) {
37709689912eSchristos 			bindrdataset(search.qpdb, node, foundsig, 0,
37719689912eSchristos 				     sigrdataset DNS__DB_FLARG_PASS);
37729689912eSchristos 		}
37739689912eSchristos 	}
37749689912eSchristos 
37759689912eSchristos 	if (wild) {
37769689912eSchristos 		foundname->attributes.wildcard = true;
37779689912eSchristos 	}
37789689912eSchristos 
37799689912eSchristos node_exit:
37809689912eSchristos 	NODE_UNLOCK(lock, &nlocktype);
37819689912eSchristos 
37829689912eSchristos tree_exit:
37839689912eSchristos 	if (nsec3) {
37849689912eSchristos 		dns_qpread_destroy(qpdb->nsec3, &search.qpr);
37859689912eSchristos 	} else {
37869689912eSchristos 		dns_qpread_destroy(qpdb->tree, &search.qpr);
37879689912eSchristos 	}
37889689912eSchristos 
37899689912eSchristos 	/*
37909689912eSchristos 	 * If we found a zonecut but aren't going to use it, we have to
37919689912eSchristos 	 * let go of it.
37929689912eSchristos 	 */
37939689912eSchristos 	if (search.need_cleanup) {
37949689912eSchristos 		node = search.zonecut;
37959689912eSchristos 		INSIST(node != NULL);
37969689912eSchristos 		lock = &(search.qpdb->node_locks[node->locknum].lock);
37979689912eSchristos 
37989689912eSchristos 		NODE_RDLOCK(lock, &nlocktype);
37999689912eSchristos 		decref(search.qpdb, node, 0, &nlocktype DNS__DB_FLARG_PASS);
38009689912eSchristos 		NODE_UNLOCK(lock, &nlocktype);
38019689912eSchristos 	}
38029689912eSchristos 
38039689912eSchristos 	if (close_version) {
38049689912eSchristos 		closeversion(db, &version, false DNS__DB_FLARG_PASS);
38059689912eSchristos 	}
38069689912eSchristos 
38079689912eSchristos 	return result;
38089689912eSchristos }
38099689912eSchristos 
38109689912eSchristos static isc_result_t
38119689912eSchristos allrdatasets(dns_db_t *db, dns_dbnode_t *dbnode, dns_dbversion_t *dbversion,
38129689912eSchristos 	     unsigned int options, isc_stdtime_t now ISC_ATTR_UNUSED,
38139689912eSchristos 	     dns_rdatasetiter_t **iteratorp DNS__DB_FLARG) {
38149689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)db;
38159689912eSchristos 	qpznode_t *node = (qpznode_t *)dbnode;
38169689912eSchristos 	qpz_version_t *version = dbversion;
38179689912eSchristos 	qpdb_rdatasetiter_t *iterator = NULL;
38189689912eSchristos 
38199689912eSchristos 	REQUIRE(VALID_QPZONE(qpdb));
38209689912eSchristos 
38219689912eSchristos 	if (version == NULL) {
38229689912eSchristos 		currentversion(db, (dns_dbversion_t **)(void *)(&version));
38239689912eSchristos 	} else {
38249689912eSchristos 		INSIST(version->qpdb == qpdb);
38259689912eSchristos 		isc_refcount_increment(&version->references);
38269689912eSchristos 	}
38279689912eSchristos 
38289689912eSchristos 	iterator = isc_mem_get(qpdb->common.mctx, sizeof(*iterator));
38299689912eSchristos 	*iterator = (qpdb_rdatasetiter_t){
38309689912eSchristos 		.common.methods = &rdatasetiter_methods,
38319689912eSchristos 		.common.db = db,
38329689912eSchristos 		.common.node = node,
38339689912eSchristos 		.common.version = (dns_dbversion_t *)version,
38349689912eSchristos 		.common.options = options,
38359689912eSchristos 		.common.magic = DNS_RDATASETITER_MAGIC,
38369689912eSchristos 	};
38379689912eSchristos 
38389689912eSchristos 	newref(qpdb, node DNS__DB_FLARG_PASS);
38399689912eSchristos 
38409689912eSchristos 	*iteratorp = (dns_rdatasetiter_t *)iterator;
38419689912eSchristos 	return ISC_R_SUCCESS;
38429689912eSchristos }
38439689912eSchristos 
38449689912eSchristos static void
38459689912eSchristos attachnode(dns_db_t *db, dns_dbnode_t *source,
38469689912eSchristos 	   dns_dbnode_t **targetp DNS__DB_FLARG) {
38479689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)db;
38489689912eSchristos 	qpznode_t *node = (qpznode_t *)source;
38499689912eSchristos 
38509689912eSchristos 	REQUIRE(VALID_QPZONE(qpdb));
38519689912eSchristos 	REQUIRE(targetp != NULL && *targetp == NULL);
38529689912eSchristos 
38539689912eSchristos 	newref(qpdb, node DNS__DB_FLARG_PASS);
38549689912eSchristos 
38559689912eSchristos 	*targetp = source;
38569689912eSchristos }
38579689912eSchristos 
38589689912eSchristos static void
38599689912eSchristos detachnode(dns_db_t *db, dns_dbnode_t **targetp DNS__DB_FLARG) {
38609689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)db;
38619689912eSchristos 	qpznode_t *node = NULL;
38629689912eSchristos 	bool want_free = false;
38639689912eSchristos 	bool inactive = false;
38649689912eSchristos 	db_nodelock_t *nodelock = NULL;
38659689912eSchristos 	isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
38669689912eSchristos 
38679689912eSchristos 	REQUIRE(VALID_QPZONE(qpdb));
38689689912eSchristos 	REQUIRE(targetp != NULL && *targetp != NULL);
38699689912eSchristos 
38709689912eSchristos 	node = (qpznode_t *)(*targetp);
38719689912eSchristos 	nodelock = &qpdb->node_locks[node->locknum];
38729689912eSchristos 
38739689912eSchristos 	NODE_RDLOCK(&nodelock->lock, &nlocktype);
38749689912eSchristos 	decref(qpdb, node, 0, &nlocktype DNS__DB_FLARG_PASS);
38759689912eSchristos 	if (isc_refcount_current(&nodelock->references) == 0 &&
38769689912eSchristos 	    nodelock->exiting)
38779689912eSchristos 	{
38789689912eSchristos 		inactive = true;
38799689912eSchristos 	}
38809689912eSchristos 	NODE_UNLOCK(&nodelock->lock, &nlocktype);
38819689912eSchristos 
38829689912eSchristos 	*targetp = NULL;
38839689912eSchristos 
38849689912eSchristos 	if (inactive) {
38859689912eSchristos 		RWLOCK(&qpdb->lock, isc_rwlocktype_write);
38869689912eSchristos 		qpdb->active--;
38879689912eSchristos 		if (qpdb->active == 0) {
38889689912eSchristos 			want_free = true;
38899689912eSchristos 		}
38909689912eSchristos 		RWUNLOCK(&qpdb->lock, isc_rwlocktype_write);
38919689912eSchristos 		if (want_free) {
38929689912eSchristos 			char buf[DNS_NAME_FORMATSIZE];
38939689912eSchristos 			if (dns_name_dynamic(&qpdb->common.origin)) {
38949689912eSchristos 				dns_name_format(&qpdb->common.origin, buf,
38959689912eSchristos 						sizeof(buf));
38969689912eSchristos 			} else {
38979689912eSchristos 				strlcpy(buf, "<UNKNOWN>", sizeof(buf));
38989689912eSchristos 			}
38999689912eSchristos 			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
39009689912eSchristos 				      DNS_LOGMODULE_DB, ISC_LOG_DEBUG(1),
39019689912eSchristos 				      "calling free_qpdb(%s)", buf);
39029689912eSchristos 			free_qpdb(qpdb, true);
39039689912eSchristos 		}
39049689912eSchristos 	}
39059689912eSchristos }
39069689912eSchristos 
39079689912eSchristos static unsigned int
39089689912eSchristos nodecount(dns_db_t *db, dns_dbtree_t tree) {
39099689912eSchristos 	qpzonedb_t *qpdb = NULL;
39109689912eSchristos 	dns_qp_memusage_t mu;
39119689912eSchristos 
39129689912eSchristos 	qpdb = (qpzonedb_t *)db;
39139689912eSchristos 
39149689912eSchristos 	REQUIRE(VALID_QPZONE(qpdb));
39159689912eSchristos 
39169689912eSchristos 	switch (tree) {
39179689912eSchristos 	case dns_dbtree_main:
39189689912eSchristos 		mu = dns_qpmulti_memusage(qpdb->tree);
39199689912eSchristos 		break;
39209689912eSchristos 	case dns_dbtree_nsec:
39219689912eSchristos 		mu = dns_qpmulti_memusage(qpdb->nsec);
39229689912eSchristos 		break;
39239689912eSchristos 	case dns_dbtree_nsec3:
39249689912eSchristos 		mu = dns_qpmulti_memusage(qpdb->nsec3);
39259689912eSchristos 		break;
39269689912eSchristos 	default:
39279689912eSchristos 		UNREACHABLE();
39289689912eSchristos 	}
39299689912eSchristos 
39309689912eSchristos 	return mu.leaves;
39319689912eSchristos }
39329689912eSchristos 
39339689912eSchristos static void
39349689912eSchristos setloop(dns_db_t *db, isc_loop_t *loop) {
39359689912eSchristos 	qpzonedb_t *qpdb = NULL;
39369689912eSchristos 
39379689912eSchristos 	qpdb = (qpzonedb_t *)db;
39389689912eSchristos 
39399689912eSchristos 	REQUIRE(VALID_QPZONE(qpdb));
39409689912eSchristos 
39419689912eSchristos 	RWLOCK(&qpdb->lock, isc_rwlocktype_write);
39429689912eSchristos 	if (qpdb->loop != NULL) {
39439689912eSchristos 		isc_loop_detach(&qpdb->loop);
39449689912eSchristos 	}
39459689912eSchristos 	if (loop != NULL) {
39469689912eSchristos 		isc_loop_attach(loop, &qpdb->loop);
39479689912eSchristos 	}
39489689912eSchristos 	RWUNLOCK(&qpdb->lock, isc_rwlocktype_write);
39499689912eSchristos }
39509689912eSchristos 
39519689912eSchristos static isc_result_t
39529689912eSchristos getoriginnode(dns_db_t *db, dns_dbnode_t **nodep DNS__DB_FLARG) {
39539689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)db;
39549689912eSchristos 	qpznode_t *onode = NULL;
39559689912eSchristos 
39569689912eSchristos 	REQUIRE(VALID_QPZONE(qpdb));
39579689912eSchristos 	REQUIRE(nodep != NULL && *nodep == NULL);
39589689912eSchristos 
39599689912eSchristos 	/* Note that the access to the origin node doesn't require a DB lock */
39609689912eSchristos 	onode = (qpznode_t *)qpdb->origin;
39619689912eSchristos 	INSIST(onode != NULL);
39629689912eSchristos 	newref(qpdb, onode DNS__DB_FLARG_PASS);
39639689912eSchristos 	*nodep = onode;
39649689912eSchristos 
39659689912eSchristos 	return ISC_R_SUCCESS;
39669689912eSchristos }
39679689912eSchristos 
39689689912eSchristos static void
39699689912eSchristos locknode(dns_db_t *db, dns_dbnode_t *dbnode, isc_rwlocktype_t type) {
39709689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)db;
39719689912eSchristos 	qpznode_t *node = (qpznode_t *)dbnode;
39729689912eSchristos 
39739689912eSchristos 	RWLOCK(&qpdb->node_locks[node->locknum].lock, type);
39749689912eSchristos }
39759689912eSchristos 
39769689912eSchristos static void
39779689912eSchristos unlocknode(dns_db_t *db, dns_dbnode_t *dbnode, isc_rwlocktype_t type) {
39789689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)db;
39799689912eSchristos 	qpznode_t *node = (qpznode_t *)dbnode;
39809689912eSchristos 
39819689912eSchristos 	RWUNLOCK(&qpdb->node_locks[node->locknum].lock, type);
39829689912eSchristos }
39839689912eSchristos 
39849689912eSchristos static void
39859689912eSchristos deletedata(dns_db_t *db ISC_ATTR_UNUSED, dns_dbnode_t *node ISC_ATTR_UNUSED,
39869689912eSchristos 	   void *data) {
39879689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)db;
39889689912eSchristos 	dns_slabheader_t *header = data;
39899689912eSchristos 
39909689912eSchristos 	if (header->heap != NULL && header->heap_index != 0) {
39919689912eSchristos 		RWLOCK(&qpdb->lock, isc_rwlocktype_write);
39929689912eSchristos 		isc_heap_delete(header->heap, header->heap_index);
39939689912eSchristos 		RWUNLOCK(&qpdb->lock, isc_rwlocktype_write);
39949689912eSchristos 	}
39959689912eSchristos 	header->heap_index = 0;
39969689912eSchristos }
39979689912eSchristos 
39989689912eSchristos /*
39999689912eSchristos  * Rdataset Iterator Methods
40009689912eSchristos  */
40019689912eSchristos 
40029689912eSchristos static void
40039689912eSchristos rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp DNS__DB_FLARG) {
40049689912eSchristos 	qpdb_rdatasetiter_t *qrditer = NULL;
40059689912eSchristos 
40069689912eSchristos 	qrditer = (qpdb_rdatasetiter_t *)(*iteratorp);
40079689912eSchristos 
40089689912eSchristos 	if (qrditer->common.version != NULL) {
40099689912eSchristos 		closeversion(qrditer->common.db, &qrditer->common.version,
40109689912eSchristos 			     false DNS__DB_FLARG_PASS);
40119689912eSchristos 	}
40129689912eSchristos 	dns__db_detachnode(qrditer->common.db,
40139689912eSchristos 			   &qrditer->common.node DNS__DB_FLARG_PASS);
40149689912eSchristos 	isc_mem_put(qrditer->common.db->mctx, qrditer, sizeof(*qrditer));
40159689912eSchristos 
40169689912eSchristos 	*iteratorp = NULL;
40179689912eSchristos }
40189689912eSchristos 
40199689912eSchristos static isc_result_t
40209689912eSchristos rdatasetiter_first(dns_rdatasetiter_t *iterator DNS__DB_FLARG) {
40219689912eSchristos 	qpdb_rdatasetiter_t *qrditer = (qpdb_rdatasetiter_t *)iterator;
40229689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)(qrditer->common.db);
40239689912eSchristos 	qpznode_t *node = qrditer->common.node;
40249689912eSchristos 	qpz_version_t *version = qrditer->common.version;
40259689912eSchristos 	dns_slabheader_t *header = NULL, *top_next = NULL;
40269689912eSchristos 	isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
40279689912eSchristos 
40289689912eSchristos 	NODE_RDLOCK(&qpdb->node_locks[node->locknum].lock, &nlocktype);
40299689912eSchristos 
40309689912eSchristos 	for (header = node->data; header != NULL; header = top_next) {
40319689912eSchristos 		top_next = header->next;
40329689912eSchristos 		do {
40339689912eSchristos 			if (header->serial <= version->serial &&
40349689912eSchristos 			    !IGNORE(header))
40359689912eSchristos 			{
40369689912eSchristos 				if (NONEXISTENT(header)) {
40379689912eSchristos 					header = NULL;
40389689912eSchristos 				}
40399689912eSchristos 				break;
40409689912eSchristos 			} else {
40419689912eSchristos 				header = header->down;
40429689912eSchristos 			}
40439689912eSchristos 		} while (header != NULL);
40449689912eSchristos 		if (header != NULL) {
40459689912eSchristos 			break;
40469689912eSchristos 		}
40479689912eSchristos 	}
40489689912eSchristos 
40499689912eSchristos 	NODE_UNLOCK(&qpdb->node_locks[node->locknum].lock, &nlocktype);
40509689912eSchristos 
40519689912eSchristos 	qrditer->current = header;
40529689912eSchristos 
40539689912eSchristos 	if (header == NULL) {
40549689912eSchristos 		return ISC_R_NOMORE;
40559689912eSchristos 	}
40569689912eSchristos 
40579689912eSchristos 	return ISC_R_SUCCESS;
40589689912eSchristos }
40599689912eSchristos 
40609689912eSchristos static isc_result_t
40619689912eSchristos rdatasetiter_next(dns_rdatasetiter_t *iterator DNS__DB_FLARG) {
40629689912eSchristos 	qpdb_rdatasetiter_t *qrditer = (qpdb_rdatasetiter_t *)iterator;
40639689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)(qrditer->common.db);
40649689912eSchristos 	qpznode_t *node = qrditer->common.node;
40659689912eSchristos 	qpz_version_t *version = qrditer->common.version;
40669689912eSchristos 	dns_slabheader_t *header = NULL, *top_next = NULL;
40679689912eSchristos 	dns_typepair_t type, negtype;
40689689912eSchristos 	dns_rdatatype_t rdtype;
40699689912eSchristos 	isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
40709689912eSchristos 
40719689912eSchristos 	header = qrditer->current;
40729689912eSchristos 	if (header == NULL) {
40739689912eSchristos 		return ISC_R_NOMORE;
40749689912eSchristos 	}
40759689912eSchristos 
40769689912eSchristos 	NODE_RDLOCK(&qpdb->node_locks[node->locknum].lock, &nlocktype);
40779689912eSchristos 
40789689912eSchristos 	type = header->type;
40799689912eSchristos 	rdtype = DNS_TYPEPAIR_TYPE(header->type);
40809689912eSchristos 	negtype = DNS_TYPEPAIR_VALUE(0, rdtype);
40819689912eSchristos 
40829689912eSchristos 	/*
40839689912eSchristos 	 * Find the start of the header chain for the next type
40849689912eSchristos 	 * by walking back up the list.
40859689912eSchristos 	 */
40869689912eSchristos 	top_next = header->next;
40879689912eSchristos 	while (top_next != NULL &&
40889689912eSchristos 	       (top_next->type == type || top_next->type == negtype))
40899689912eSchristos 	{
40909689912eSchristos 		top_next = top_next->next;
40919689912eSchristos 	}
40929689912eSchristos 	for (header = top_next; header != NULL; header = top_next) {
40939689912eSchristos 		top_next = header->next;
40949689912eSchristos 		do {
40959689912eSchristos 			if (header->serial <= version->serial &&
40969689912eSchristos 			    !IGNORE(header))
40979689912eSchristos 			{
40989689912eSchristos 				if (NONEXISTENT(header)) {
40999689912eSchristos 					header = NULL;
41009689912eSchristos 				}
41019689912eSchristos 				break;
41029689912eSchristos 			} else {
41039689912eSchristos 				header = header->down;
41049689912eSchristos 			}
41059689912eSchristos 		} while (header != NULL);
41069689912eSchristos 		if (header != NULL) {
41079689912eSchristos 			break;
41089689912eSchristos 		}
41099689912eSchristos 		/*
41109689912eSchristos 		 * Find the start of the header chain for the next type
41119689912eSchristos 		 * by walking back up the list.
41129689912eSchristos 		 */
41139689912eSchristos 		while (top_next != NULL &&
41149689912eSchristos 		       (top_next->type == type || top_next->type == negtype))
41159689912eSchristos 		{
41169689912eSchristos 			top_next = top_next->next;
41179689912eSchristos 		}
41189689912eSchristos 	}
41199689912eSchristos 
41209689912eSchristos 	NODE_UNLOCK(&qpdb->node_locks[node->locknum].lock, &nlocktype);
41219689912eSchristos 
41229689912eSchristos 	qrditer->current = header;
41239689912eSchristos 
41249689912eSchristos 	if (header == NULL) {
41259689912eSchristos 		return ISC_R_NOMORE;
41269689912eSchristos 	}
41279689912eSchristos 
41289689912eSchristos 	return ISC_R_SUCCESS;
41299689912eSchristos }
41309689912eSchristos 
41319689912eSchristos static void
41329689912eSchristos rdatasetiter_current(dns_rdatasetiter_t *iterator,
41339689912eSchristos 		     dns_rdataset_t *rdataset DNS__DB_FLARG) {
41349689912eSchristos 	qpdb_rdatasetiter_t *qrditer = (qpdb_rdatasetiter_t *)iterator;
41359689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)(qrditer->common.db);
41369689912eSchristos 	qpznode_t *node = qrditer->common.node;
41379689912eSchristos 	dns_slabheader_t *header = NULL;
41389689912eSchristos 	isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
41399689912eSchristos 
41409689912eSchristos 	header = qrditer->current;
41419689912eSchristos 	REQUIRE(header != NULL);
41429689912eSchristos 
41439689912eSchristos 	NODE_RDLOCK(&qpdb->node_locks[node->locknum].lock, &nlocktype);
41449689912eSchristos 
41459689912eSchristos 	bindrdataset(qpdb, node, header, qrditer->common.now,
41469689912eSchristos 		     rdataset DNS__DB_FLARG_PASS);
41479689912eSchristos 
41489689912eSchristos 	NODE_UNLOCK(&qpdb->node_locks[node->locknum].lock, &nlocktype);
41499689912eSchristos }
41509689912eSchristos 
41519689912eSchristos /*
41529689912eSchristos  * Database Iterator Methods
41539689912eSchristos  */
41549689912eSchristos static void
41559689912eSchristos reference_iter_node(qpdb_dbiterator_t *iter DNS__DB_FLARG) {
41569689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)iter->common.db;
41579689912eSchristos 	qpznode_t *node = iter->node;
41589689912eSchristos 
41599689912eSchristos 	if (node == NULL) {
41609689912eSchristos 		return;
41619689912eSchristos 	}
41629689912eSchristos 
41639689912eSchristos 	newref(qpdb, node DNS__DB_FLARG_PASS);
41649689912eSchristos }
41659689912eSchristos 
41669689912eSchristos static void
41679689912eSchristos dereference_iter_node(qpdb_dbiterator_t *iter DNS__DB_FLARG) {
41689689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)iter->common.db;
41699689912eSchristos 	qpznode_t *node = iter->node;
41709689912eSchristos 	isc_rwlock_t *lock = NULL;
41719689912eSchristos 	isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
41729689912eSchristos 
41739689912eSchristos 	if (node == NULL) {
41749689912eSchristos 		return;
41759689912eSchristos 	}
41769689912eSchristos 
41779689912eSchristos 	lock = &qpdb->node_locks[node->locknum].lock;
41789689912eSchristos 	NODE_RDLOCK(lock, &nlocktype);
41799689912eSchristos 	decref(qpdb, node, 0, &nlocktype DNS__DB_FLARG_PASS);
41809689912eSchristos 	NODE_UNLOCK(lock, &nlocktype);
41819689912eSchristos 	iter->node = NULL;
41829689912eSchristos }
41839689912eSchristos 
41849689912eSchristos static void
41859689912eSchristos dbiterator_destroy(dns_dbiterator_t **iteratorp DNS__DB_FLARG) {
41869689912eSchristos 	qpdb_dbiterator_t *iter = (qpdb_dbiterator_t *)(*iteratorp);
41879689912eSchristos 	dns_db_t *db = NULL;
41889689912eSchristos 
41899689912eSchristos 	dereference_iter_node(iter DNS__DB_FLARG_PASS);
41909689912eSchristos 
41919689912eSchristos 	dns_db_attach(iter->common.db, &db);
41929689912eSchristos 	dns_db_detach(&iter->common.db);
41939689912eSchristos 
41949689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)db;
41959689912eSchristos 	dns_qpsnap_destroy(qpdb->tree, &iter->tsnap);
41969689912eSchristos 	dns_qpsnap_destroy(qpdb->nsec3, &iter->nsnap);
41979689912eSchristos 
41989689912eSchristos 	isc_mem_put(db->mctx, iter, sizeof(*iter));
41999689912eSchristos 	dns_db_detach(&db);
42009689912eSchristos 
42019689912eSchristos 	*iteratorp = NULL;
42029689912eSchristos }
42039689912eSchristos 
42049689912eSchristos static isc_result_t
42059689912eSchristos dbiterator_first(dns_dbiterator_t *iterator DNS__DB_FLARG) {
42069689912eSchristos 	isc_result_t result;
42079689912eSchristos 	qpdb_dbiterator_t *qpdbiter = (qpdb_dbiterator_t *)iterator;
42089689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)iterator->db;
42099689912eSchristos 
42109689912eSchristos 	if (qpdbiter->result != ISC_R_SUCCESS &&
42119689912eSchristos 	    qpdbiter->result != ISC_R_NOTFOUND &&
42129689912eSchristos 	    qpdbiter->result != DNS_R_PARTIALMATCH &&
42139689912eSchristos 	    qpdbiter->result != ISC_R_NOMORE)
42149689912eSchristos 	{
42159689912eSchristos 		return qpdbiter->result;
42169689912eSchristos 	}
42179689912eSchristos 
42189689912eSchristos 	dereference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
42199689912eSchristos 
42209689912eSchristos 	switch (qpdbiter->nsec3mode) {
42219689912eSchristos 	case nsec3only:
42229689912eSchristos 		qpdbiter->current = &qpdbiter->nsec3iter;
42239689912eSchristos 		dns_qpiter_init(qpdbiter->nsnap, qpdbiter->current);
42249689912eSchristos 		result = dns_qpiter_next(qpdbiter->current, NULL,
42259689912eSchristos 					 (void **)&qpdbiter->node, NULL);
42269689912eSchristos 		if (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) {
42279689912eSchristos 			/* If we're in the NSEC3 tree, skip the origin */
42289689912eSchristos 			if (QPDBITER_NSEC3_ORIGIN_NODE(qpdb, qpdbiter)) {
42299689912eSchristos 				result = dns_qpiter_next(
42309689912eSchristos 					qpdbiter->current, NULL,
42319689912eSchristos 					(void **)&qpdbiter->node, NULL);
42329689912eSchristos 			}
42339689912eSchristos 		}
42349689912eSchristos 		break;
42359689912eSchristos 	case nonsec3:
42369689912eSchristos 		qpdbiter->current = &qpdbiter->mainiter;
42379689912eSchristos 		dns_qpiter_init(qpdbiter->tsnap, qpdbiter->current);
42389689912eSchristos 		result = dns_qpiter_next(qpdbiter->current, NULL,
42399689912eSchristos 					 (void **)&qpdbiter->node, NULL);
42409689912eSchristos 		break;
42419689912eSchristos 	case full:
42429689912eSchristos 		qpdbiter->current = &qpdbiter->mainiter;
42439689912eSchristos 		dns_qpiter_init(qpdbiter->tsnap, qpdbiter->current);
42449689912eSchristos 		result = dns_qpiter_next(qpdbiter->current, NULL,
42459689912eSchristos 					 (void **)&qpdbiter->node, NULL);
42469689912eSchristos 		if (result == ISC_R_NOMORE) {
42479689912eSchristos 			qpdbiter->current = &qpdbiter->nsec3iter;
42489689912eSchristos 			dns_qpiter_init(qpdbiter->nsnap, qpdbiter->current);
42499689912eSchristos 			result = dns_qpiter_next(qpdbiter->current, NULL,
42509689912eSchristos 						 (void **)&qpdbiter->node,
42519689912eSchristos 						 NULL);
42529689912eSchristos 		}
42539689912eSchristos 		break;
42549689912eSchristos 	default:
42559689912eSchristos 		UNREACHABLE();
42569689912eSchristos 	}
42579689912eSchristos 
42589689912eSchristos 	if (result == ISC_R_SUCCESS) {
42599689912eSchristos 		reference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
42609689912eSchristos 	} else {
42619689912eSchristos 		qpdbiter->node = NULL;
42629689912eSchristos 	}
42639689912eSchristos 	qpdbiter->result = result;
42649689912eSchristos 	return result;
42659689912eSchristos }
42669689912eSchristos 
42679689912eSchristos static isc_result_t
42689689912eSchristos dbiterator_last(dns_dbiterator_t *iterator DNS__DB_FLARG) {
42699689912eSchristos 	isc_result_t result;
42709689912eSchristos 	qpdb_dbiterator_t *qpdbiter = (qpdb_dbiterator_t *)iterator;
42719689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)iterator->db;
42729689912eSchristos 
42739689912eSchristos 	if (qpdbiter->result != ISC_R_SUCCESS &&
42749689912eSchristos 	    qpdbiter->result != ISC_R_NOTFOUND &&
42759689912eSchristos 	    qpdbiter->result != DNS_R_PARTIALMATCH &&
42769689912eSchristos 	    qpdbiter->result != ISC_R_NOMORE)
42779689912eSchristos 	{
42789689912eSchristos 		return qpdbiter->result;
42799689912eSchristos 	}
42809689912eSchristos 
42819689912eSchristos 	dereference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
42829689912eSchristos 
42839689912eSchristos 	switch (qpdbiter->nsec3mode) {
42849689912eSchristos 	case nsec3only:
42859689912eSchristos 		qpdbiter->current = &qpdbiter->nsec3iter;
42869689912eSchristos 		dns_qpiter_init(qpdbiter->nsnap, qpdbiter->current);
42879689912eSchristos 		result = dns_qpiter_prev(qpdbiter->current, NULL,
42889689912eSchristos 					 (void **)&qpdbiter->node, NULL);
42899689912eSchristos 		if ((result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) &&
42909689912eSchristos 		    QPDBITER_NSEC3_ORIGIN_NODE(qpdb, qpdbiter))
42919689912eSchristos 		{
42929689912eSchristos 			/*
42939689912eSchristos 			 * NSEC3 tree only has an origin node.
42949689912eSchristos 			 */
42959689912eSchristos 			qpdbiter->node = NULL;
42969689912eSchristos 			result = ISC_R_NOMORE;
42979689912eSchristos 		}
42989689912eSchristos 		break;
42999689912eSchristos 	case nonsec3:
43009689912eSchristos 		qpdbiter->current = &qpdbiter->mainiter;
43019689912eSchristos 		dns_qpiter_init(qpdbiter->tsnap, qpdbiter->current);
43029689912eSchristos 		result = dns_qpiter_prev(qpdbiter->current, NULL,
43039689912eSchristos 					 (void **)&qpdbiter->node, NULL);
43049689912eSchristos 		break;
43059689912eSchristos 	case full:
43069689912eSchristos 		qpdbiter->current = &qpdbiter->nsec3iter;
43079689912eSchristos 		dns_qpiter_init(qpdbiter->nsnap, qpdbiter->current);
43089689912eSchristos 		result = dns_qpiter_prev(qpdbiter->current, NULL,
43099689912eSchristos 					 (void **)&qpdbiter->node, NULL);
43109689912eSchristos 		if ((result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) &&
43119689912eSchristos 		    QPDBITER_NSEC3_ORIGIN_NODE(qpdb, qpdbiter))
43129689912eSchristos 		{
43139689912eSchristos 			/*
43149689912eSchristos 			 * NSEC3 tree only has an origin node.
43159689912eSchristos 			 */
43169689912eSchristos 			qpdbiter->node = NULL;
43179689912eSchristos 			result = ISC_R_NOMORE;
43189689912eSchristos 		}
43199689912eSchristos 		if (result == ISC_R_NOMORE) {
43209689912eSchristos 			qpdbiter->current = &qpdbiter->mainiter;
43219689912eSchristos 			dns_qpiter_init(qpdbiter->tsnap, qpdbiter->current);
43229689912eSchristos 			result = dns_qpiter_prev(qpdbiter->current, NULL,
43239689912eSchristos 						 (void **)&qpdbiter->node,
43249689912eSchristos 						 NULL);
43259689912eSchristos 		}
43269689912eSchristos 		break;
43279689912eSchristos 	default:
43289689912eSchristos 		UNREACHABLE();
43299689912eSchristos 	}
43309689912eSchristos 
43319689912eSchristos 	if (result == ISC_R_SUCCESS) {
43329689912eSchristos 		reference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
43339689912eSchristos 	} else {
43349689912eSchristos 		qpdbiter->node = NULL;
43359689912eSchristos 	}
43369689912eSchristos 	qpdbiter->result = result;
43379689912eSchristos 	return result;
43389689912eSchristos }
43399689912eSchristos 
43409689912eSchristos static isc_result_t
43419689912eSchristos dbiterator_seek(dns_dbiterator_t *iterator,
43429689912eSchristos 		const dns_name_t *name DNS__DB_FLARG) {
43439689912eSchristos 	isc_result_t result, tresult;
43449689912eSchristos 	qpdb_dbiterator_t *qpdbiter = (qpdb_dbiterator_t *)iterator;
43459689912eSchristos 
43469689912eSchristos 	if (qpdbiter->result != ISC_R_SUCCESS &&
43479689912eSchristos 	    qpdbiter->result != ISC_R_NOTFOUND &&
43489689912eSchristos 	    qpdbiter->result != DNS_R_PARTIALMATCH &&
43499689912eSchristos 	    qpdbiter->result != ISC_R_NOMORE)
43509689912eSchristos 	{
43519689912eSchristos 		return qpdbiter->result;
43529689912eSchristos 	}
43539689912eSchristos 
43549689912eSchristos 	dereference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
43559689912eSchristos 
43569689912eSchristos 	switch (qpdbiter->nsec3mode) {
43579689912eSchristos 	case nsec3only:
43589689912eSchristos 		qpdbiter->current = &qpdbiter->nsec3iter;
43599689912eSchristos 		result = dns_qp_lookup(qpdbiter->nsnap, name, NULL,
43609689912eSchristos 				       qpdbiter->current, NULL,
43619689912eSchristos 				       (void **)&qpdbiter->node, NULL);
43629689912eSchristos 		break;
43639689912eSchristos 	case nonsec3:
43649689912eSchristos 		qpdbiter->current = &qpdbiter->mainiter;
43659689912eSchristos 		result = dns_qp_lookup(qpdbiter->tsnap, name, NULL,
43669689912eSchristos 				       qpdbiter->current, NULL,
43679689912eSchristos 				       (void **)&qpdbiter->node, NULL);
43689689912eSchristos 		break;
43699689912eSchristos 	case full:
43709689912eSchristos 		/*
43719689912eSchristos 		 * Stay on main chain if not found on
43729689912eSchristos 		 * either iterator.
43739689912eSchristos 		 */
43749689912eSchristos 		qpdbiter->current = &qpdbiter->mainiter;
43759689912eSchristos 		result = dns_qp_lookup(qpdbiter->tsnap, name, NULL,
43769689912eSchristos 				       qpdbiter->current, NULL,
43779689912eSchristos 				       (void **)&qpdbiter->node, NULL);
43789689912eSchristos 		if (result == DNS_R_PARTIALMATCH) {
43799689912eSchristos 			tresult = dns_qp_lookup(qpdbiter->nsnap, name, NULL,
43809689912eSchristos 						&qpdbiter->nsec3iter, NULL,
43819689912eSchristos 						NULL, NULL);
43829689912eSchristos 			if (tresult == ISC_R_SUCCESS) {
43839689912eSchristos 				qpdbiter->current = &qpdbiter->nsec3iter;
43849689912eSchristos 				result = tresult;
43859689912eSchristos 			}
43869689912eSchristos 		}
43879689912eSchristos 		break;
43889689912eSchristos 	default:
43899689912eSchristos 		UNREACHABLE();
43909689912eSchristos 	}
43919689912eSchristos 
43929689912eSchristos 	if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
43939689912eSchristos 		reference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
43949689912eSchristos 	} else {
43959689912eSchristos 		qpdbiter->node = NULL;
43969689912eSchristos 	}
43979689912eSchristos 
43989689912eSchristos 	qpdbiter->result = (result == DNS_R_PARTIALMATCH) ? ISC_R_SUCCESS
43999689912eSchristos 							  : result;
44009689912eSchristos 	return result;
44019689912eSchristos }
44029689912eSchristos 
44039689912eSchristos static isc_result_t
44049689912eSchristos dbiterator_prev(dns_dbiterator_t *iterator DNS__DB_FLARG) {
44059689912eSchristos 	isc_result_t result;
44069689912eSchristos 	qpdb_dbiterator_t *qpdbiter = (qpdb_dbiterator_t *)iterator;
44079689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)iterator->db;
44089689912eSchristos 
44099689912eSchristos 	REQUIRE(qpdbiter->node != NULL);
44109689912eSchristos 
44119689912eSchristos 	if (qpdbiter->result != ISC_R_SUCCESS) {
44129689912eSchristos 		return qpdbiter->result;
44139689912eSchristos 	}
44149689912eSchristos 
44159689912eSchristos 	dereference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
44169689912eSchristos 
44179689912eSchristos 	result = dns_qpiter_prev(qpdbiter->current, NULL,
44189689912eSchristos 				 (void **)&qpdbiter->node, NULL);
44199689912eSchristos 
44209689912eSchristos 	if (qpdbiter->current == &qpdbiter->nsec3iter) {
44219689912eSchristos 		if (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) {
44229689912eSchristos 			/*
44239689912eSchristos 			 * If we're in the NSEC3 tree, it's empty or
44249689912eSchristos 			 * we've reached the origin, then we're done
44259689912eSchristos 			 * with it.
44269689912eSchristos 			 */
44279689912eSchristos 			if (QPDBITER_NSEC3_ORIGIN_NODE(qpdb, qpdbiter)) {
44289689912eSchristos 				qpdbiter->node = NULL;
44299689912eSchristos 				result = ISC_R_NOMORE;
44309689912eSchristos 			}
44319689912eSchristos 		}
44329689912eSchristos 		if (result == ISC_R_NOMORE && qpdbiter->nsec3mode == full) {
44339689912eSchristos 			qpdbiter->current = &qpdbiter->mainiter;
44349689912eSchristos 			dns_qpiter_init(qpdbiter->tsnap, qpdbiter->current);
44359689912eSchristos 			result = dns_qpiter_prev(qpdbiter->current, NULL,
44369689912eSchristos 						 (void **)&qpdbiter->node,
44379689912eSchristos 						 NULL);
44389689912eSchristos 		}
44399689912eSchristos 	}
44409689912eSchristos 
44419689912eSchristos 	if (result == ISC_R_SUCCESS) {
44429689912eSchristos 		reference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
44439689912eSchristos 	} else {
44449689912eSchristos 		qpdbiter->node = NULL;
44459689912eSchristos 	}
44469689912eSchristos 
44479689912eSchristos 	qpdbiter->result = result;
44489689912eSchristos 	return result;
44499689912eSchristos }
44509689912eSchristos 
44519689912eSchristos static isc_result_t
44529689912eSchristos dbiterator_next(dns_dbiterator_t *iterator DNS__DB_FLARG) {
44539689912eSchristos 	isc_result_t result;
44549689912eSchristos 	qpdb_dbiterator_t *qpdbiter = (qpdb_dbiterator_t *)iterator;
44559689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)iterator->db;
44569689912eSchristos 
44579689912eSchristos 	REQUIRE(qpdbiter->node != NULL);
44589689912eSchristos 
44599689912eSchristos 	if (qpdbiter->result != ISC_R_SUCCESS) {
44609689912eSchristos 		return qpdbiter->result;
44619689912eSchristos 	}
44629689912eSchristos 
44639689912eSchristos 	dereference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
44649689912eSchristos 
44659689912eSchristos 	result = dns_qpiter_next(qpdbiter->current, NULL,
44669689912eSchristos 				 (void **)&qpdbiter->node, NULL);
44679689912eSchristos 
44689689912eSchristos 	if (result == ISC_R_NOMORE && qpdbiter->nsec3mode == full &&
44699689912eSchristos 	    qpdbiter->current == &qpdbiter->mainiter)
44709689912eSchristos 	{
44719689912eSchristos 		qpdbiter->current = &qpdbiter->nsec3iter;
44729689912eSchristos 		dns_qpiter_init(qpdbiter->nsnap, qpdbiter->current);
44739689912eSchristos 		result = dns_qpiter_next(qpdbiter->current, NULL,
44749689912eSchristos 					 (void **)&qpdbiter->node, NULL);
44759689912eSchristos 	}
44769689912eSchristos 
44779689912eSchristos 	if (result == ISC_R_SUCCESS) {
44789689912eSchristos 		/*
44799689912eSchristos 		 * If we've just started the NSEC3 tree,
44809689912eSchristos 		 * skip over the origin.
44819689912eSchristos 		 */
44829689912eSchristos 		if (QPDBITER_NSEC3_ORIGIN_NODE(qpdb, qpdbiter)) {
44839689912eSchristos 			switch (qpdbiter->nsec3mode) {
44849689912eSchristos 			case nsec3only:
44859689912eSchristos 			case full:
44869689912eSchristos 				result = dns_qpiter_next(
44879689912eSchristos 					qpdbiter->current, NULL,
44889689912eSchristos 					(void **)&qpdbiter->node, NULL);
44899689912eSchristos 				break;
44909689912eSchristos 			case nonsec3:
44919689912eSchristos 				result = ISC_R_NOMORE;
44929689912eSchristos 				qpdbiter->node = NULL;
44939689912eSchristos 				break;
44949689912eSchristos 			default:
44959689912eSchristos 				UNREACHABLE();
44969689912eSchristos 			}
44979689912eSchristos 		}
44989689912eSchristos 	}
44999689912eSchristos 
45009689912eSchristos 	if (result == ISC_R_SUCCESS) {
45019689912eSchristos 		reference_iter_node(qpdbiter DNS__DB_FLARG_PASS);
45029689912eSchristos 	} else {
45039689912eSchristos 		qpdbiter->node = NULL;
45049689912eSchristos 	}
45059689912eSchristos 
45069689912eSchristos 	qpdbiter->result = result;
45079689912eSchristos 	return result;
45089689912eSchristos }
45099689912eSchristos 
45109689912eSchristos static isc_result_t
45119689912eSchristos dbiterator_current(dns_dbiterator_t *iterator, dns_dbnode_t **nodep,
45129689912eSchristos 		   dns_name_t *name DNS__DB_FLARG) {
45139689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)iterator->db;
45149689912eSchristos 	qpdb_dbiterator_t *qpdbiter = (qpdb_dbiterator_t *)iterator;
45159689912eSchristos 	qpznode_t *node = qpdbiter->node;
45169689912eSchristos 
45179689912eSchristos 	REQUIRE(qpdbiter->result == ISC_R_SUCCESS);
45189689912eSchristos 	REQUIRE(qpdbiter->node != NULL);
45199689912eSchristos 
45209689912eSchristos 	if (name != NULL) {
45219689912eSchristos 		dns_name_copy(&qpdbiter->node->name, name);
45229689912eSchristos 	}
45239689912eSchristos 
45249689912eSchristos 	newref(qpdb, node DNS__DB_FLARG_PASS);
45259689912eSchristos 
45269689912eSchristos 	*nodep = qpdbiter->node;
45279689912eSchristos 
45289689912eSchristos 	return ISC_R_SUCCESS;
45299689912eSchristos }
45309689912eSchristos 
45319689912eSchristos static isc_result_t
45329689912eSchristos dbiterator_pause(dns_dbiterator_t *iterator ISC_ATTR_UNUSED) {
45339689912eSchristos 	return ISC_R_SUCCESS;
45349689912eSchristos }
45359689912eSchristos 
45369689912eSchristos static isc_result_t
45379689912eSchristos dbiterator_origin(dns_dbiterator_t *iterator, dns_name_t *name) {
45389689912eSchristos 	qpdb_dbiterator_t *qpdbiter = (qpdb_dbiterator_t *)iterator;
45399689912eSchristos 
45409689912eSchristos 	if (qpdbiter->result != ISC_R_SUCCESS) {
45419689912eSchristos 		return qpdbiter->result;
45429689912eSchristos 	}
45439689912eSchristos 
45449689912eSchristos 	dns_name_copy(dns_rootname, name);
45459689912eSchristos 	return ISC_R_SUCCESS;
45469689912eSchristos }
45479689912eSchristos 
45489689912eSchristos static isc_result_t
45499689912eSchristos createiterator(dns_db_t *db, unsigned int options,
45509689912eSchristos 	       dns_dbiterator_t **iteratorp) {
45519689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)db;
45529689912eSchristos 	qpdb_dbiterator_t *iter = NULL;
45539689912eSchristos 
45549689912eSchristos 	REQUIRE(VALID_QPZONE(qpdb));
45559689912eSchristos 
45569689912eSchristos 	iter = isc_mem_get(qpdb->common.mctx, sizeof(*iter));
45579689912eSchristos 	*iter = (qpdb_dbiterator_t){
45589689912eSchristos 		.common.magic = DNS_DBITERATOR_MAGIC,
45599689912eSchristos 		.common.methods = &dbiterator_methods,
45609689912eSchristos 		.common.relative_names = ((options & DNS_DB_RELATIVENAMES) !=
45619689912eSchristos 					  0),
45629689912eSchristos 	};
45639689912eSchristos 
45649689912eSchristos 	if ((options & DNS_DB_NSEC3ONLY) != 0) {
45659689912eSchristos 		iter->nsec3mode = nsec3only;
45669689912eSchristos 		iter->current = &iter->nsec3iter;
45679689912eSchristos 	} else if ((options & DNS_DB_NONSEC3) != 0) {
45689689912eSchristos 		iter->nsec3mode = nonsec3;
45699689912eSchristos 		iter->current = &iter->mainiter;
45709689912eSchristos 	} else {
45719689912eSchristos 		iter->nsec3mode = full;
45729689912eSchristos 		iter->current = &iter->mainiter;
45739689912eSchristos 	}
45749689912eSchristos 
45759689912eSchristos 	dns_db_attach(db, &iter->common.db);
45769689912eSchristos 
45779689912eSchristos 	dns_qpmulti_snapshot(qpdb->tree, &iter->tsnap);
45789689912eSchristos 	dns_qpiter_init(iter->tsnap, &iter->mainiter);
45799689912eSchristos 
45809689912eSchristos 	dns_qpmulti_snapshot(qpdb->nsec3, &iter->nsnap);
45819689912eSchristos 	dns_qpiter_init(iter->nsnap, &iter->nsec3iter);
45829689912eSchristos 
45839689912eSchristos 	*iteratorp = (dns_dbiterator_t *)iter;
45849689912eSchristos 	return ISC_R_SUCCESS;
45859689912eSchristos }
45869689912eSchristos 
45879689912eSchristos static isc_result_t
45889689912eSchristos addrdataset(dns_db_t *db, dns_dbnode_t *dbnode, dns_dbversion_t *dbversion,
45899689912eSchristos 	    isc_stdtime_t now ISC_ATTR_UNUSED, dns_rdataset_t *rdataset,
45909689912eSchristos 	    unsigned int options, dns_rdataset_t *addedrdataset DNS__DB_FLARG) {
45919689912eSchristos 	isc_result_t result;
45929689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)db;
45939689912eSchristos 	qpznode_t *node = (qpznode_t *)dbnode;
45949689912eSchristos 	qpz_version_t *version = dbversion;
45959689912eSchristos 	isc_region_t region;
45969689912eSchristos 	dns_slabheader_t *newheader = NULL;
45979689912eSchristos 	isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
45989689912eSchristos 	dns_fixedname_t fn;
45999689912eSchristos 	dns_name_t *name = dns_fixedname_initname(&fn);
46009689912eSchristos 	dns_qp_t *nsec = NULL;
46019689912eSchristos 
46029689912eSchristos 	REQUIRE(VALID_QPZONE(qpdb));
46039689912eSchristos 	REQUIRE(version != NULL && version->qpdb == qpdb);
46049689912eSchristos 
46059689912eSchristos 	/*
46069689912eSchristos 	 * SOA records are only allowed at top of zone.
46079689912eSchristos 	 */
46089689912eSchristos 	if (rdataset->type == dns_rdatatype_soa && node != qpdb->origin) {
46099689912eSchristos 		return DNS_R_NOTZONETOP;
46109689912eSchristos 	}
46119689912eSchristos 
46129689912eSchristos 	REQUIRE((node->nsec == DNS_DB_NSEC_NSEC3 &&
46139689912eSchristos 		 (rdataset->type == dns_rdatatype_nsec3 ||
46149689912eSchristos 		  rdataset->covers == dns_rdatatype_nsec3)) ||
46159689912eSchristos 		(node->nsec != DNS_DB_NSEC_NSEC3 &&
46169689912eSchristos 		 rdataset->type != dns_rdatatype_nsec3 &&
46179689912eSchristos 		 rdataset->covers != dns_rdatatype_nsec3));
46189689912eSchristos 
46199689912eSchristos 	result = dns_rdataslab_fromrdataset(rdataset, qpdb->common.mctx,
46209689912eSchristos 					    &region, sizeof(dns_slabheader_t),
46219689912eSchristos 					    qpdb->maxrrperset);
46229689912eSchristos 	if (result != ISC_R_SUCCESS) {
46239689912eSchristos 		if (result == DNS_R_TOOMANYRECORDS) {
46249689912eSchristos 			dns__db_logtoomanyrecords((dns_db_t *)qpdb, &node->name,
46259689912eSchristos 						  rdataset->type, "adding",
46269689912eSchristos 						  qpdb->maxrrperset);
46279689912eSchristos 		}
46289689912eSchristos 		return result;
46299689912eSchristos 	}
46309689912eSchristos 
46319689912eSchristos 	dns_name_copy(&node->name, name);
46329689912eSchristos 	dns_rdataset_getownercase(rdataset, name);
46339689912eSchristos 
46349689912eSchristos 	newheader = (dns_slabheader_t *)region.base;
46359689912eSchristos 	*newheader = (dns_slabheader_t){
46369689912eSchristos 		.type = DNS_TYPEPAIR_VALUE(rdataset->type, rdataset->covers),
46379689912eSchristos 		.trust = rdataset->trust,
46389689912eSchristos 		.node = node,
46399689912eSchristos 	};
46409689912eSchristos 
46419689912eSchristos 	dns_slabheader_reset(newheader, db, node);
46429689912eSchristos 	newheader->ttl = rdataset->ttl;
46439689912eSchristos 	if (rdataset->ttl == 0U) {
46449689912eSchristos 		DNS_SLABHEADER_SETATTR(newheader, DNS_SLABHEADERATTR_ZEROTTL);
46459689912eSchristos 	}
46469689912eSchristos 	atomic_init(&newheader->count,
46479689912eSchristos 		    atomic_fetch_add_relaxed(&init_count, 1));
46489689912eSchristos 
46499689912eSchristos 	newheader->serial = version->serial;
46509689912eSchristos 	if ((rdataset->attributes & DNS_RDATASETATTR_RESIGN) != 0) {
46519689912eSchristos 		DNS_SLABHEADER_SETATTR(newheader, DNS_SLABHEADERATTR_RESIGN);
46529689912eSchristos 		newheader->resign =
46539689912eSchristos 			(isc_stdtime_t)(dns_time64_from32(rdataset->resign) >>
46549689912eSchristos 					1);
46559689912eSchristos 		newheader->resign_lsb = rdataset->resign & 0x1;
46569689912eSchristos 	}
46579689912eSchristos 
46589689912eSchristos 	/*
46599689912eSchristos 	 * Add to the auxiliary NSEC tree if we're adding an NSEC record.
46609689912eSchristos 	 */
46619689912eSchristos 	if (node->nsec != DNS_DB_NSEC_HAS_NSEC &&
46629689912eSchristos 	    rdataset->type == dns_rdatatype_nsec)
46639689912eSchristos 	{
46649689912eSchristos 		dns_qpmulti_write(qpdb->nsec, &nsec);
46659689912eSchristos 	}
46669689912eSchristos 
46679689912eSchristos 	/*
46689689912eSchristos 	 * If we're adding a delegation type or adding to the auxiliary NSEC
46699689912eSchristos 	 * tree hold an exclusive lock on the tree.  In the latter case the
46709689912eSchristos 	 * lock does not necessarily have to be acquired but it will help
46719689912eSchristos 	 * purge ancient entries more effectively.
46729689912eSchristos 	 *
46739689912eSchristos 	 * (Note: node lock must be acquired after starting
46749689912eSchristos 	 * the QPDB transaction and released before committing.)
46759689912eSchristos 	 */
46769689912eSchristos 	NODE_WRLOCK(&qpdb->node_locks[node->locknum].lock, &nlocktype);
46779689912eSchristos 
46789689912eSchristos 	result = ISC_R_SUCCESS;
46799689912eSchristos 	if (nsec != NULL) {
46809689912eSchristos 		qpznode_t *nsecnode = new_qpznode(qpdb, name);
46819689912eSchristos 		result = dns_qp_insert(nsec, nsecnode, 0);
46829689912eSchristos 		if (result == ISC_R_SUCCESS) {
46839689912eSchristos 			nsecnode->nsec = DNS_DB_NSEC_NSEC;
46849689912eSchristos 			node->nsec = DNS_DB_NSEC_HAS_NSEC;
46859689912eSchristos 		} else if (result == ISC_R_EXISTS) {
46869689912eSchristos 			node->nsec = DNS_DB_NSEC_HAS_NSEC;
46879689912eSchristos 			result = ISC_R_SUCCESS;
46889689912eSchristos 		}
46899689912eSchristos 		qpznode_detach(&nsecnode);
46909689912eSchristos 	}
46919689912eSchristos 
46929689912eSchristos 	if (result == ISC_R_SUCCESS) {
46939689912eSchristos 		result = add(qpdb, node, name, version, newheader, options,
46949689912eSchristos 			     false, addedrdataset, 0 DNS__DB_FLARG_PASS);
46959689912eSchristos 	}
46969689912eSchristos 
46979689912eSchristos 	/*
46989689912eSchristos 	 * If we're adding a delegation type (e.g. NS or DNAME),
46999689912eSchristos 	 * then we need to set the callback bit on the node.
47009689912eSchristos 	 */
47019689912eSchristos 	if (result == ISC_R_SUCCESS &&
47029689912eSchristos 	    delegating_type(qpdb, node, rdataset->type))
47039689912eSchristos 	{
47049689912eSchristos 		node->delegating = true;
47059689912eSchristos 	}
47069689912eSchristos 
47079689912eSchristos 	NODE_UNLOCK(&qpdb->node_locks[node->locknum].lock, &nlocktype);
47089689912eSchristos 
47099689912eSchristos 	if (nsec != NULL) {
47109689912eSchristos 		dns_qpmulti_commit(qpdb->nsec, &nsec);
47119689912eSchristos 	}
47129689912eSchristos 
47139689912eSchristos 	return result;
47149689912eSchristos }
47159689912eSchristos 
47169689912eSchristos static isc_result_t
47179689912eSchristos subtractrdataset(dns_db_t *db, dns_dbnode_t *dbnode, dns_dbversion_t *dbversion,
47189689912eSchristos 		 dns_rdataset_t *rdataset, unsigned int options,
47199689912eSchristos 		 dns_rdataset_t *newrdataset DNS__DB_FLARG) {
47209689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)db;
47219689912eSchristos 	qpznode_t *node = (qpznode_t *)dbnode;
47229689912eSchristos 	qpz_version_t *version = dbversion;
47239689912eSchristos 	dns_fixedname_t fname;
47249689912eSchristos 	dns_name_t *nodename = dns_fixedname_initname(&fname);
47259689912eSchristos 	dns_slabheader_t *topheader = NULL, *topheader_prev = NULL;
47269689912eSchristos 	dns_slabheader_t *header = NULL, *newheader = NULL;
47279689912eSchristos 	unsigned char *subresult = NULL;
47289689912eSchristos 	isc_region_t region;
47299689912eSchristos 	isc_result_t result;
47309689912eSchristos 	qpz_changed_t *changed = NULL;
47319689912eSchristos 	isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
47329689912eSchristos 
47339689912eSchristos 	REQUIRE(VALID_QPZONE(qpdb));
47349689912eSchristos 	REQUIRE(version != NULL && version->qpdb == qpdb);
47359689912eSchristos 
47369689912eSchristos 	REQUIRE((node->nsec == DNS_DB_NSEC_NSEC3 &&
47379689912eSchristos 		 (rdataset->type == dns_rdatatype_nsec3 ||
47389689912eSchristos 		  rdataset->covers == dns_rdatatype_nsec3)) ||
47399689912eSchristos 		(node->nsec != DNS_DB_NSEC_NSEC3 &&
47409689912eSchristos 		 rdataset->type != dns_rdatatype_nsec3 &&
47419689912eSchristos 		 rdataset->covers != dns_rdatatype_nsec3));
47429689912eSchristos 
47439689912eSchristos 	dns_name_copy(&node->name, nodename);
47449689912eSchristos 	result = dns_rdataslab_fromrdataset(rdataset, qpdb->common.mctx,
47459689912eSchristos 					    &region, sizeof(dns_slabheader_t),
47469689912eSchristos 					    0);
47479689912eSchristos 	if (result != ISC_R_SUCCESS) {
47489689912eSchristos 		return result;
47499689912eSchristos 	}
47509689912eSchristos 
47519689912eSchristos 	newheader = (dns_slabheader_t *)region.base;
47529689912eSchristos 	dns_slabheader_reset(newheader, db, node);
47539689912eSchristos 	newheader->ttl = rdataset->ttl;
47549689912eSchristos 	newheader->type = DNS_TYPEPAIR_VALUE(rdataset->type, rdataset->covers);
47559689912eSchristos 	atomic_init(&newheader->attributes, 0);
47569689912eSchristos 	newheader->serial = version->serial;
47579689912eSchristos 	newheader->trust = 0;
47589689912eSchristos 	newheader->noqname = NULL;
47599689912eSchristos 	newheader->closest = NULL;
47609689912eSchristos 	atomic_init(&newheader->count,
47619689912eSchristos 		    atomic_fetch_add_relaxed(&init_count, 1));
47629689912eSchristos 	newheader->last_used = 0;
47639689912eSchristos 	newheader->node = node;
47649689912eSchristos 	newheader->db = (dns_db_t *)qpdb;
47659689912eSchristos 	if ((rdataset->attributes & DNS_RDATASETATTR_RESIGN) != 0) {
47669689912eSchristos 		DNS_SLABHEADER_SETATTR(newheader, DNS_SLABHEADERATTR_RESIGN);
47679689912eSchristos 		newheader->resign =
47689689912eSchristos 			(isc_stdtime_t)(dns_time64_from32(rdataset->resign) >>
47699689912eSchristos 					1);
47709689912eSchristos 		newheader->resign_lsb = rdataset->resign & 0x1;
47719689912eSchristos 	} else {
47729689912eSchristos 		newheader->resign = 0;
47739689912eSchristos 		newheader->resign_lsb = 0;
47749689912eSchristos 	}
47759689912eSchristos 
47769689912eSchristos 	NODE_WRLOCK(&qpdb->node_locks[node->locknum].lock, &nlocktype);
47779689912eSchristos 
47789689912eSchristos 	changed = add_changed(newheader, version DNS__DB_FLARG_PASS);
47799689912eSchristos 	for (topheader = node->data; topheader != NULL;
47809689912eSchristos 	     topheader = topheader->next)
47819689912eSchristos 	{
47829689912eSchristos 		if (topheader->type == newheader->type) {
47839689912eSchristos 			break;
47849689912eSchristos 		}
47859689912eSchristos 		topheader_prev = topheader;
47869689912eSchristos 	}
47879689912eSchristos 	/*
47889689912eSchristos 	 * If header isn't NULL, we've found the right type.  There may be
47899689912eSchristos 	 * IGNORE rdatasets between the top of the chain and the first real
47909689912eSchristos 	 * data.  We skip over them.
47919689912eSchristos 	 */
47929689912eSchristos 	header = topheader;
47939689912eSchristos 	while (header != NULL && IGNORE(header)) {
47949689912eSchristos 		header = header->down;
47959689912eSchristos 	}
47969689912eSchristos 	if (header != NULL && !NONEXISTENT(header)) {
47979689912eSchristos 		unsigned int flags = 0;
47989689912eSchristos 		subresult = NULL;
47999689912eSchristos 		result = ISC_R_SUCCESS;
48009689912eSchristos 		if ((options & DNS_DBSUB_EXACT) != 0) {
48019689912eSchristos 			flags |= DNS_RDATASLAB_EXACT;
48029689912eSchristos 			if (newheader->ttl != header->ttl) {
48039689912eSchristos 				result = DNS_R_NOTEXACT;
48049689912eSchristos 			}
48059689912eSchristos 		}
48069689912eSchristos 		if (result == ISC_R_SUCCESS) {
48079689912eSchristos 			result = dns_rdataslab_subtract(
48089689912eSchristos 				(unsigned char *)header,
48099689912eSchristos 				(unsigned char *)newheader,
48109689912eSchristos 				(unsigned int)(sizeof(*newheader)),
48119689912eSchristos 				qpdb->common.mctx, qpdb->common.rdclass,
48129689912eSchristos 				(dns_rdatatype_t)header->type, flags,
48139689912eSchristos 				&subresult);
48149689912eSchristos 		}
48159689912eSchristos 		if (result == ISC_R_SUCCESS) {
48169689912eSchristos 			dns_slabheader_destroy(&newheader);
48179689912eSchristos 			newheader = (dns_slabheader_t *)subresult;
48189689912eSchristos 			dns_slabheader_reset(newheader, db, node);
48199689912eSchristos 			dns_slabheader_copycase(newheader, header);
48209689912eSchristos 			if (RESIGN(header)) {
48219689912eSchristos 				DNS_SLABHEADER_SETATTR(
48229689912eSchristos 					newheader, DNS_SLABHEADERATTR_RESIGN);
48239689912eSchristos 				newheader->resign = header->resign;
48249689912eSchristos 				newheader->resign_lsb = header->resign_lsb;
48259689912eSchristos 				resigninsert(qpdb, newheader);
48269689912eSchristos 			}
48279689912eSchristos 			/*
48289689912eSchristos 			 * We have to set the serial since the rdataslab
48299689912eSchristos 			 * subtraction routine copies the reserved portion of
48309689912eSchristos 			 * header, not newheader.
48319689912eSchristos 			 */
48329689912eSchristos 			newheader->serial = version->serial;
48339689912eSchristos 			/*
48349689912eSchristos 			 * XXXJT: dns_rdataslab_subtract() copied the pointers
48359689912eSchristos 			 * to additional info.  We need to clear these fields
48369689912eSchristos 			 * to avoid having duplicated references.
48379689912eSchristos 			 */
48389689912eSchristos 			maybe_update_recordsandsize(true, version, newheader,
48399689912eSchristos 						    nodename->length);
48409689912eSchristos 		} else if (result == DNS_R_NXRRSET) {
48419689912eSchristos 			/*
48429689912eSchristos 			 * This subtraction would remove all of the rdata;
48439689912eSchristos 			 * add a nonexistent header instead.
48449689912eSchristos 			 */
48459689912eSchristos 			dns_slabheader_destroy(&newheader);
48469689912eSchristos 			newheader = dns_slabheader_new((dns_db_t *)qpdb,
48479689912eSchristos 						       (dns_dbnode_t *)node);
48489689912eSchristos 			newheader->ttl = 0;
48499689912eSchristos 			newheader->type = topheader->type;
48509689912eSchristos 			atomic_init(&newheader->attributes,
48519689912eSchristos 				    DNS_SLABHEADERATTR_NONEXISTENT);
48529689912eSchristos 			newheader->serial = version->serial;
48539689912eSchristos 		} else {
48549689912eSchristos 			dns_slabheader_destroy(&newheader);
48559689912eSchristos 			goto unlock;
48569689912eSchristos 		}
48579689912eSchristos 
48589689912eSchristos 		/*
48599689912eSchristos 		 * If we're here, we want to link newheader in front of
48609689912eSchristos 		 * topheader.
48619689912eSchristos 		 */
48629689912eSchristos 		INSIST(version->serial >= topheader->serial);
48639689912eSchristos 		maybe_update_recordsandsize(false, version, header,
48649689912eSchristos 					    nodename->length);
48659689912eSchristos 		if (topheader_prev != NULL) {
48669689912eSchristos 			topheader_prev->next = newheader;
48679689912eSchristos 		} else {
48689689912eSchristos 			node->data = newheader;
48699689912eSchristos 		}
48709689912eSchristos 		newheader->next = topheader->next;
48719689912eSchristos 		newheader->down = topheader;
48729689912eSchristos 		topheader->next = newheader;
48739689912eSchristos 		node->dirty = true;
48749689912eSchristos 		changed->dirty = true;
48759689912eSchristos 		resigndelete(qpdb, version, header DNS__DB_FLARG_PASS);
48769689912eSchristos 	} else {
48779689912eSchristos 		/*
48789689912eSchristos 		 * The rdataset doesn't exist, so we don't need to do anything
48799689912eSchristos 		 * to satisfy the deletion request.
48809689912eSchristos 		 */
48819689912eSchristos 		dns_slabheader_destroy(&newheader);
48829689912eSchristos 		if ((options & DNS_DBSUB_EXACT) != 0) {
48839689912eSchristos 			result = DNS_R_NOTEXACT;
48849689912eSchristos 		} else {
48859689912eSchristos 			result = DNS_R_UNCHANGED;
48869689912eSchristos 		}
48879689912eSchristos 	}
48889689912eSchristos 
48899689912eSchristos 	if (result == ISC_R_SUCCESS && newrdataset != NULL) {
48909689912eSchristos 		bindrdataset(qpdb, node, newheader, 0,
48919689912eSchristos 			     newrdataset DNS__DB_FLARG_PASS);
48929689912eSchristos 	}
48939689912eSchristos 
48949689912eSchristos 	if (result == DNS_R_NXRRSET && newrdataset != NULL &&
48959689912eSchristos 	    (options & DNS_DBSUB_WANTOLD) != 0)
48969689912eSchristos 	{
48979689912eSchristos 		bindrdataset(qpdb, node, header, 0,
48989689912eSchristos 			     newrdataset DNS__DB_FLARG_PASS);
48999689912eSchristos 	}
49009689912eSchristos 
49019689912eSchristos unlock:
49029689912eSchristos 	NODE_UNLOCK(&qpdb->node_locks[node->locknum].lock, &nlocktype);
49039689912eSchristos 	return result;
49049689912eSchristos }
49059689912eSchristos 
49069689912eSchristos static isc_result_t
49079689912eSchristos deleterdataset(dns_db_t *db, dns_dbnode_t *dbnode, dns_dbversion_t *dbversion,
49089689912eSchristos 	       dns_rdatatype_t type, dns_rdatatype_t covers DNS__DB_FLARG) {
49099689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)db;
49109689912eSchristos 	qpznode_t *node = (qpznode_t *)dbnode;
49119689912eSchristos 	qpz_version_t *version = dbversion;
49129689912eSchristos 	dns_fixedname_t fname;
49139689912eSchristos 	dns_name_t *nodename = dns_fixedname_initname(&fname);
49149689912eSchristos 	isc_result_t result;
49159689912eSchristos 	dns_slabheader_t *newheader = NULL;
49169689912eSchristos 	isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
49179689912eSchristos 
49189689912eSchristos 	REQUIRE(VALID_QPZONE(qpdb));
49199689912eSchristos 	REQUIRE(version != NULL && version->qpdb == qpdb);
49209689912eSchristos 
49219689912eSchristos 	if (type == dns_rdatatype_any) {
49229689912eSchristos 		return ISC_R_NOTIMPLEMENTED;
49239689912eSchristos 	}
49249689912eSchristos 	if (type == dns_rdatatype_rrsig && covers == 0) {
49259689912eSchristos 		return ISC_R_NOTIMPLEMENTED;
49269689912eSchristos 	}
49279689912eSchristos 
49289689912eSchristos 	newheader = dns_slabheader_new(db, node);
49299689912eSchristos 	newheader->type = DNS_TYPEPAIR_VALUE(type, covers);
49309689912eSchristos 	newheader->ttl = 0;
49319689912eSchristos 	atomic_init(&newheader->attributes, DNS_SLABHEADERATTR_NONEXISTENT);
49329689912eSchristos 	newheader->serial = version->serial;
49339689912eSchristos 
49349689912eSchristos 	dns_name_copy(&node->name, nodename);
49359689912eSchristos 
49369689912eSchristos 	NODE_WRLOCK(&qpdb->node_locks[node->locknum].lock, &nlocktype);
49379689912eSchristos 	result = add(qpdb, node, nodename, version, newheader, DNS_DBADD_FORCE,
49389689912eSchristos 		     false, NULL, 0 DNS__DB_FLARG_PASS);
49399689912eSchristos 	NODE_UNLOCK(&qpdb->node_locks[node->locknum].lock, &nlocktype);
49409689912eSchristos 	return result;
49419689912eSchristos }
49429689912eSchristos 
49439689912eSchristos static isc_result_t
49449689912eSchristos nodefullname(dns_db_t *db, dns_dbnode_t *node, dns_name_t *name) {
49459689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)db;
49469689912eSchristos 	qpznode_t *qpnode = (qpznode_t *)node;
49479689912eSchristos 	isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
49489689912eSchristos 
49499689912eSchristos 	REQUIRE(VALID_QPZONE(qpdb));
49509689912eSchristos 	REQUIRE(node != NULL);
49519689912eSchristos 	REQUIRE(name != NULL);
49529689912eSchristos 
49539689912eSchristos 	NODE_RDLOCK(&qpdb->node_locks[qpnode->locknum].lock, &nlocktype);
49549689912eSchristos 	dns_name_copy(&qpnode->name, name);
49559689912eSchristos 	NODE_UNLOCK(&qpdb->node_locks[qpnode->locknum].lock, &nlocktype);
49569689912eSchristos 	return ISC_R_SUCCESS;
49579689912eSchristos }
49589689912eSchristos 
49599689912eSchristos static dns_glue_t *
49609689912eSchristos new_gluelist(isc_mem_t *mctx, dns_name_t *name) {
49619689912eSchristos 	dns_glue_t *glue = isc_mem_get(mctx, sizeof(*glue));
49629689912eSchristos 	*glue = (dns_glue_t){ 0 };
49639689912eSchristos 	dns_name_t *gluename = dns_fixedname_initname(&glue->fixedname);
49649689912eSchristos 
49659689912eSchristos 	dns_name_copy(name, gluename);
49669689912eSchristos 
49679689912eSchristos 	return glue;
49689689912eSchristos }
49699689912eSchristos 
49709689912eSchristos static isc_result_t
49719689912eSchristos glue_nsdname_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype,
49729689912eSchristos 		dns_rdataset_t *unused DNS__DB_FLARG) {
49739689912eSchristos 	dns_glue_additionaldata_ctx_t *ctx = NULL;
49749689912eSchristos 	isc_result_t result;
49759689912eSchristos 	dns_fixedname_t fixedname_a;
49769689912eSchristos 	dns_name_t *name_a = NULL;
49779689912eSchristos 	dns_rdataset_t rdataset_a, sigrdataset_a;
49789689912eSchristos 	qpznode_t *node_a = NULL;
49799689912eSchristos 	dns_fixedname_t fixedname_aaaa;
49809689912eSchristos 	dns_name_t *name_aaaa = NULL;
49819689912eSchristos 	dns_rdataset_t rdataset_aaaa, sigrdataset_aaaa;
49829689912eSchristos 	qpznode_t *node_aaaa = NULL;
49839689912eSchristos 	dns_glue_t *glue = NULL;
49849689912eSchristos 
49859689912eSchristos 	UNUSED(unused);
49869689912eSchristos 
49879689912eSchristos 	/*
49889689912eSchristos 	 * NS records want addresses in additional records.
49899689912eSchristos 	 */
49909689912eSchristos 	INSIST(qtype == dns_rdatatype_a);
49919689912eSchristos 
49929689912eSchristos 	ctx = (dns_glue_additionaldata_ctx_t *)arg;
49939689912eSchristos 
49949689912eSchristos 	name_a = dns_fixedname_initname(&fixedname_a);
49959689912eSchristos 	dns_rdataset_init(&rdataset_a);
49969689912eSchristos 	dns_rdataset_init(&sigrdataset_a);
49979689912eSchristos 
49989689912eSchristos 	name_aaaa = dns_fixedname_initname(&fixedname_aaaa);
49999689912eSchristos 	dns_rdataset_init(&rdataset_aaaa);
50009689912eSchristos 	dns_rdataset_init(&sigrdataset_aaaa);
50019689912eSchristos 
50029689912eSchristos 	result = find(ctx->db, name, ctx->version, dns_rdatatype_a,
50039689912eSchristos 		      DNS_DBFIND_GLUEOK, 0, (dns_dbnode_t **)&node_a, name_a,
50049689912eSchristos 		      &rdataset_a, &sigrdataset_a DNS__DB_FLARG_PASS);
50059689912eSchristos 	if (result == DNS_R_GLUE) {
50069689912eSchristos 		glue = new_gluelist(ctx->db->mctx, name_a);
50079689912eSchristos 
50089689912eSchristos 		dns_rdataset_init(&glue->rdataset_a);
50099689912eSchristos 		dns_rdataset_init(&glue->sigrdataset_a);
50109689912eSchristos 		dns_rdataset_init(&glue->rdataset_aaaa);
50119689912eSchristos 		dns_rdataset_init(&glue->sigrdataset_aaaa);
50129689912eSchristos 
50139689912eSchristos 		dns_rdataset_clone(&rdataset_a, &glue->rdataset_a);
50149689912eSchristos 		if (dns_rdataset_isassociated(&sigrdataset_a)) {
50159689912eSchristos 			dns_rdataset_clone(&sigrdataset_a,
50169689912eSchristos 					   &glue->sigrdataset_a);
50179689912eSchristos 		}
50189689912eSchristos 	}
50199689912eSchristos 
50209689912eSchristos 	result = find(ctx->db, name, ctx->version, dns_rdatatype_aaaa,
50219689912eSchristos 		      DNS_DBFIND_GLUEOK, 0, (dns_dbnode_t **)&node_aaaa,
50229689912eSchristos 		      name_aaaa, &rdataset_aaaa,
50239689912eSchristos 		      &sigrdataset_aaaa DNS__DB_FLARG_PASS);
50249689912eSchristos 	if (result == DNS_R_GLUE) {
50259689912eSchristos 		if (glue == NULL) {
50269689912eSchristos 			glue = new_gluelist(ctx->db->mctx, name_aaaa);
50279689912eSchristos 
50289689912eSchristos 			dns_rdataset_init(&glue->rdataset_a);
50299689912eSchristos 			dns_rdataset_init(&glue->sigrdataset_a);
50309689912eSchristos 			dns_rdataset_init(&glue->rdataset_aaaa);
50319689912eSchristos 			dns_rdataset_init(&glue->sigrdataset_aaaa);
50329689912eSchristos 		} else {
50339689912eSchristos 			INSIST(node_a == node_aaaa);
50349689912eSchristos 			INSIST(dns_name_equal(name_a, name_aaaa));
50359689912eSchristos 		}
50369689912eSchristos 
50379689912eSchristos 		dns_rdataset_clone(&rdataset_aaaa, &glue->rdataset_aaaa);
50389689912eSchristos 		if (dns_rdataset_isassociated(&sigrdataset_aaaa)) {
50399689912eSchristos 			dns_rdataset_clone(&sigrdataset_aaaa,
50409689912eSchristos 					   &glue->sigrdataset_aaaa);
50419689912eSchristos 		}
50429689912eSchristos 	}
50439689912eSchristos 
50449689912eSchristos 	/*
50459689912eSchristos 	 * If the currently processed NS record is in-bailiwick, mark any glue
50469689912eSchristos 	 * RRsets found for it with DNS_RDATASETATTR_REQUIRED.  Note that for
50479689912eSchristos 	 * simplicity, glue RRsets for all in-bailiwick NS records are marked
50489689912eSchristos 	 * this way, even though dns_message_rendersection() only checks the
50499689912eSchristos 	 * attributes for the first rdataset associated with the first name
50509689912eSchristos 	 * added to the ADDITIONAL section.
50519689912eSchristos 	 */
50529689912eSchristos 	if (glue != NULL && dns_name_issubdomain(name, ctx->nodename)) {
50539689912eSchristos 		if (dns_rdataset_isassociated(&glue->rdataset_a)) {
50549689912eSchristos 			glue->rdataset_a.attributes |=
50559689912eSchristos 				DNS_RDATASETATTR_REQUIRED;
50569689912eSchristos 		}
50579689912eSchristos 		if (dns_rdataset_isassociated(&glue->rdataset_aaaa)) {
50589689912eSchristos 			glue->rdataset_aaaa.attributes |=
50599689912eSchristos 				DNS_RDATASETATTR_REQUIRED;
50609689912eSchristos 		}
50619689912eSchristos 	}
50629689912eSchristos 
50639689912eSchristos 	if (glue != NULL) {
50649689912eSchristos 		glue->next = ctx->glue_list;
50659689912eSchristos 		ctx->glue_list = glue;
50669689912eSchristos 	}
50679689912eSchristos 
50689689912eSchristos 	result = ISC_R_SUCCESS;
50699689912eSchristos 
50709689912eSchristos 	if (dns_rdataset_isassociated(&rdataset_a)) {
50719689912eSchristos 		dns_rdataset_disassociate(&rdataset_a);
50729689912eSchristos 	}
50739689912eSchristos 	if (dns_rdataset_isassociated(&sigrdataset_a)) {
50749689912eSchristos 		dns_rdataset_disassociate(&sigrdataset_a);
50759689912eSchristos 	}
50769689912eSchristos 
50779689912eSchristos 	if (dns_rdataset_isassociated(&rdataset_aaaa)) {
50789689912eSchristos 		dns_rdataset_disassociate(&rdataset_aaaa);
50799689912eSchristos 	}
50809689912eSchristos 	if (dns_rdataset_isassociated(&sigrdataset_aaaa)) {
50819689912eSchristos 		dns_rdataset_disassociate(&sigrdataset_aaaa);
50829689912eSchristos 	}
50839689912eSchristos 
50849689912eSchristos 	if (node_a != NULL) {
50859689912eSchristos 		dns__db_detachnode(ctx->db,
50869689912eSchristos 				   (dns_dbnode_t *)&node_a DNS__DB_FLARG_PASS);
50879689912eSchristos 	}
50889689912eSchristos 	if (node_aaaa != NULL) {
50899689912eSchristos 		dns__db_detachnode(
50909689912eSchristos 			ctx->db, (dns_dbnode_t *)&node_aaaa DNS__DB_FLARG_PASS);
50919689912eSchristos 	}
50929689912eSchristos 
50939689912eSchristos 	return result;
50949689912eSchristos }
50959689912eSchristos 
50969689912eSchristos #define IS_REQUIRED_GLUE(r) (((r)->attributes & DNS_RDATASETATTR_REQUIRED) != 0)
50979689912eSchristos 
50989689912eSchristos static void
50999689912eSchristos addglue_to_message(dns_glue_t *ge, dns_message_t *msg) {
51009689912eSchristos 	for (; ge != NULL; ge = ge->next) {
51019689912eSchristos 		dns_name_t *name = NULL;
51029689912eSchristos 		dns_rdataset_t *rdataset_a = NULL;
51039689912eSchristos 		dns_rdataset_t *sigrdataset_a = NULL;
51049689912eSchristos 		dns_rdataset_t *rdataset_aaaa = NULL;
51059689912eSchristos 		dns_rdataset_t *sigrdataset_aaaa = NULL;
51069689912eSchristos 		dns_name_t *gluename = dns_fixedname_name(&ge->fixedname);
51079689912eSchristos 		bool prepend_name = false;
51089689912eSchristos 
51099689912eSchristos 		dns_message_gettempname(msg, &name);
51109689912eSchristos 
51119689912eSchristos 		dns_name_copy(gluename, name);
51129689912eSchristos 
51139689912eSchristos 		if (dns_rdataset_isassociated(&ge->rdataset_a)) {
51149689912eSchristos 			dns_message_gettemprdataset(msg, &rdataset_a);
51159689912eSchristos 		}
51169689912eSchristos 
51179689912eSchristos 		if (dns_rdataset_isassociated(&ge->sigrdataset_a)) {
51189689912eSchristos 			dns_message_gettemprdataset(msg, &sigrdataset_a);
51199689912eSchristos 		}
51209689912eSchristos 
51219689912eSchristos 		if (dns_rdataset_isassociated(&ge->rdataset_aaaa)) {
51229689912eSchristos 			dns_message_gettemprdataset(msg, &rdataset_aaaa);
51239689912eSchristos 		}
51249689912eSchristos 
51259689912eSchristos 		if (dns_rdataset_isassociated(&ge->sigrdataset_aaaa)) {
51269689912eSchristos 			dns_message_gettemprdataset(msg, &sigrdataset_aaaa);
51279689912eSchristos 		}
51289689912eSchristos 
51299689912eSchristos 		if (rdataset_a != NULL) {
51309689912eSchristos 			dns_rdataset_clone(&ge->rdataset_a, rdataset_a);
51319689912eSchristos 			ISC_LIST_APPEND(name->list, rdataset_a, link);
51329689912eSchristos 			if (IS_REQUIRED_GLUE(rdataset_a)) {
51339689912eSchristos 				prepend_name = true;
51349689912eSchristos 			}
51359689912eSchristos 		}
51369689912eSchristos 
51379689912eSchristos 		if (sigrdataset_a != NULL) {
51389689912eSchristos 			dns_rdataset_clone(&ge->sigrdataset_a, sigrdataset_a);
51399689912eSchristos 			ISC_LIST_APPEND(name->list, sigrdataset_a, link);
51409689912eSchristos 		}
51419689912eSchristos 
51429689912eSchristos 		if (rdataset_aaaa != NULL) {
51439689912eSchristos 			dns_rdataset_clone(&ge->rdataset_aaaa, rdataset_aaaa);
51449689912eSchristos 			ISC_LIST_APPEND(name->list, rdataset_aaaa, link);
51459689912eSchristos 			if (IS_REQUIRED_GLUE(rdataset_aaaa)) {
51469689912eSchristos 				prepend_name = true;
51479689912eSchristos 			}
51489689912eSchristos 		}
51499689912eSchristos 		if (sigrdataset_aaaa != NULL) {
51509689912eSchristos 			dns_rdataset_clone(&ge->sigrdataset_aaaa,
51519689912eSchristos 					   sigrdataset_aaaa);
51529689912eSchristos 			ISC_LIST_APPEND(name->list, sigrdataset_aaaa, link);
51539689912eSchristos 		}
51549689912eSchristos 
51559689912eSchristos 		dns_message_addname(msg, name, DNS_SECTION_ADDITIONAL);
51569689912eSchristos 
51579689912eSchristos 		/*
51589689912eSchristos 		 * When looking for required glue, dns_message_rendersection()
51599689912eSchristos 		 * only processes the first rdataset associated with the first
51609689912eSchristos 		 * name added to the ADDITIONAL section.  dns_message_addname()
51619689912eSchristos 		 * performs an append on the list of names in a given section,
51629689912eSchristos 		 * so if any glue record was marked as required, we need to
51639689912eSchristos 		 * move the name it is associated with to the beginning of the
51649689912eSchristos 		 * list for the ADDITIONAL section or else required glue might
51659689912eSchristos 		 * not be rendered.
51669689912eSchristos 		 */
51679689912eSchristos 		if (prepend_name) {
51689689912eSchristos 			ISC_LIST_UNLINK(msg->sections[DNS_SECTION_ADDITIONAL],
51699689912eSchristos 					name, link);
51709689912eSchristos 			ISC_LIST_PREPEND(msg->sections[DNS_SECTION_ADDITIONAL],
51719689912eSchristos 					 name, link);
51729689912eSchristos 		}
51739689912eSchristos 	}
51749689912eSchristos }
51759689912eSchristos 
51769689912eSchristos static dns_glue_t *
51779689912eSchristos newglue(dns_db_t *db, qpz_version_t *version, qpznode_t *node,
51789689912eSchristos 	dns_rdataset_t *rdataset) {
51799689912eSchristos 	dns_fixedname_t nodename;
51809689912eSchristos 	dns_glue_additionaldata_ctx_t ctx = {
51819689912eSchristos 		.db = db,
51829689912eSchristos 		.version = (dns_dbversion_t *)version,
51839689912eSchristos 		.nodename = dns_fixedname_initname(&nodename),
51849689912eSchristos 	};
51859689912eSchristos 
51869689912eSchristos 	/*
51879689912eSchristos 	 * Get the owner name of the NS RRset - it will be necessary for
51889689912eSchristos 	 * identifying required glue in glue_nsdname_cb() (by
51899689912eSchristos 	 * determining which NS records in the delegation are
51909689912eSchristos 	 * in-bailiwick).
51919689912eSchristos 	 */
51929689912eSchristos 	dns_name_copy(&node->name, ctx.nodename);
51939689912eSchristos 
51949689912eSchristos 	(void)dns_rdataset_additionaldata(rdataset, dns_rootname,
51959689912eSchristos 					  glue_nsdname_cb, &ctx);
51969689912eSchristos 
51979689912eSchristos 	return ctx.glue_list;
51989689912eSchristos }
51999689912eSchristos 
52009689912eSchristos static dns_gluenode_t *
52019689912eSchristos new_gluenode(dns_db_t *db, qpz_version_t *version, qpznode_t *node,
52029689912eSchristos 	     dns_rdataset_t *rdataset) {
52039689912eSchristos 	dns_gluenode_t *gluenode = isc_mem_get(db->mctx, sizeof(*gluenode));
52049689912eSchristos 	*gluenode = (dns_gluenode_t){
52059689912eSchristos 		.glue = newglue(db, version, node, rdataset),
52069689912eSchristos 	};
52079689912eSchristos 
52089689912eSchristos 	isc_mem_attach(db->mctx, &gluenode->mctx);
52099689912eSchristos 	qpznode_attach(node, &gluenode->node);
52109689912eSchristos 
52119689912eSchristos 	return gluenode;
52129689912eSchristos }
52139689912eSchristos 
52149689912eSchristos static void
52159689912eSchristos freeglue(isc_mem_t *mctx, dns_glue_t *glue) {
52169689912eSchristos 	while (glue != NULL) {
52179689912eSchristos 		dns_glue_t *next = glue->next;
52189689912eSchristos 
52199689912eSchristos 		if (dns_rdataset_isassociated(&glue->rdataset_a)) {
52209689912eSchristos 			dns_rdataset_disassociate(&glue->rdataset_a);
52219689912eSchristos 		}
52229689912eSchristos 		if (dns_rdataset_isassociated(&glue->sigrdataset_a)) {
52239689912eSchristos 			dns_rdataset_disassociate(&glue->sigrdataset_a);
52249689912eSchristos 		}
52259689912eSchristos 
52269689912eSchristos 		if (dns_rdataset_isassociated(&glue->rdataset_aaaa)) {
52279689912eSchristos 			dns_rdataset_disassociate(&glue->rdataset_aaaa);
52289689912eSchristos 		}
52299689912eSchristos 		if (dns_rdataset_isassociated(&glue->sigrdataset_aaaa)) {
52309689912eSchristos 			dns_rdataset_disassociate(&glue->sigrdataset_aaaa);
52319689912eSchristos 		}
52329689912eSchristos 
52339689912eSchristos 		dns_rdataset_invalidate(&glue->rdataset_a);
52349689912eSchristos 		dns_rdataset_invalidate(&glue->sigrdataset_a);
52359689912eSchristos 		dns_rdataset_invalidate(&glue->rdataset_aaaa);
52369689912eSchristos 		dns_rdataset_invalidate(&glue->sigrdataset_aaaa);
52379689912eSchristos 
52389689912eSchristos 		isc_mem_put(mctx, glue, sizeof(*glue));
52399689912eSchristos 
52409689912eSchristos 		glue = next;
52419689912eSchristos 	}
52429689912eSchristos }
52439689912eSchristos 
52449689912eSchristos static void
52459689912eSchristos free_gluenode_rcu(struct rcu_head *rcu_head) {
52469689912eSchristos 	dns_gluenode_t *gluenode = caa_container_of(rcu_head, dns_gluenode_t,
52479689912eSchristos 						    rcu_head);
52489689912eSchristos 
52499689912eSchristos 	freeglue(gluenode->mctx, gluenode->glue);
52509689912eSchristos 
52519689912eSchristos 	qpznode_detach(&gluenode->node);
52529689912eSchristos 
52539689912eSchristos 	isc_mem_putanddetach(&gluenode->mctx, gluenode, sizeof(*gluenode));
52549689912eSchristos }
52559689912eSchristos 
52569689912eSchristos static void
52579689912eSchristos free_gluenode(dns_gluenode_t *gluenode) {
52589689912eSchristos 	call_rcu(&gluenode->rcu_head, free_gluenode_rcu);
52599689912eSchristos }
52609689912eSchristos 
52619689912eSchristos static uint32_t
52629689912eSchristos qpznode_hash(const qpznode_t *node) {
52639689912eSchristos 	return isc_hash32(&node, sizeof(node), true);
52649689912eSchristos }
52659689912eSchristos 
52669689912eSchristos static int
52679689912eSchristos qpznode_match(struct cds_lfht_node *ht_node, const void *key) {
52689689912eSchristos 	const dns_gluenode_t *gluenode =
52699689912eSchristos 		caa_container_of(ht_node, dns_gluenode_t, ht_node);
52709689912eSchristos 	const qpznode_t *node = key;
52719689912eSchristos 
52729689912eSchristos 	return gluenode->node == node;
52739689912eSchristos }
52749689912eSchristos 
52759689912eSchristos static uint32_t
52769689912eSchristos gluenode_hash(const dns_gluenode_t *gluenode) {
52779689912eSchristos 	return qpznode_hash(gluenode->node);
52789689912eSchristos }
52799689912eSchristos 
52809689912eSchristos static int
52819689912eSchristos gluenode_match(struct cds_lfht_node *ht_node, const void *key) {
52829689912eSchristos 	const dns_gluenode_t *gluenode = key;
52839689912eSchristos 
52849689912eSchristos 	return qpznode_match(ht_node, gluenode->node);
52859689912eSchristos }
52869689912eSchristos 
52879689912eSchristos static void
52889689912eSchristos addglue(dns_db_t *db, dns_dbversion_t *dbversion, dns_rdataset_t *rdataset,
52899689912eSchristos 	dns_message_t *msg) {
52909689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)db;
52919689912eSchristos 	qpz_version_t *version = dbversion;
52929689912eSchristos 	qpznode_t *node = (qpznode_t *)rdataset->slab.node;
52939689912eSchristos 	dns_gluenode_t *gluenode = NULL;
52949689912eSchristos 
52959689912eSchristos 	REQUIRE(rdataset->type == dns_rdatatype_ns);
52969689912eSchristos 	REQUIRE(qpdb == (qpzonedb_t *)rdataset->slab.db);
52979689912eSchristos 	REQUIRE(qpdb == version->qpdb);
52989689912eSchristos 	REQUIRE(!IS_STUB(qpdb));
52999689912eSchristos 
53009689912eSchristos 	/*
53019689912eSchristos 	 * The glue table cache that forms a part of the DB version
53029689912eSchristos 	 * structure is not explicitly bounded and there's no cache
53039689912eSchristos 	 * cleaning. The zone data size itself is an implicit bound.
53049689912eSchristos 	 *
53059689912eSchristos 	 * The key into the glue hashtable is the node pointer. This is
53069689912eSchristos 	 * because the glue hashtable is a property of the DB version,
53079689912eSchristos 	 * and the glue is keyed for the ownername/NS tuple. We don't
53089689912eSchristos 	 * bother with using an expensive dns_name_t comparison here as
53099689912eSchristos 	 * the node pointer is a fixed value that won't change for a DB
53109689912eSchristos 	 * version and can be compared directly.
53119689912eSchristos 	 */
53129689912eSchristos 
53139689912eSchristos 	rcu_read_lock();
53149689912eSchristos 
53159689912eSchristos 	struct cds_lfht_iter iter;
53169689912eSchristos 	cds_lfht_lookup(version->glue_table, qpznode_hash(node), qpznode_match,
53179689912eSchristos 			node, &iter);
53189689912eSchristos 
53199689912eSchristos 	gluenode = cds_lfht_entry(cds_lfht_iter_get_node(&iter), dns_gluenode_t,
53209689912eSchristos 				  ht_node);
53219689912eSchristos 	if (gluenode == NULL) {
53229689912eSchristos 		/* No cached glue was found in the table. Get new glue. */
53239689912eSchristos 		gluenode = new_gluenode(db, version, node, rdataset);
53249689912eSchristos 
53259689912eSchristos 		struct cds_lfht_node *ht_node = cds_lfht_add_unique(
53269689912eSchristos 			version->glue_table, gluenode_hash(gluenode),
53279689912eSchristos 			gluenode_match, gluenode, &gluenode->ht_node);
53289689912eSchristos 
53299689912eSchristos 		if (ht_node != &gluenode->ht_node) {
53309689912eSchristos 			free_gluenode_rcu(&gluenode->rcu_head);
53319689912eSchristos 
53329689912eSchristos 			gluenode = cds_lfht_entry(ht_node, dns_gluenode_t,
53339689912eSchristos 						  ht_node);
53349689912eSchristos 		}
53359689912eSchristos 	}
53369689912eSchristos 
53379689912eSchristos 	INSIST(gluenode != NULL);
53389689912eSchristos 
53399689912eSchristos 	dns_glue_t *glue = gluenode->glue;
53409689912eSchristos 	isc_statscounter_t counter = dns_gluecachestatscounter_hits_present;
53419689912eSchristos 
53429689912eSchristos 	if (glue != NULL) {
53439689912eSchristos 		/* We have a cached result. Add it to the message and return. */
53449689912eSchristos 		addglue_to_message(glue, msg);
53459689912eSchristos 	} else {
53469689912eSchristos 		counter = dns_gluecachestatscounter_hits_absent;
53479689912eSchristos 	}
53489689912eSchristos 
53499689912eSchristos 	rcu_read_unlock();
53509689912eSchristos 
53519689912eSchristos 	if (qpdb->gluecachestats != NULL) {
53529689912eSchristos 		isc_stats_increment(qpdb->gluecachestats, counter);
53539689912eSchristos 	}
53549689912eSchristos }
53559689912eSchristos 
53569689912eSchristos static void
53579689912eSchristos setmaxrrperset(dns_db_t *db, uint32_t value) {
53589689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)db;
53599689912eSchristos 
53609689912eSchristos 	REQUIRE(VALID_QPZONE(qpdb));
53619689912eSchristos 
53629689912eSchristos 	qpdb->maxrrperset = value;
53639689912eSchristos }
53649689912eSchristos 
53659689912eSchristos static void
53669689912eSchristos setmaxtypepername(dns_db_t *db, uint32_t value) {
53679689912eSchristos 	qpzonedb_t *qpdb = (qpzonedb_t *)db;
53689689912eSchristos 
53699689912eSchristos 	REQUIRE(VALID_QPZONE(qpdb));
53709689912eSchristos 
53719689912eSchristos 	qpdb->maxtypepername = value;
53729689912eSchristos }
53739689912eSchristos 
53749689912eSchristos static dns_dbmethods_t qpdb_zonemethods = {
53759689912eSchristos 	.destroy = qpdb_destroy,
53769689912eSchristos 	.beginload = beginload,
53779689912eSchristos 	.endload = endload,
53789689912eSchristos 	.currentversion = currentversion,
53799689912eSchristos 	.newversion = newversion,
53809689912eSchristos 	.attachversion = attachversion,
53819689912eSchristos 	.closeversion = closeversion,
53829689912eSchristos 	.findnode = findnode,
53839689912eSchristos 	.find = find,
53849689912eSchristos 	.attachnode = attachnode,
53859689912eSchristos 	.detachnode = detachnode,
53869689912eSchristos 	.createiterator = createiterator,
53879689912eSchristos 	.findrdataset = findrdataset,
53889689912eSchristos 	.allrdatasets = allrdatasets,
53899689912eSchristos 	.addrdataset = addrdataset,
53909689912eSchristos 	.subtractrdataset = subtractrdataset,
53919689912eSchristos 	.deleterdataset = deleterdataset,
53929689912eSchristos 	.issecure = issecure,
53939689912eSchristos 	.nodecount = nodecount,
53949689912eSchristos 	.setloop = setloop,
53959689912eSchristos 	.getoriginnode = getoriginnode,
53969689912eSchristos 	.getnsec3parameters = getnsec3parameters,
53979689912eSchristos 	.findnsec3node = findnsec3node,
53989689912eSchristos 	.setsigningtime = setsigningtime,
53999689912eSchristos 	.getsigningtime = getsigningtime,
54009689912eSchristos 	.getsize = getsize,
54019689912eSchristos 	.setgluecachestats = setgluecachestats,
54029689912eSchristos 	.locknode = locknode,
54039689912eSchristos 	.unlocknode = unlocknode,
54049689912eSchristos 	.addglue = addglue,
54059689912eSchristos 	.deletedata = deletedata,
54069689912eSchristos 	.nodefullname = nodefullname,
54079689912eSchristos 	.setmaxrrperset = setmaxrrperset,
54089689912eSchristos 	.setmaxtypepername = setmaxtypepername,
54099689912eSchristos };
54109689912eSchristos 
54119689912eSchristos static void
54129689912eSchristos destroy_qpznode(qpznode_t *node) {
54139689912eSchristos 	dns_slabheader_t *current = NULL, *next = NULL;
54149689912eSchristos 
54159689912eSchristos 	for (current = node->data; current != NULL; current = next) {
54169689912eSchristos 		dns_slabheader_t *down = current->down, *down_next = NULL;
54179689912eSchristos 
54189689912eSchristos 		next = current->next;
54199689912eSchristos 
54209689912eSchristos 		for (down = current->down; down != NULL; down = down_next) {
54219689912eSchristos 			down_next = down->down;
54229689912eSchristos 			dns_slabheader_destroy(&down);
54239689912eSchristos 		}
54249689912eSchristos 
54259689912eSchristos 		dns_slabheader_destroy(&current);
54269689912eSchristos 	}
54279689912eSchristos 
54289689912eSchristos 	dns_name_free(&node->name, node->mctx);
54299689912eSchristos 	isc_mem_putanddetach(&node->mctx, node, sizeof(qpznode_t));
54309689912eSchristos }
54319689912eSchristos 
54329689912eSchristos #if DNS_DB_NODETRACE
54339689912eSchristos ISC_REFCOUNT_STATIC_TRACE_IMPL(qpznode, destroy_qpznode);
54349689912eSchristos #else
54359689912eSchristos ISC_REFCOUNT_STATIC_IMPL(qpznode, destroy_qpznode);
54369689912eSchristos #endif
54379689912eSchristos 
54389689912eSchristos static void
54399689912eSchristos qp_attach(void *uctx ISC_ATTR_UNUSED, void *pval,
54409689912eSchristos 	  uint32_t ival ISC_ATTR_UNUSED) {
54419689912eSchristos 	qpznode_t *data = pval;
54429689912eSchristos 	qpznode_ref(data);
54439689912eSchristos }
54449689912eSchristos 
54459689912eSchristos static void
54469689912eSchristos qp_detach(void *uctx ISC_ATTR_UNUSED, void *pval,
54479689912eSchristos 	  uint32_t ival ISC_ATTR_UNUSED) {
54489689912eSchristos 	qpznode_t *data = pval;
54499689912eSchristos 	qpznode_detach(&data);
54509689912eSchristos }
54519689912eSchristos 
54529689912eSchristos static size_t
54539689912eSchristos qp_makekey(dns_qpkey_t key, void *uctx ISC_ATTR_UNUSED, void *pval,
54549689912eSchristos 	   uint32_t ival ISC_ATTR_UNUSED) {
54559689912eSchristos 	qpznode_t *data = pval;
54569689912eSchristos 	return dns_qpkey_fromname(key, &data->name);
54579689912eSchristos }
54589689912eSchristos 
54599689912eSchristos static void
54609689912eSchristos qp_triename(void *uctx ISC_ATTR_UNUSED, char *buf, size_t size) {
54619689912eSchristos 	snprintf(buf, size, "QPDB");
54629689912eSchristos }
5463