From 34b3e7cb4048b9effdc3ba74b81c8c2b69ecd0bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Sun, 3 Nov 2024 10:22:29 +0100 Subject: [PATCH] Remove RBTDB implementation QPDB is now a default implementation for both cache and zone. Remove the venerable RBTDB database implementation, so we can fast-track the changes to the database without having to implement the design changes to both QPDB and RBTDB and this allows us to be more aggressive when refactoring the database design. --- .gitlab-ci.yml | 2 +- bin/named/server.c | 1 - bin/tests/system/dyndb/driver/db.c | 95 +- configure.ac | 32 +- doc/design/red-black | 260 -- lib/dns/Makefile.am | 6 - lib/dns/db.c | 9 - lib/dns/db_p.h | 1 - lib/dns/ecs.c | 1 - lib/dns/include/dns/rbt.h | 853 ----- lib/dns/qpcache_p.h | 1 - lib/dns/rbt-cachedb.c | 1732 ---------- lib/dns/rbt-zonedb.c | 2582 -------------- lib/dns/rbt.c | 2943 ---------------- lib/dns/rbtdb.c | 5000 ---------------------------- lib/dns/rbtdb_p.h | 511 --- lib/dns/resolver.c | 1 - lib/dns/view.c | 1 - lib/isccfg/check.c | 1 - lib/ns/query.c | 1 - tests/bench/load-names.c | 75 +- tests/bench/qp-dump.c | 1 - tests/bench/qplookups.c | 1 - tests/dns/Makefile.am | 1 - tests/dns/qpdb_test.c | 1 - tests/dns/rbt_test.c | 1222 ------- 26 files changed, 52 insertions(+), 15282 deletions(-) delete mode 100644 doc/design/red-black delete mode 100644 lib/dns/include/dns/rbt.h delete mode 100644 lib/dns/rbt-cachedb.c delete mode 100644 lib/dns/rbt-zonedb.c delete mode 100644 lib/dns/rbt.c delete mode 100644 lib/dns/rbtdb.c delete mode 100644 lib/dns/rbtdb_p.h delete mode 100644 tests/dns/rbt_test.c diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f580f1ddc2..26ff4026cd 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -920,7 +920,7 @@ gcc:bookworm:rbt:amd64: variables: CC: gcc CFLAGS: "${CFLAGS_COMMON}" - EXTRA_CONFIGURE: "--with-libidn2 --with-zonedb=rbt --with-cachedb=rbt" + EXTRA_CONFIGURE: "--with-libidn2" <<: *debian_bookworm_amd64_image <<: *build_job diff --git a/bin/named/server.c b/bin/named/server.c index 9ec95d6da6..104c88ad42 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -89,7 +89,6 @@ #include #include #include -#include #include #include #include diff --git a/bin/tests/system/dyndb/driver/db.c b/bin/tests/system/dyndb/driver/db.c index de26821b2c..7fe469b8b0 100644 --- a/bin/tests/system/dyndb/driver/db.c +++ b/bin/tests/system/dyndb/driver/db.c @@ -33,8 +33,8 @@ * dns_db_*() calls on database instances backed by this driver use * struct sampledb_methods to find appropriate function implementation. * - * This example re-uses RBT DB implementation from original BIND and blindly - * proxies most of dns_db_*() calls to this underlying RBT DB. + * This example re-uses DB implementation from original BIND and blindly + * proxies most of dns_db_*() calls to this underlying DB. * See struct sampledb below. */ @@ -48,7 +48,6 @@ #include #include #include -#include #include #include #include @@ -67,11 +66,11 @@ struct sampledb { sample_instance_t *inst; /* - * Internal RBT database implementation provided by BIND. + * Internal database implementation provided by BIND. * Most dns_db_* calls (find(), createiterator(), etc.) * are blindly forwarded to this RBT DB. */ - dns_db_t *rbtdb; + dns_db_t *db; }; typedef struct sampledb sampledb_t; @@ -82,7 +81,7 @@ destroy(dns_db_t *db) { REQUIRE(VALID_SAMPLEDB(sampledb)); - dns_db_detach(&sampledb->rbtdb); + dns_db_detach(&sampledb->db); dns_name_free(&sampledb->common.origin, sampledb->common.mctx); isc_mem_putanddetach(&sampledb->common.mctx, sampledb, sizeof(*sampledb)); @@ -94,7 +93,7 @@ currentversion(dns_db_t *db, dns_dbversion_t **versionp) { REQUIRE(VALID_SAMPLEDB(sampledb)); - dns_db_currentversion(sampledb->rbtdb, versionp); + dns_db_currentversion(sampledb->db, versionp); } static isc_result_t @@ -103,7 +102,7 @@ newversion(dns_db_t *db, dns_dbversion_t **versionp) { REQUIRE(VALID_SAMPLEDB(sampledb)); - return (dns_db_newversion(sampledb->rbtdb, versionp)); + return (dns_db_newversion(sampledb->db, versionp)); } static void @@ -113,7 +112,7 @@ attachversion(dns_db_t *db, dns_dbversion_t *source, REQUIRE(VALID_SAMPLEDB(sampledb)); - dns_db_attachversion(sampledb->rbtdb, source, targetp); + dns_db_attachversion(sampledb->db, source, targetp); } static void @@ -123,8 +122,7 @@ closeversion(dns_db_t *db, dns_dbversion_t **versionp, REQUIRE(VALID_SAMPLEDB(sampledb)); - dns__db_closeversion(sampledb->rbtdb, versionp, - commit DNS__DB_FLARG_PASS); + dns__db_closeversion(sampledb->db, versionp, commit DNS__DB_FLARG_PASS); } static isc_result_t @@ -134,7 +132,7 @@ findnode(dns_db_t *db, const dns_name_t *name, bool create, REQUIRE(VALID_SAMPLEDB(sampledb)); - return (dns__db_findnode(sampledb->rbtdb, name, create, + return (dns__db_findnode(sampledb->db, name, create, nodep DNS__DB_FLARG_PASS)); } @@ -147,7 +145,7 @@ find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version, REQUIRE(VALID_SAMPLEDB(sampledb)); - return (dns__db_find(sampledb->rbtdb, name, version, type, options, now, + return (dns__db_find(sampledb->db, name, version, type, options, now, nodep, foundname, rdataset, sigrdataset DNS__DB_FLARG_PASS)); } @@ -161,7 +159,7 @@ findzonecut(dns_db_t *db, const dns_name_t *name, unsigned int options, REQUIRE(VALID_SAMPLEDB(sampledb)); - return (dns__db_findzonecut(sampledb->rbtdb, name, options, now, nodep, + return (dns__db_findzonecut(sampledb->db, name, options, now, nodep, foundname, dcname, rdataset, sigrdataset DNS__DB_FLARG_PASS)); } @@ -173,7 +171,7 @@ attachnode(dns_db_t *db, dns_dbnode_t *source, REQUIRE(VALID_SAMPLEDB(sampledb)); - dns__db_attachnode(sampledb->rbtdb, source, targetp DNS__DB_FLARG_PASS); + dns__db_attachnode(sampledb->db, source, targetp DNS__DB_FLARG_PASS); } static void @@ -182,7 +180,7 @@ detachnode(dns_db_t *db, dns_dbnode_t **targetp DNS__DB_FLARG) { REQUIRE(VALID_SAMPLEDB(sampledb)); - dns__db_detachnode(sampledb->rbtdb, targetp DNS__DB_FLARG_PASS); + dns__db_detachnode(sampledb->db, targetp DNS__DB_FLARG_PASS); } static isc_result_t @@ -192,7 +190,7 @@ createiterator(dns_db_t *db, unsigned int options, REQUIRE(VALID_SAMPLEDB(sampledb)); - return (dns_db_createiterator(sampledb->rbtdb, options, iteratorp)); + return (dns_db_createiterator(sampledb->db, options, iteratorp)); } static isc_result_t @@ -204,8 +202,8 @@ findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, REQUIRE(VALID_SAMPLEDB(sampledb)); - return (dns__db_findrdataset(sampledb->rbtdb, node, version, type, - covers, now, rdataset, + return (dns__db_findrdataset(sampledb->db, node, version, type, covers, + now, rdataset, sigrdataset DNS__DB_FLARG_PASS)); } @@ -217,8 +215,8 @@ allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, REQUIRE(VALID_SAMPLEDB(sampledb)); - return (dns__db_allrdatasets(sampledb->rbtdb, node, version, options, - now, iteratorp DNS__DB_FLARG_PASS)); + return (dns__db_allrdatasets(sampledb->db, node, version, options, now, + iteratorp DNS__DB_FLARG_PASS)); } static isc_result_t @@ -232,12 +230,12 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, REQUIRE(VALID_SAMPLEDB(sampledb)); dns_fixedname_init(&name); - CHECK(dns__db_addrdataset(sampledb->rbtdb, node, version, now, rdataset, + CHECK(dns__db_addrdataset(sampledb->db, node, version, now, rdataset, options, addedrdataset DNS__DB_FLARG_PASS)); if (rdataset->type == dns_rdatatype_a || rdataset->type == dns_rdatatype_aaaa) { - CHECK(dns_db_nodefullname(sampledb->rbtdb, node, + CHECK(dns_db_nodefullname(sampledb->db, node, dns_fixedname_name(&name))); CHECK(syncptrs(sampledb->inst, dns_fixedname_name(&name), rdataset, DNS_DIFFOP_ADD)); @@ -258,8 +256,8 @@ subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, REQUIRE(VALID_SAMPLEDB(sampledb)); dns_fixedname_init(&name); - result = dns__db_subtractrdataset(sampledb->rbtdb, node, version, - rdataset, options, + result = dns__db_subtractrdataset(sampledb->db, node, version, rdataset, + options, newrdataset DNS__DB_FLARG_PASS); if (result != ISC_R_SUCCESS && result != DNS_R_NXRRSET) { goto cleanup; @@ -268,7 +266,7 @@ subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, if (rdataset->type == dns_rdatatype_a || rdataset->type == dns_rdatatype_aaaa) { - CHECK(dns_db_nodefullname(sampledb->rbtdb, node, + CHECK(dns_db_nodefullname(sampledb->db, node, dns_fixedname_name(&name))); CHECK(syncptrs(sampledb->inst, dns_fixedname_name(&name), rdataset, DNS_DIFFOP_DEL)); @@ -289,7 +287,7 @@ deleterdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, REQUIRE(VALID_SAMPLEDB(sampledb)); - return (dns__db_deleterdataset(sampledb->rbtdb, node, version, type, + return (dns__db_deleterdataset(sampledb->db, node, version, type, covers DNS__DB_FLARG_PASS)); } @@ -299,7 +297,7 @@ issecure(dns_db_t *db) { REQUIRE(VALID_SAMPLEDB(sampledb)); - return (dns_db_issecure(sampledb->rbtdb)); + return (dns_db_issecure(sampledb->db)); } static unsigned int @@ -308,7 +306,7 @@ nodecount(dns_db_t *db, dns_dbtree_t tree) { REQUIRE(VALID_SAMPLEDB(sampledb)); - return (dns_db_nodecount(sampledb->rbtdb, tree)); + return (dns_db_nodecount(sampledb->db, tree)); } static void @@ -317,7 +315,7 @@ setloop(dns_db_t *db, isc_loop_t *loop) { REQUIRE(VALID_SAMPLEDB(sampledb)); - dns_db_setloop(sampledb->rbtdb, loop); + dns_db_setloop(sampledb->db, loop); } static isc_result_t @@ -326,8 +324,7 @@ getoriginnode(dns_db_t *db, dns_dbnode_t **nodep DNS__DB_FLARG) { REQUIRE(VALID_SAMPLEDB(sampledb)); - return (dns__db_getoriginnode(sampledb->rbtdb, - nodep DNS__DB_FLARG_PASS)); + return (dns__db_getoriginnode(sampledb->db, nodep DNS__DB_FLARG_PASS)); } static isc_result_t @@ -338,7 +335,7 @@ getnsec3parameters(dns_db_t *db, dns_dbversion_t *version, dns_hash_t *hash, REQUIRE(VALID_SAMPLEDB(sampledb)); - return (dns_db_getnsec3parameters(sampledb->rbtdb, version, hash, flags, + return (dns_db_getnsec3parameters(sampledb->db, version, hash, flags, iterations, salt, salt_length)); } @@ -349,7 +346,7 @@ findnsec3node(dns_db_t *db, const dns_name_t *name, bool create, REQUIRE(VALID_SAMPLEDB(sampledb)); - return (dns__db_findnsec3node(sampledb->rbtdb, name, create, + return (dns__db_findnsec3node(sampledb->db, name, create, nodep DNS__DB_FLARG_PASS)); } @@ -359,7 +356,7 @@ setsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, isc_stdtime_t resign) { REQUIRE(VALID_SAMPLEDB(sampledb)); - return (dns_db_setsigningtime(sampledb->rbtdb, rdataset, resign)); + return (dns_db_setsigningtime(sampledb->db, rdataset, resign)); } static isc_result_t @@ -369,7 +366,7 @@ getsigningtime(dns_db_t *db, isc_stdtime_t *resign, dns_name_t *name, REQUIRE(VALID_SAMPLEDB(sampledb)); - return (dns_db_getsigningtime(sampledb->rbtdb, resign, name, type)); + return (dns_db_getsigningtime(sampledb->db, resign, name, type)); } static dns_stats_t * @@ -378,7 +375,7 @@ getrrsetstats(dns_db_t *db) { REQUIRE(VALID_SAMPLEDB(sampledb)); - return (dns_db_getrrsetstats(sampledb->rbtdb)); + return (dns_db_getrrsetstats(sampledb->db)); } static isc_result_t @@ -389,7 +386,7 @@ findnodeext(dns_db_t *db, const dns_name_t *name, bool create, REQUIRE(VALID_SAMPLEDB(sampledb)); - return (dns__db_findnodeext(sampledb->rbtdb, name, create, methods, + return (dns__db_findnodeext(sampledb->db, name, create, methods, clientinfo, nodep DNS__DB_FLARG_PASS)); } @@ -403,9 +400,9 @@ findext(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version, REQUIRE(VALID_SAMPLEDB(sampledb)); - return (dns__db_findext(sampledb->rbtdb, name, version, type, options, - now, nodep, foundname, methods, clientinfo, - rdataset, sigrdataset DNS__DB_FLARG_PASS)); + return (dns__db_findext(sampledb->db, name, version, type, options, now, + nodep, foundname, methods, clientinfo, rdataset, + sigrdataset DNS__DB_FLARG_PASS)); } static isc_result_t @@ -414,7 +411,7 @@ setcachestats(dns_db_t *db, isc_stats_t *stats) { REQUIRE(VALID_SAMPLEDB(sampledb)); - return (dns_db_setcachestats(sampledb->rbtdb, stats)); + return (dns_db_setcachestats(sampledb->db, stats)); } static isc_result_t @@ -423,7 +420,7 @@ nodefullname(dns_db_t *db, dns_dbnode_t *node, dns_name_t *name) { REQUIRE(VALID_SAMPLEDB(sampledb)); - return (dns_db_nodefullname(sampledb->rbtdb, node, name)); + return (dns_db_nodefullname(sampledb->db, node, name)); } /* @@ -622,14 +619,14 @@ create_db(isc_mem_t *mctx, const dns_name_t *origin, dns_dbtype_t type, /* Create internal instance of DB implementation from BIND. */ CHECK(dns_db_create(mctx, ZONEDB_DEFAULT, origin, dns_dbtype_zone, - dns_rdataclass_in, 0, NULL, &sampledb->rbtdb)); + dns_rdataclass_in, 0, NULL, &sampledb->db)); /* Create fake SOA, NS, and A records to make database loadable. */ - CHECK(dns_db_newversion(sampledb->rbtdb, &version)); - CHECK(add_soa(sampledb->rbtdb, version, origin, origin, origin)); - CHECK(add_ns(sampledb->rbtdb, version, origin, origin)); - CHECK(add_a(sampledb->rbtdb, version, origin, a_addr)); - dns_db_closeversion(sampledb->rbtdb, &version, true); + CHECK(dns_db_newversion(sampledb->db, &version)); + CHECK(add_soa(sampledb->db, version, origin, origin, origin)); + CHECK(add_ns(sampledb->db, version, origin, origin)); + CHECK(add_a(sampledb->db, version, origin, a_addr)); + dns_db_closeversion(sampledb->db, &version, true); *dbp = (dns_db_t *)sampledb; diff --git a/configure.ac b/configure.ac index 4f786605c2..5602899ff6 100644 --- a/configure.ac +++ b/configure.ac @@ -171,7 +171,7 @@ AC_ARG_ENABLE([developer], AS_IF([test "$enable_developer" = "yes"], [DEVELOPER_MODE=yes - STD_CPPFLAGS="$STD_CPPFLAGS -DISC_MEM_DEFAULTFILL=1 -DISC_MEM_TRACKLINES=1 -DISC_LIST_CHECKINIT=1 -DISC_STATS_CHECKUNDERFLOW=1 -DDNS_RBTDB_STRONG_RWLOCK_CHECK=1 -DISC_MUTEX_ERROR_CHECK=1" + STD_CPPFLAGS="$STD_CPPFLAGS -DISC_MEM_DEFAULTFILL=1 -DISC_MEM_TRACKLINES=1 -DISC_LIST_CHECKINIT=1 -DISC_STATS_CHECKUNDERFLOW=1 -DISC_MUTEX_ERROR_CHECK=1" test "${enable_fixed_rrset+set}" = set || enable_fixed_rrset=yes test "${enable_querytrace+set}" = set || enable_querytrace=yes test "${with_cmocka+set}" = set || with_cmocka=yes @@ -1399,34 +1399,10 @@ AS_IF([test -z "$DTRACE"], AC_SUBST([DTRACE]) # -# Which should be the default zone database, RBTDB or QPZONE? -# [pairwise: --with-zonedb=qp, --with-zonedb=rbt] +# We only support QP zone and cache databases # -AC_ARG_WITH([zonedb], - [AS_HELP_STRING([--with-zonedb=detect],[specify default zone database type (default is "qpzone")])], - [],[with_zonedb=qp]) -zonedb="qpzone" -AS_CASE([$with_zonedb], - [RBT*|rbt*],[zonedb="rbt"], - [QP|qp],[], - [AC_MSG_ERROR([Unknown zone database type])] - ) -AC_DEFINE_UNQUOTED([ZONEDB_DEFAULT], ["$zonedb"], [Default zone database type]) - -# -# Which should be the default zone database, RBTDB or QPCACHE? -# [pairwise: --with-cachedb=qp, --with-cachedb=rbt] -# -AC_ARG_WITH([cachedb], - [AS_HELP_STRING([--with-cachedb=detect],[specify default cache database type (default is "qpcache")])], - [],[with_cachedb=qp]) -cachedb="qpcache" -AS_CASE([$with_cachedb], - [RBT*|rbt*],[cachedb="rbt"], - [QP*|qp*],[], - [AC_MSG_ERROR([Unknown cache database type])] - ) -AC_DEFINE_UNQUOTED([CACHEDB_DEFAULT], ["$cachedb"], [Default cache database type]) +AC_DEFINE_UNQUOTED([ZONEDB_DEFAULT], ["qpzone"], [Default zone database type]) +AC_DEFINE_UNQUOTED([CACHEDB_DEFAULT], ["qpcache"], [Default cache database type]) # # Files to configure. These are listed here because we used to diff --git a/doc/design/red-black b/doc/design/red-black deleted file mode 100644 index 86e2973506..0000000000 --- a/doc/design/red-black +++ /dev/null @@ -1,260 +0,0 @@ - - - Red-Black Tree Implementation Notes - -OVERVIEW - -BIND9's basic name storage mechanism is to use a modified form of -balanced binary tree known as a red-black tree. Red-black trees -provide for relatively efficient storage, retrieval and removal of -data while maintaining the lexical order of all stored keys, a -necessary function for DNS security. - -DESCRIPTION - -A red-black tree is a balanced binary tree named for the coloring that -is done in the tree, identifying each node as either red or black. -There are two simple rules for maintaining the color of nodes: - (1) A red node has only black children. - (2) The path from the root to any leaf node always includes the - same number of black nodes. - -Whenever a key is added or removed, adjustments are made to adhere to -those two rules. These adjustments are relatively cheap to make but -maintain the balance of the tree, thus making for efficient addition, -lookup and deletion operations, all of which are O(log N). The color -of a node is not relevant to external users of the tree; it is needed -only to maintain the balance of the tree. - -For more information on basic red-black trees, see _Introduction to -Algorithms_, Cormen, Leiserson, and Rivest, MIT Press / McGraw Hill, -1990, ISBN 0-262-03141-8, chapter 14. - -In BIND9, the red-black tree implementation uses DNS names as keys, -and can store arbitrary data with each key value. "name" and "key" -are used interchangeably in this document. - -The basic red-black tree algorithm is further adapted for use in BIND9 -to incorporate the notion of hierarchy, creating a tree of red-black -trees. Where there is more than one name with a common suffix, all -names with that suffix are stored in their own red-black tree, with a -down pointer from the suffix locating the subtree. - -For example, consider storing the following names: - a x.d.e.f o.w.y.d.e.f - b z.d.e.f p.w.y.d.e.f - c g.h q.w.y.d.e.f - -No matter which order the keys were added, this would result in a tree -that can be visualized as: - - b - / \ - a d.e.f - /|\ - c | g.h - | - w.y - /|\ - x | z - | - p - / \ - o q - -This tree shows that when there is no key for a particular label, and -when there is only one known label for its immediate subordinate, then -multiple labels can appear in a single node, such as at d.e.f and g.h. -It also demonstrates that there can be more nodes in the tree of trees -than there are actual keys (which degrades the O(log N) performance -marginally); the nodes at d.e.f and w.y do not represent keys. - -As an aside, remember that when ordering DNS names, labels are -examined from the right, therefore w.y sorts after x and before z. - -A split can occur not only on a regular label boundary, but also -between any two bits in an EDNS bitstring label. The common-suffix -rules will be applied to keep as many bits together as possible. - -In the current implementation of the tree of trees, a node is -considered to "formally" exist only if it has data associated with -it. So if the above tree then had the key d.e.f added to it, the -operation would succeed rather than getting an "already exists" -error. - -Along the same lines, if a key is added with a name which is a proper -superdomain of the name stored in an existing node, the operation will -succeed by splitting the existing node into one node that is the key -and another node that is the remaining parts of the name. Adding e.f -to the above tree results in the top level red-black tree having a -node named e.f where the current d.e.f is, and a down pointer from -d.e.f to a "tree" of a single node named d. The down pointer from d -would be kept to the level which has x, w.y, and z. - -A similar split of d.e.f would occur if the name k.e.f were added. -The top level tree would have the node e.f with a down pointer to a -level that had both d and k, and d would continue to have its down -pointer to the x, w.y and z level. - -It is guaranteed when splitting that external references to the node -that is split will remain valid --- in the previous examples, anything -that was pointing to the node that was d.e.f will still point to the -node that is now just d. - -When deleting keys, nodes can be rejoined. If both of p.w.y.d.e.f and -q.w.y.d.e.f were removed from the example tree, the node named w.y -would become o.w.y. Unlike splitting, it is _not_ guaranteed that -external references remain consistent; sometimes they will, sometimes -they won't. Also, note that deletion is not perfectly symmetric with -addition. If you "undo" the last addition with a deletion of the same -key then the tree of trees is not guaranteed to have exactly the same -structure as it had prior to the addition. Sometimes, but not always. - -Rejoining does not happen if it would violate any of the rules that -cause a split. o would not be rejoined with w.y if w.y had data -associated with the key; o would remain as a single node on its own -level. This emphasizes the rule that a node is considered to formally -exist only if data is associated with it, because even if w.y.d.e.f -had been explicitly added as a key but with no data, then o would -still be merged with the w.y node when p and q were deleted. - -Searching for a node generally returns one of three possible results: -either the key is found, a superdomain (partial match) of the key is -found, or no part of the key is found. The first and last are rather -obvious, and the second result basically means that a hierarchically -enclosing name is found; e.g, searching for bb.rc.vix.com turned up -rc.vix.com, but not the full name. - -No locking is done within the RBT library. @@@ - -CHAINS - -@@@ - -When a partial match is made, level_matches is set while the chain -points to the partial match node that was found. Then the chain is -adjusted to point to the DNSSEC predecessor node, which might not even -be under the same top level domain as the name that was searched for. -For example, consider a database that had only the names vix.com and -isc.org. A search for uu.net would leave the chain pointed to -vix.com, the DNSSEC predecessor. Though this might first appear to -cause level_matches to be bogus because the chain has been unwound and -sent down another path, note that the partial match node will always -be in the chain of the predecessor, too --- and often the partial -match node will be the predecessor itself. In the vix.com/isc.org -example, the search for uu.net finds a partial match at ".", which is -of course also in the path to the vix.com predecessor. A search for -www.isc.org would find that isc.org is both the partial match and the -predecessor. - -EXTERNAL PROGRAMMATIC DETAILS - -This section details the functions used to interact with the BIND9 -red-black tree library, or RBT for short. - -A source file that will be using RBT will usually need to include -. This header file automatically includes , , and . - -The rbt.h file has more complete descriptions of each of the functions -named here, including what is required for each argument, what each -function ensures (and might not ensure) will occur, and the full range -of possible results for each call. Note well: if a function returns a -dns_result_t rather than void, it definitely means there is something -that can go possibly wrong in the function and it should be checked by -the caller. - -A new tree of trees must be initialized using: - - dns_result_t dns_rbt_create(isc_mem_t *mctx, void (*deleter)(void *, void *), - void *deleter_arg, dns_rbt_t **rbtp); - -The memory context, mctx, must be a non-null pointer that was -initialized with isc_mem_create(). The deleter argument, if non-null, -should point to a function that is responsible for cleaning up any -memory associated with the data pointer of a node when the node is -deleted. It is passed the deleted node's data pointer as its first -argument and deleter_arg as its second argument. - -After initializing an RBT manager, to add keys to the tree, use: - - dns_result_t dns_rbt_addname(dns_rbt_t *rbt, dns_name_t *name, void *data); - -The name _must_ be an absolute name. It is not required that the data -pointer be non-null, but it is recommended that it point to something, -even just invalid memory, because of the various searching and -deletion issues described in the previous section. The RBT code will -not attempt to dereference the pointer. - -To find a key in the tree, use: - - dns_result_t dns_rbt_findname(dns_rbt_t *rbt, dns_name_t *name, void **data); - -The data parameter must not be NULL, but *data must be NULL. The -result will be either DNS_R_SUCCESS, DNS_R_PARTIALMATCH or -DNS_R_NOTFOUND. In the first case, an exact match was found for the -name and there was an associate data pointer, which is returned via -the data parameter. A partial match results when the name has not -been found but a superdomain name, with data, does exist; then the -data for that name is returned in the data parameter. If no data is -found for the name or a superdomain, *data will remain NULL. - - -INTERNAL PROGRAMMATIC DETAILS - -This section is mainly relevant to the RBT DB implementation. It is -highly recommended that programmers using the RBT library stick to the -functions named in the previous section. - -The dns_rbt_addname and dns_rbt_findname functions named in the -previous section are wrappers around dns_rbt_addnode and -dns_rbt_findnode. The *node functions for the most part do not -particularly care whether a node has an associated data pointer or -not, whereas the *name functions do. The one exception to this is -that when a PARTIALMATCH is returned for a search, the indicated node -is the deepest match that has data, rather than just the deepest -match. Even that behavior is selectable, however, using the boolean -empty_data_ok argument to dns_rbt_findnode. - -Each node in the tree of trees is represented by the following structure: - - typedef struct dns_rbtnode { - struct dns_rbtnode *left; - struct dns_rbtnode *right; - struct dns_rbtnode *down; - /* - * The following bitfields add up to a total bitwidth of 32. - * The range of values necessary for each item is indicated, - * but in the case of "attributes" the field is wider to accommodate - * possible future expansion. "offsetlen" could be one bit - * narrower by always adjusting its value by 1 to find the real - * offsetlen, but doing so does not gain anything (except perhaps - * another bit for "attributes", which doesn't yet need any more). - */ - unsigned int color:1; /* range is 0..1 */ - unsigned int attributes:6; /* range is 0..2 */ - unsigned int namelen:8; /* range is 1..255 */ - unsigned int offsetlen:8; /* range is 1..128 */ - unsigned int padbytes:9; /* range is 0..380 */ - /* - * These values are used in the RBT DB implementation. The - * appropriate node lock must be held before accessing them. - */ - void *data; - unsigned int dirty:1; - unsigned int locknum:DNS_RBT_LOCKLENGTH; - unsigned int references:DNS_RBT_REFLENGTH; - } dns_rbtnode_t; - -@@@ diff --git a/lib/dns/Makefile.am b/lib/dns/Makefile.am index 640108129a..d287dc5503 100644 --- a/lib/dns/Makefile.am +++ b/lib/dns/Makefile.am @@ -99,7 +99,6 @@ libdns_la_HEADERS = \ include/dns/peer.h \ include/dns/private.h \ include/dns/qp.h \ - include/dns/rbt.h \ include/dns/rcode.h \ include/dns/rdata.h \ include/dns/rdataclass.h \ @@ -213,11 +212,6 @@ libdns_la_SOURCES = \ qp_p.h \ qpzone_p.h \ qpzone.c \ - rbt.c \ - rbt-cachedb.c \ - rbt-zonedb.c \ - rbtdb.c \ - rbtdb_p.h \ qpcache.c \ qpcache_p.h \ rcode.c \ diff --git a/lib/dns/db.c b/lib/dns/db.c index bc65ec3292..526b3ecbfc 100644 --- a/lib/dns/db.c +++ b/lib/dns/db.c @@ -62,7 +62,6 @@ struct dns_dbimplementation { #include "db_p.h" #include "qpcache_p.h" #include "qpzone_p.h" -#include "rbtdb_p.h" unsigned int dns_pps = 0U; @@ -70,7 +69,6 @@ static ISC_LIST(dns_dbimplementation_t) implementations; static isc_rwlock_t implock; static isc_once_t once = ISC_ONCE_INIT; -static dns_dbimplementation_t rbtimp; static dns_dbimplementation_t qpimp; static dns_dbimplementation_t qpzoneimp; @@ -80,12 +78,6 @@ initialize(void) { ISC_LIST_INIT(implementations); - rbtimp = (dns_dbimplementation_t){ - .name = "rbt", - .create = dns__rbtdb_create, - .link = ISC_LINK_INITIALIZER, - }; - qpimp = (dns_dbimplementation_t){ .name = "qpcache", .create = dns__qpcache_create, @@ -98,7 +90,6 @@ initialize(void) { .link = ISC_LINK_INITIALIZER, }; - ISC_LIST_APPEND(implementations, &rbtimp, link); ISC_LIST_APPEND(implementations, &qpimp, link); ISC_LIST_APPEND(implementations, &qpzoneimp, link); } diff --git a/lib/dns/db_p.h b/lib/dns/db_p.h index f566f90442..275fc422c3 100644 --- a/lib/dns/db_p.h +++ b/lib/dns/db_p.h @@ -18,7 +18,6 @@ #include #include -#include #include #define GLUETABLE_INIT_SIZE 1 << 2 diff --git a/lib/dns/ecs.c b/lib/dns/ecs.c index 19166c027f..88e5dadf9d 100644 --- a/lib/dns/ecs.c +++ b/lib/dns/ecs.c @@ -22,7 +22,6 @@ #include #include -#include #include #include #include diff --git a/lib/dns/include/dns/rbt.h b/lib/dns/include/dns/rbt.h deleted file mode 100644 index 22a92c484c..0000000000 --- a/lib/dns/include/dns/rbt.h +++ /dev/null @@ -1,853 +0,0 @@ -/* - * Copyright (C) Internet Systems Consortium, Inc. ("ISC") - * - * SPDX-License-Identifier: MPL-2.0 - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, you can obtain one at https://mozilla.org/MPL/2.0/. - * - * See the COPYRIGHT file distributed with this work for additional - * information regarding copyright ownership. - */ - -#pragma once - -/*! \file dns/rbt.h */ - -#include -#include - -#include -#include -#include -#include - -#include - -ISC_LANG_BEGINDECLS - -/*@{*/ -/*% - * Option values for dns_rbt_findnode(). - * These are used to form a bitmask. - */ -#define DNS_RBTFIND_NOOPTIONS 0x00 -#define DNS_RBTFIND_EMPTYDATA 0x01 -#define DNS_RBTFIND_NOEXACT 0x02 -#define DNS_RBTFIND_NOPREDECESSOR 0x04 -/*@}*/ - -#define DNS_RBT_USEMAGIC 1 - -#define DNS_RBT_LOCKLENGTH (sizeof(((dns_rbtnode_t *)0)->locknum) * 8) - -#define DNS_RBTNODE_MAGIC ISC_MAGIC('R', 'B', 'N', 'O') -#if DNS_RBT_USEMAGIC -#define DNS_RBTNODE_VALID(n) ISC_MAGIC_VALID(n, DNS_RBTNODE_MAGIC) -#else /* if DNS_RBT_USEMAGIC */ -#define DNS_RBTNODE_VALID(n) true -#endif /* if DNS_RBT_USEMAGIC */ - -/*% - * This is the structure that is used for each node in the red/black - * tree of trees. NOTE WELL: the implementation manages this as a variable - * length structure, with the actual wire-format name and other data - * appended to this structure. Allocating a contiguous block of memory for - * multiple dns_rbtnode structures will not work. - */ -struct dns_rbtnode { -#if DNS_RBT_USEMAGIC - unsigned int magic; -#endif /* if DNS_RBT_USEMAGIC */ - /*@{*/ - /*! - * The following bitfields add up to a total bitwidth of 32. - * The range of values necessary for each item is indicated. - * - * In each case below the "range" indicated is what's _necessary_ for - * the bitfield to hold, not what it actually _can_ hold. - * - * Note: Tree lock must be held before modifying these - * bit-fields. - * - * Note: The two "unsigned int :0;" unnamed bitfields on either - * side of the bitfields below are scaffolding that border the - * set of bitfields which are accessed after acquiring the tree - * lock. Please don't insert any other bitfield members between - * the unnamed bitfields unless they should also be accessed - * after acquiring the tree lock. - */ - unsigned int : 0; /* start of bitfields c/o tree lock */ - unsigned int is_root : 1; /*%< range is 0..1 */ - unsigned int color : 1; /*%< range is 0..1 */ - unsigned int find_callback : 1; /*%< range is 0..1 */ - bool absolute : 1; /*%< node with absolute DNS name */ - unsigned int nsec : 2; /*%< range is 0..3 */ - unsigned int namelen : 8; /*%< range is 1..255 */ - unsigned int offsetlen : 8; /*%< range is 1..128 */ - unsigned int oldnamelen : 8; /*%< range is 1..255 */ - unsigned int : 0; /* end of bitfields c/o tree lock */ - /*@}*/ - - /*% - * These are needed for hashing. The 'uppernode' points to the - * node's superdomain node in the parent subtree, so that it can - * be reached from a child that was found by a hash lookup. - */ - unsigned int hashval; - dns_rbtnode_t *uppernode; - dns_rbtnode_t *hashnext; - - dns_rbtnode_t *parent; - dns_rbtnode_t *left; - dns_rbtnode_t *right; - dns_rbtnode_t *down; - - /*% - * Used for LRU cache. This linked list is used to mark nodes which - * have no data any longer, but we cannot unlink at that exact moment - * because we did not or could not obtain a write lock on the tree. - */ - ISC_LINK(dns_rbtnode_t) deadlink; - - /*@{*/ - /*! - * These values are used in the RBT DB implementation. The appropriate - * node lock must be held before accessing them. - * - * Note: The two "unsigned int :0;" unnamed bitfields on either - * side of the bitfields below are scaffolding that border the - * set of bitfields which are accessed after acquiring the node - * lock. Please don't insert any other bitfield members between - * the unnamed bitfields unless they should also be accessed - * after acquiring the node lock. - * - * NOTE: Do not merge these fields into bitfields above, as - * they'll all be put in the same qword that could be accessed - * without the node lock as it shares the qword with other - * members. Leave these members here so that they occupy a - * separate region of memory. - */ - void *data; - uint8_t : 0; /* start of bitfields c/o node lock */ - uint8_t dirty : 1; - uint8_t wild : 1; - uint8_t : 0; /* end of bitfields c/o node lock */ - uint16_t locknum; /* note that this is not in the bitfield */ - isc_refcount_t references; - /*@}*/ -}; - -typedef isc_result_t (*dns_rbtfindcallback_t)(dns_rbtnode_t *node, - dns_name_t *name, - void *callback_arg DNS__DB_FLARG); - -typedef void (*dns_rbtdeleter_t)(void *, void *); - -/***** -***** Chain Info -*****/ - -/*! - * A chain is used to keep track of the sequence of nodes to reach any given - * node from the root of the tree. Originally nodes did not have parent - * pointers in them (for memory usage reasons) so there was no way to find - * the path back to the root from any given node. Now that nodes have parent - * pointers, chains might be going away in a future release, though the - * movement functionality would remain. - * - * Chains may be used to iterate over a tree of trees. After setting up the - * chain's structure using dns_rbtnodechain_init(), it needs to be initialized - * to point to the lexically first or lexically last node in the tree of trees - * using dns_rbtnodechain_first() or dns_rbtnodechain_last(), respectively. - * Calling dns_rbtnodechain_next() or dns_rbtnodechain_prev() then moves the - * chain over to the next or previous node, respectively. - * - * In any event, parent information, whether via parent pointers or chains, is - * necessary information for iterating through the tree or for basic internal - * tree maintenance issues (ie, the rotations that are done to rebalance the - * tree when a node is added). The obvious implication of this is that for a - * chain to remain valid, the tree has to be locked down against writes for the - * duration of the useful life of the chain, because additions or removals can - * change the path from the root to the node the chain has targeted. - * - * The dns_rbtnodechain_ functions _first, _last, _prev and _next all take - * dns_name_t parameters for the name and the origin, which can be NULL. If - * non-NULL, 'name' will end up pointing to the name data and offsets that are - * stored at the node (and thus it will be read-only), so it should be a - * regular dns_name_t that has been initialized with dns_name_init. When - * 'origin' is non-NULL, it will get the name of the origin stored in it, so it - * needs to have its own buffer space and offsets, which is most easily - * accomplished with a dns_fixedname_t. It is _not_ necessary to reinitialize - * either 'name' or 'origin' between calls to the chain functions. - * - * NOTE WELL: even though the name data at the root of the tree of trees will - * be absolute (typically just "."), it will will be made into a relative name - * with an origin of "." -- an empty name when the node is ".". This is - * because a common on operation on 'name' and 'origin' is to use - * dns_name_concatenate() on them to generate the complete name. An empty name - * can be detected when dns_name_countlabels == 0, and is printed by - * dns_name_totext()/dns_name_format() as "@", consistent with RFC1035's - * definition of "@" as the current origin. - * - * dns_rbtnodechain_current is similar to the _first, _last, _prev and _next - * functions but additionally can provide the node to which the chain points. - */ - -/*% - * The number of level blocks to allocate at a time, same as the maximum - * number of labels. Allocating space for 128 levels when the tree is - * almost never that deep is wasteful, but it's not clear that it matters, - * since the waste is only 1MB for 1000 concurrently active chains on a - * system with 64-bit pointers. - */ -#define DNS_RBT_LEVELBLOCK 127 - -typedef struct dns_rbtnodechain { - unsigned int magic; - /*% - * The terminal node of the chain. It is not in levels[]. - * This is ostensibly private ... but in a pinch it could be - * used tell that the chain points nowhere without needing to - * call dns_rbtnodechain_current(). - */ - dns_rbtnode_t *end; - /*% - * Currently the maximum number of levels is allocated directly in - * the structure, but future revisions of this code might have a - * static initial block with dynamic growth. - */ - dns_rbtnode_t *levels[DNS_RBT_LEVELBLOCK]; - /*% - * level_count indicates how deep the chain points into the - * tree of trees, and is the index into the levels[] array. - * Thus, levels[level_count - 1] is the last level node stored. - * A chain that points to the top level of the tree of trees has - * a level_count of 0, the first level has a level_count of 1, and - * so on. - */ - unsigned int level_count; - /*% - * level_matches tells how many levels matched above the node - * returned by dns_rbt_findnode(). A match (partial or exact) found - * in the first level thus results in level_matches being set to 1. - * This is used by the rbtdb to set the start point for a recursive - * search of superdomains until the RR it is looking for is found. - */ - unsigned int level_matches; -} dns_rbtnodechain_t; - -/***** -***** Public interfaces. -*****/ -isc_result_t -dns_rbt_create(isc_mem_t *mctx, dns_rbtdeleter_t deleter, void *deleter_arg, - dns_rbt_t **rbtp); -/*%< - * Initialize a red-black tree of trees. - * - * Notes: - *\li The deleter argument, if non-null, points to a function that is - * responsible for cleaning up any memory associated with the data - * pointer of a node when the node is deleted. It is passed the - * deleted node's data pointer as its first argument and deleter_arg - * as its second argument. - * - * Requires: - * \li mctx is a pointer to a valid memory context. - *\li rbtp != NULL && *rbtp == NULL - *\li arg == NULL iff deleter == NULL - * - * Ensures: - *\li If result is ISC_R_SUCCESS: - * *rbtp points to a valid red-black tree manager - * - *\li If result is failure: - * *rbtp does not point to a valid red-black tree manager. - * - * Returns: - *\li #ISC_R_SUCCESS Success - */ - -isc_result_t -dns_rbt_addnode(dns_rbt_t *rbt, const dns_name_t *name, dns_rbtnode_t **nodep); - -/*%< - * Add 'name' to the tree of trees. On success, return the address of - * the newly added node. If 'name' already existed, return ISC_R_EXISTS - * and the address of the pre-existing node. - * - * Requires: - *\li rbt is a valid rbt structure. - *\li dns_name_isabsolute(name) == TRUE - *\li nodep != NULL && *nodep == NULL - * - * Ensures: - *\li 'name' is not altered in any way. - * - *\li Any external references to nodes in the tree are unaffected by - * node splits that are necessary to insert the new name. - * - *\li If result is ISC_R_SUCCESS: - * 'name' is findable in the red/black tree of trees in O(log N). - * *nodep is the node that was added for 'name'. - * - *\li If result is ISC_R_EXISTS: - * The tree of trees is unaltered. - * *nodep is the existing node for 'name'. - * - * Returns: - *\li #ISC_R_SUCCESS Success - *\li #ISC_R_EXISTS The name already exists, possibly without data. - *\li #ISC_R_NOSPACE The name had more logical labels than are allowed. - */ - -#define dns_rbt_findnode(rbt, name, foundname, node, chain, options, callback, \ - callback_arg) \ - dns__rbt_findnode(rbt, name, foundname, node, chain, options, \ - callback, callback_arg DNS__DB_FILELINE) -isc_result_t -dns__rbt_findnode(dns_rbt_t *rbt, const dns_name_t *name, dns_name_t *foundname, - dns_rbtnode_t **node, dns_rbtnodechain_t *chain, - unsigned int options, dns_rbtfindcallback_t callback, - void *callback_arg DNS__DB_FLARG); -/*%< - * Find the node for 'name'. - * - * Notes: - *\li A node that has no data is considered not to exist for this function, - * unless the DNS_RBTFIND_EMPTYDATA option is set. This applies to both - * exact matches and partial matches. - * - *\li If the chain parameter is non-NULL, then the path through the tree - * to the DNSSEC predecessor of the searched for name is maintained, - * unless the DNS_RBTFIND_NOPREDECESSOR or DNS_RBTFIND_NOEXACT option - * is used. (For more details on those options, see below.) - * - *\li If there is no predecessor, then the chain will point to nowhere, as - * indicated by chain->end being NULL or dns_rbtnodechain_current - * returning ISC_R_NOTFOUND. Note that in a normal Internet DNS RBT - * there will always be a predecessor for all names except the root - * name, because '.' will exist and '.' is the predecessor of - * everything. But you can certainly construct a trivial tree and a - * search for it that has no predecessor. - * - *\li Within the chain structure, the 'levels' member of the structure holds - * the root node of each level except the first. - * - *\li The 'level_count' of the chain indicates how deep the chain to the - * predecessor name is, as an index into the 'levels[]' array. It does - * not count name elements, per se, but only levels of the tree of trees, - * the distinction arising because multiple labels from a name can be - * stored on only one level. It is also does not include the level - * that has the node, since that level is not stored in levels[]. - * - *\li The chain's 'level_matches' is not directly related to the predecessor. - * It is the number of levels above the level of the found 'node', - * regardless of whether it was a partial match or exact match. When - * the node is found in the top level tree, or no node is found at all, - * level_matches is 0. - * - *\li When DNS_RBTFIND_NOEXACT is set, the closest matching superdomain is - * returned (also subject to DNS_RBTFIND_EMPTYDATA), even when - * there is an exact match in the tree. In this case, the chain - * will not point to the DNSSEC predecessor, but will instead point - * to the exact match, if there was any. Thus the preceding paragraphs - * should have "exact match" substituted for "predecessor" to describe - * how the various elements of the chain are set. This was done to - * ensure that the chain's state was sane, and to prevent problems that - * occurred when running the predecessor location code under conditions - * it was not designed for. It is not clear *where* the chain should - * point when DNS_RBTFIND_NOEXACT is set, so if you end up using a chain - * with this option because you want a particular node, let us know - * where you want the chain pointed, so this can be made more firm. - * - * Requires: - *\li rbt is a valid rbt manager. - *\li dns_name_isabsolute(name) == TRUE. - *\li node != NULL && *node == NULL. - *\li #DNS_RBTFIND_NOEXACT and DNS_RBTFIND_NOPREDECESSOR are mutually - * exclusive. - * - * Ensures: - *\li 'name' and the tree are not altered in any way. - * - *\li If result is ISC_R_SUCCESS: - *\verbatim - * *node is the terminal node for 'name'. - * - * 'foundname' and 'name' represent the same name (though not - * the same memory). - * - * 'chain' points to the DNSSEC predecessor, if any, of 'name'. - * - * chain->level_matches and chain->level_count are equal. - *\endverbatim - * - * If result is DNS_R_PARTIALMATCH: - *\verbatim - * *node is the data associated with the deepest superdomain - * of 'name' which has data. - * - * 'foundname' is the name of deepest superdomain (which has - * data, unless the DNS_RBTFIND_EMPTYDATA option is set). - * - * 'chain' points to the DNSSEC predecessor, if any, of 'name'. - *\endverbatim - * - *\li If result is ISC_R_NOTFOUND: - *\verbatim - * Neither the name nor a superdomain was found. *node is NULL. - * - * 'chain' points to the DNSSEC predecessor, if any, of 'name'. - * - * chain->level_matches is 0. - *\endverbatim - * - * Returns: - *\li #ISC_R_SUCCESS Success - *\li #DNS_R_PARTIALMATCH Superdomain found with data - *\li #ISC_R_NOTFOUND No match, or superdomain with no data - *\li #ISC_R_NOSPACE Concatenating nodes to form foundname failed - */ - -isc_result_t -dns_rbt_deletenode(dns_rbt_t *rbt, dns_rbtnode_t *node, bool recurse); -/*%< - * Delete 'node' from the tree of trees. - * - * Notes: - *\li When 'node' is removed, if recurse is true then all nodes - * in levels down from it are removed too. - * - * Requires: - *\li rbt is a valid rbt manager. - *\li node != NULL. - * - * Ensures: - *\li Does NOT ensure that any external references to nodes in the tree - * are unaffected by node joins. - * - *\li If result is ISC_R_SUCCESS: - * 'node' does not appear in the tree with data; however, - * the node might still exist if it serves as a pointer to - * a lower tree level as long as 'recurse' was false, hence - * the node could can be found with dns_rbt_findnode when - * that function's empty_data_ok parameter is true. - * - *\li If result is ISC_R_NOSPACE: - * The node was deleted, but the tree structure was not - * optimized. - * - * Returns: - *\li #ISC_R_SUCCESS Success - *\li #ISC_R_NOSPACE dns_name_concatenate failed when joining nodes. - */ - -void -dns_rbt_namefromnode(dns_rbtnode_t *node, dns_name_t *name); -/*%< - * Convert the sequence of labels stored at 'node' into a 'name'. - * - * Notes: - *\li This function does not return the full name, from the root, but - * just the labels at the indicated node. - * - *\li The name data pointed to by 'name' is the information stored - * in the node, not a copy. Altering the data at this pointer - * will likely cause grief. - * - * Requires: - * \li name->offsets == NULL - * - * Ensures: - * \li 'name' is readonly. - * - * \li 'name' will point directly to the labels stored after the - * dns_rbtnode_t struct. - * - * \li 'name' will have offsets that also point to the information stored - * as part of the node. - */ - -isc_result_t -dns_rbt_fullnamefromnode(dns_rbtnode_t *node, dns_name_t *name); -/*%< - * Like dns_rbt_namefromnode, but returns the full name from the root. - * - * Notes: - * \li Unlike dns_rbt_namefromnode, the name will not point directly - * to node data. Rather, dns_name_concatenate will be used to copy - * the name data from each node into the 'name' argument. - * - * Requires: - * \li name != NULL - * \li name has a dedicated buffer. - * - * Returns: - * \li ISC_R_SUCCESS - * \li ISC_R_NOSPACE (possible via dns_name_concatenate) - * \li DNS_R_NAMETOOLONG (possible via dns_name_concatenate) - */ - -char * -dns_rbt_formatnodename(dns_rbtnode_t *node, char *printname, unsigned int size); -/*%< - * Format the full name of a node for printing, using dns_name_format(). - * - * Notes: - * \li 'size' is the length of the printname buffer. This should be - * DNS_NAME_FORMATSIZE or larger. - * - * Requires: - * \li node and printname are not NULL. - * - * Returns: - * \li The 'printname' pointer. - */ - -unsigned int -dns_rbt_nodecount(dns_rbt_t *rbt); -/*%< - * Obtain the number of nodes in the tree of trees. - * - * Requires: - * \li rbt is a valid rbt manager. - */ - -size_t -dns_rbt_hashsize(dns_rbt_t *rbt); -/*%< - * Obtain the current number of buckets in the 'rbt' hash table. - * - * Requires: - * \li rbt is a valid rbt manager. - */ - -isc_result_t -dns_rbt_destroy(dns_rbt_t **rbtp, unsigned int quantum); -/*%< - * Stop working with a red-black tree of trees. - * If 'quantum' is zero then the entire tree will be destroyed. - * If 'quantum' is non zero then up to 'quantum' nodes will be destroyed - * allowing the rbt to be incrementally destroyed by repeated calls to - * dns_rbt_destroy2(). Once dns_rbt_destroy2() has been called no other - * operations than dns_rbt_destroy()/dns_rbt_destroy2() should be - * performed on the tree of trees. - * - * Requires: - * \li *rbt is a valid rbt manager. - * - * Ensures on ISC_R_SUCCESS: - * \li All space allocated by the RBT library has been returned. - * - * \li *rbt is invalidated as an rbt manager. - * - * Returns: - * \li ISC_R_SUCCESS - * \li ISC_R_QUOTA if 'quantum' nodes have been destroyed. - */ - -void -dns_rbt_printtext(dns_rbt_t *rbt, void (*data_printer)(FILE *, void *), - FILE *f); -/*%< - * Print an ASCII representation of the internal structure of the red-black - * tree of trees to the passed stream. - * - * data_printer is a callback function that is called to print the data - * in a node. It should print it to the passed FILE stream. - * - * Notes: - * \li The name stored at each node, along with the node's color, is printed. - * Then the down pointer, left and right pointers are displayed - * recursively in turn. NULL down pointers are silently omitted; - * NULL left and right pointers are printed. - */ - -void -dns_rbt_printdot(dns_rbt_t *rbt, bool show_pointers, FILE *f); -/*%< - * Print a GraphViz dot representation of the internal structure of the - * red-black tree of trees to the passed stream. - * - * If show_pointers is TRUE, pointers are also included in the generated - * graph. - * - * Notes: - * \li The name stored at each node, along with the node's color is displayed. - * Then the down pointer, left and right pointers are displayed - * recursively in turn. NULL left, right and down pointers are - * silently omitted. - */ - -void -dns_rbt_printnodeinfo(dns_rbtnode_t *n, FILE *f); -/*%< - * Print out various information about a node - * - * Requires: - *\li 'n' is a valid pointer. - * - *\li 'f' points to a valid open FILE structure that allows writing. - */ - -size_t -dns__rbt_getheight(dns_rbt_t *rbt); -/*%< - * Return the maximum height of sub-root nodes found in the red-black - * forest. - * - * The height of a node is defined as the number of nodes in the longest - * path from the node to a leaf. For each subtree in the forest, this - * function determines the height of its root node. Then it returns the - * maximum such height in the forest. - * - * Note: This function exists for testing purposes. Non-test code must - * not use it. - * - * Requires: - * \li rbt is a valid rbt manager. - */ - -bool -dns__rbt_checkproperties(dns_rbt_t *rbt); -/*%< - * Check red-black properties of the forest. - * - * Note: This function exists for testing purposes. Non-test code must - * not use it. - * - * Requires: - * \li rbt is a valid rbt manager. - */ - -size_t -dns__rbtnode_getdistance(dns_rbtnode_t *node); -/*%< - * Return the distance (in nodes) from the node to its upper node of its - * subtree. The root node has a distance of 1. A child of the root node - * has a distance of 2. - */ - -/***** -***** Chain Functions -*****/ - -void -dns_rbtnodechain_init(dns_rbtnodechain_t *chain); -/*%< - * Initialize 'chain'. - * - * Requires: - *\li 'chain' is a valid pointer. - * - * Ensures: - *\li 'chain' is suitable for use. - */ - -void -dns_rbtnodechain_reset(dns_rbtnodechain_t *chain); -/*%< - * Free any dynamic storage associated with 'chain', and then reinitialize - * 'chain'. - * - * Requires: - *\li 'chain' is a valid pointer. - * - * Ensures: - *\li 'chain' is suitable for use, and uses no dynamic storage. - */ - -void -dns_rbtnodechain_invalidate(dns_rbtnodechain_t *chain); -/*%< - * Free any dynamic storage associated with 'chain', and then invalidates it. - * - * Notes: - *\li Future calls to any dns_rbtnodechain_ function will need to call - * dns_rbtnodechain_init on the chain first (except, of course, - * dns_rbtnodechain_init itself). - * - * Requires: - *\li 'chain' is a valid chain. - * - * Ensures: - *\li 'chain' is no longer suitable for use, and uses no dynamic storage. - */ - -isc_result_t -dns_rbtnodechain_current(dns_rbtnodechain_t *chain, dns_name_t *name, - dns_name_t *origin, dns_rbtnode_t **node); -/*%< - * Provide the name, origin and node to which the chain is currently pointed. - * - * Notes: - *\li The tree need not have be locked against additions for the chain - * to remain valid, however there are no guarantees if any deletion - * has been made since the chain was established. - * - * Requires: - *\li 'chain' is a valid chain. - * - * Ensures: - *\li 'node', if non-NULL, is the node to which the chain was pointed - * by dns_rbt_findnode, dns_rbtnodechain_first or dns_rbtnodechain_last. - * If none were called for the chain since it was initialized or reset, - * or if the was no predecessor to the name searched for with - * dns_rbt_findnode, then '*node' is NULL and ISC_R_NOTFOUND is returned. - * - *\li 'name', if non-NULL, is the name stored at the terminal level of - * the chain. This is typically a single label, like the "www" of - * "www.isc.org", but need not be so. At the root of the tree of trees, - * if the node is "." then 'name' is ".", otherwise it is relative to ".". - * (Minimalist and atypical case: if the tree has just the name - * "isc.org." then the root node's stored name is "isc.org." but 'name' - * will be "isc.org".) - * - *\li 'origin', if non-NULL, is the sequence of labels in the levels - * above the terminal level, such as "isc.org." in the above example. - * 'origin' is always "." for the root node. - * - * - * Returns: - *\li #ISC_R_SUCCESS name, origin & node were successfully set. - *\li #ISC_R_NOTFOUND The chain does not point to any node. - *\li <something_else> Any error return from dns_name_concatenate. - */ - -isc_result_t -dns_rbtnodechain_first(dns_rbtnodechain_t *chain, dns_rbt_t *rbt, - dns_name_t *name, dns_name_t *origin); -/*%< - * Set the chain to the lexically first node in the tree of trees. - * - * Notes: - *\li By the definition of ordering for DNS names, the root of the tree of - * trees is the very first node, since everything else in the megatree - * uses it as a common suffix. - * - * Requires: - *\li 'chain' is a valid chain. - *\li 'rbt' is a valid rbt manager. - * - * Ensures: - *\li The chain points to the very first node of the tree. - * - *\li 'name' and 'origin', if non-NULL, are set as described for - * dns_rbtnodechain_current. Thus 'origin' will always be ".". - * - * Returns: - *\li #DNS_R_NEWORIGIN The name & origin were successfully set. - *\li <something_else> Any error result from dns_rbtnodechain_current. - */ - -isc_result_t -dns_rbtnodechain_last(dns_rbtnodechain_t *chain, dns_rbt_t *rbt, - dns_name_t *name, dns_name_t *origin); -/*%< - * Set the chain to the lexically last node in the tree of trees. - * - * Requires: - *\li 'chain' is a valid chain. - *\li 'rbt' is a valid rbt manager. - * - * Ensures: - *\li The chain points to the very last node of the tree. - * - *\li 'name' and 'origin', if non-NULL, are set as described for - * dns_rbtnodechain_current. - * - * Returns: - *\li #DNS_R_NEWORIGIN The name & origin were successfully set. - *\li <something_else> Any error result from dns_name_concatenate. - */ - -isc_result_t -dns_rbtnodechain_prev(dns_rbtnodechain_t *chain, dns_name_t *name, - dns_name_t *origin); -/*%< - * Adjusts chain to point the DNSSEC predecessor of the name to which it - * is currently pointed. - * - * Requires: - *\li 'chain' is a valid chain. - *\li 'chain' has been pointed somewhere in the tree with dns_rbt_findnode, - * dns_rbtnodechain_first or dns_rbtnodechain_last -- and remember that - * dns_rbt_findnode is not guaranteed to point the chain somewhere, - * since there may have been no predecessor to the searched for name. - * - * Ensures: - *\li The chain is pointed to the predecessor of its current target. - * - *\li 'name' and 'origin', if non-NULL, are set as described for - * dns_rbtnodechain_current. - * - *\li 'origin' is only if a new origin was found. - * - * Returns: - *\li #ISC_R_SUCCESS The predecessor was found and 'name' was set. - *\li #DNS_R_NEWORIGIN The predecessor was found with a - * different origin and 'name' and 'origin' were set. \li #ISC_R_NOMORE There - * was no predecessor. \li <something_else> Any error result from - * dns_rbtnodechain_current. - */ - -isc_result_t -dns_rbtnodechain_next(dns_rbtnodechain_t *chain, dns_name_t *name, - dns_name_t *origin); -/*%< - * Adjusts chain to point the DNSSEC successor of the name to which it - * is currently pointed. - * - * Requires: - *\li 'chain' is a valid chain. - *\li 'chain' has been pointed somewhere in the tree with dns_rbt_findnode, - * dns_rbtnodechain_first or dns_rbtnodechain_last -- and remember that - * dns_rbt_findnode is not guaranteed to point the chain somewhere, - * since there may have been no predecessor to the searched for name. - * - * Ensures: - *\li The chain is pointed to the successor of its current target. - * - *\li 'name' and 'origin', if non-NULL, are set as described for - * dns_rbtnodechain_current. - * - *\li 'origin' is only if a new origin was found. - * - * Returns: - *\li #ISC_R_SUCCESS The successor was found and 'name' was set. - *\li #DNS_R_NEWORIGIN The successor was found with a different - * origin and 'name' and 'origin' were set. - *\li #ISC_R_NOMORE There was no successor. - *\li <something_else> Any error result from dns_name_concatenate. - */ - -isc_result_t -dns_rbtnodechain_down(dns_rbtnodechain_t *chain, dns_name_t *name, - dns_name_t *origin); -/*%< - * Descend down if possible. - */ - -isc_result_t -dns_rbtnodechain_nextflat(dns_rbtnodechain_t *chain, dns_name_t *name); -/*%< - * Find the next node at the current depth in DNSSEC order. - */ - -unsigned int -dns__rbtnode_namelen(dns_rbtnode_t *node); -/*%< - * Returns the length of the full name of the node. Used only internally - * and in unit tests. - */ - -unsigned int -dns__rbtnode_getsize(dns_rbtnode_t *node); -/* - * Return allocated size for a node. - */ - -ISC_LANG_ENDDECLS diff --git a/lib/dns/qpcache_p.h b/lib/dns/qpcache_p.h index 5992ba77c2..63ffc584cb 100644 --- a/lib/dns/qpcache_p.h +++ b/lib/dns/qpcache_p.h @@ -19,7 +19,6 @@ #include #include -#include #include /***** diff --git a/lib/dns/rbt-cachedb.c b/lib/dns/rbt-cachedb.c deleted file mode 100644 index f917ccfd39..0000000000 --- a/lib/dns/rbt-cachedb.c +++ /dev/null @@ -1,1732 +0,0 @@ -/* - * Copyright (C) Internet Systems Consortium, Inc. ("ISC") - * - * SPDX-License-Identifier: MPL-2.0 - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, you can obtain one at https://mozilla.org/MPL/2.0/. - * - * See the COPYRIGHT file distributed with this work for additional - * information regarding copyright ownership. - */ - -/*! \file */ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "db_p.h" -#include "rbtdb_p.h" - -#define CHECK(op) \ - do { \ - result = (op); \ - if (result != ISC_R_SUCCESS) \ - goto failure; \ - } while (0) - -/*% - * Whether to rate-limit updating the LRU to avoid possible thread contention. - * Updating LRU requires write locking, so we don't do it every time the - * record is touched - only after some time passes. - */ -#ifndef DNS_RBTDB_LIMITLRUUPDATE -#define DNS_RBTDB_LIMITLRUUPDATE 1 -#endif - -/*% Time after which we update LRU for glue records, 5 minutes */ -#define DNS_RBTDB_LRUUPDATE_GLUE 300 -/*% Time after which we update LRU for all other records, 10 minutes */ -#define DNS_RBTDB_LRUUPDATE_REGULAR 600 - -#define EXISTS(header) \ - ((atomic_load_acquire(&(header)->attributes) & \ - DNS_SLABHEADERATTR_NONEXISTENT) == 0) -#define NONEXISTENT(header) \ - ((atomic_load_acquire(&(header)->attributes) & \ - DNS_SLABHEADERATTR_NONEXISTENT) != 0) -#define NXDOMAIN(header) \ - ((atomic_load_acquire(&(header)->attributes) & \ - DNS_SLABHEADERATTR_NXDOMAIN) != 0) -#define STALE(header) \ - ((atomic_load_acquire(&(header)->attributes) & \ - DNS_SLABHEADERATTR_STALE) != 0) -#define NEGATIVE(header) \ - ((atomic_load_acquire(&(header)->attributes) & \ - DNS_SLABHEADERATTR_NEGATIVE) != 0) -#define ZEROTTL(header) \ - ((atomic_load_acquire(&(header)->attributes) & \ - DNS_SLABHEADERATTR_ZEROTTL) != 0) -#define ANCIENT(header) \ - ((atomic_load_acquire(&(header)->attributes) & \ - DNS_SLABHEADERATTR_ANCIENT) != 0) -#define STATCOUNT(header) \ - ((atomic_load_acquire(&(header)->attributes) & \ - DNS_SLABHEADERATTR_STATCOUNT) != 0) - -#define STALE_TTL(header, rbtdb) \ - (NXDOMAIN(header) ? 0 : rbtdb->common.serve_stale_ttl) - -#define ACTIVE(header, now) \ - (((header)->ttl > (now)) || ((header)->ttl == (now) && ZEROTTL(header))) - -#define KEEPSTALE(rbtdb) ((rbtdb)->common.serve_stale_ttl > 0) - -/*% - * Routines for LRU-based cache management. - */ - -/*% - * See if a given cache entry that is being reused needs to be updated - * in the LRU-list. From the LRU management point of view, this - * function is expected to return true for almost all cases. When used - * with threads, however, this may cause a non-negligible performance - * penalty because a writer lock will have to be acquired before - * updating the list. If DNS_RBTDB_LIMITLRUUPDATE is defined to be non 0 - * at compilation time, this function returns true if the entry has not - * been updated for some period of time. We differentiate the NS or - * glue address case and the others since experiments have shown that - * the former tends to be accessed relatively infrequently and the cost - * of cache miss is higher (e.g., a missing NS records may cause - * external queries at a higher level zone, involving more - * transactions). - * - * Caller must hold the node (read or write) lock. - */ -static bool -need_headerupdate(dns_slabheader_t *header, isc_stdtime_t now) { - if (DNS_SLABHEADER_GETATTR(header, (DNS_SLABHEADERATTR_NONEXISTENT | - DNS_SLABHEADERATTR_ANCIENT | - DNS_SLABHEADERATTR_ZEROTTL)) != 0) - { - return (false); - } - -#if DNS_RBTDB_LIMITLRUUPDATE - if (header->type == dns_rdatatype_ns || - (header->trust == dns_trust_glue && - (header->type == dns_rdatatype_a || - header->type == dns_rdatatype_aaaa))) - { - /* - * Glue records are updated if at least DNS_RBTDB_LRUUPDATE_GLUE - * seconds have passed since the previous update time. - */ - return (header->last_used + DNS_RBTDB_LRUUPDATE_GLUE <= now); - } - - /* - * Other records are updated if DNS_RBTDB_LRUUPDATE_REGULAR seconds - * have passed. - */ - return (header->last_used + DNS_RBTDB_LRUUPDATE_REGULAR <= now); -#else - UNUSED(now); - - return (true); -#endif /* if DNS_RBTDB_LIMITLRUUPDATE */ -} - -/*% - * Update the timestamp of a given cache entry and move it to the head - * of the corresponding LRU list. - * - * Caller must hold the node (write) lock. - * - * Note that the we do NOT touch the heap here, as the TTL has not changed. - */ -static void -update_header(dns_rbtdb_t *rbtdb, dns_slabheader_t *header, isc_stdtime_t now) { - INSIST(IS_CACHE(rbtdb)); - - /* To be checked: can we really assume this? XXXMLG */ - INSIST(ISC_LINK_LINKED(header, link)); - - ISC_LIST_UNLINK(rbtdb->lru[RBTDB_HEADERNODE(header)->locknum], header, - link); - header->last_used = now; - ISC_LIST_PREPEND(rbtdb->lru[RBTDB_HEADERNODE(header)->locknum], header, - link); -} - -/* - * Locking - * - * If a routine is going to lock more than one lock in this module, then - * the locking must be done in the following order: - * - * Tree Lock - * - * Node Lock (Only one from the set may be locked at one time by - * any caller) - * - * Database Lock - * - * Failure to follow this hierarchy can result in deadlock. - */ - -/* - * Deleting Nodes - * - * For zone databases the node for the origin of the zone MUST NOT be deleted. - */ - -/* - * DB Routines - */ - -static void -update_cachestats(dns_rbtdb_t *rbtdb, isc_result_t result) { - INSIST(IS_CACHE(rbtdb)); - - if (rbtdb->cachestats == NULL) { - return; - } - - switch (result) { - case DNS_R_COVERINGNSEC: - isc_stats_increment(rbtdb->cachestats, - dns_cachestatscounter_coveringnsec); - FALLTHROUGH; - case ISC_R_SUCCESS: - case DNS_R_CNAME: - case DNS_R_DNAME: - case DNS_R_DELEGATION: - case DNS_R_NCACHENXDOMAIN: - case DNS_R_NCACHENXRRSET: - isc_stats_increment(rbtdb->cachestats, - dns_cachestatscounter_hits); - break; - default: - isc_stats_increment(rbtdb->cachestats, - dns_cachestatscounter_misses); - } -} - -static void -clean_stale_headers(dns_slabheader_t *top) { - dns_slabheader_t *d = NULL, *down_next = NULL; - - for (d = top->down; d != NULL; d = down_next) { - down_next = d->down; - dns_slabheader_destroy(&d); - } - top->down = NULL; -} - -static isc_result_t -setup_delegation(rbtdb_search_t *search, dns_dbnode_t **nodep, - dns_name_t *foundname, dns_rdataset_t *rdataset, - dns_rdataset_t *sigrdataset DNS__DB_FLARG) { - dns_name_t *zcname = NULL; - dns_typepair_t type; - dns_rbtnode_t *node = NULL; - - REQUIRE(search != NULL); - REQUIRE(search->zonecut != NULL); - REQUIRE(search->zonecut_header != NULL); - - /* - * The caller MUST NOT be holding any node locks. - */ - - node = search->zonecut; - type = search->zonecut_header->type; - - /* - * If we have to set foundname, we do it before anything else. - * If we were to set foundname after we had set nodep or bound the - * rdataset, then we'd have to undo that work if dns_name_copy() - * failed. By setting foundname first, there's nothing to undo if - * we have trouble. - */ - if (foundname != NULL && search->copy_name) { - zcname = dns_fixedname_name(&search->zonecut_name); - dns_name_copy(zcname, foundname); - } - if (nodep != NULL) { - /* - * Note that we don't have to increment the node's reference - * count here because we're going to use the reference we - * already have in the search block. - */ - *nodep = (dns_dbnode_t *)node; - search->need_cleanup = false; - } - if (rdataset != NULL) { - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - NODE_RDLOCK(&(search->rbtdb->node_locks[node->locknum].lock), - &nlocktype); - dns__rbtdb_bindrdataset(search->rbtdb, node, - search->zonecut_header, search->now, - isc_rwlocktype_read, - rdataset DNS__DB_FLARG_PASS); - if (sigrdataset != NULL && search->zonecut_sigheader != NULL) { - dns__rbtdb_bindrdataset( - search->rbtdb, node, search->zonecut_sigheader, - search->now, isc_rwlocktype_read, - sigrdataset DNS__DB_FLARG_PASS); - } - NODE_UNLOCK(&(search->rbtdb->node_locks[node->locknum].lock), - &nlocktype); - } - - if (type == dns_rdatatype_dname) { - return (DNS_R_DNAME); - } - return (DNS_R_DELEGATION); -} - -static bool -check_stale_header(dns_rbtnode_t *node, dns_slabheader_t *header, - isc_rwlocktype_t *nlocktypep, isc_rwlock_t *lock, - rbtdb_search_t *search, dns_slabheader_t **header_prev) { - if (!ACTIVE(header, search->now)) { - dns_ttl_t stale = header->ttl + - STALE_TTL(header, search->rbtdb); - /* - * If this data is in the stale window keep it and if - * DNS_DBFIND_STALEOK is not set we tell the caller to - * skip this record. We skip the records with ZEROTTL - * (these records should not be cached anyway). - */ - - DNS_SLABHEADER_CLRATTR(header, DNS_SLABHEADERATTR_STALE_WINDOW); - if (!ZEROTTL(header) && KEEPSTALE(search->rbtdb) && - stale > search->now) - { - dns__rbtdb_mark(header, DNS_SLABHEADERATTR_STALE); - *header_prev = header; - /* - * If DNS_DBFIND_STALESTART is set then it means we - * failed to resolve the name during recursion, in - * this case we mark the time in which the refresh - * failed. - */ - if ((search->options & DNS_DBFIND_STALESTART) != 0) { - atomic_store_release( - &header->last_refresh_fail_ts, - search->now); - } else if ((search->options & - DNS_DBFIND_STALEENABLED) != 0 && - search->now < - (atomic_load_acquire( - &header->last_refresh_fail_ts) + - search->rbtdb->serve_stale_refresh)) - { - /* - * If we are within interval between last - * refresh failure time + 'stale-refresh-time', - * then don't skip this stale entry but use it - * instead. - */ - DNS_SLABHEADER_SETATTR( - header, - DNS_SLABHEADERATTR_STALE_WINDOW); - return (false); - } else if ((search->options & - DNS_DBFIND_STALETIMEOUT) != 0) - { - /* - * We want stale RRset due to timeout, so we - * don't skip it. - */ - return (false); - } - return ((search->options & DNS_DBFIND_STALEOK) == 0); - } - - /* - * This rdataset is stale. If no one else is using the - * node, we can clean it up right now, otherwise we mark - * it as ancient, and the node as dirty, so it will get - * cleaned up later. - */ - if ((header->ttl < search->now - RBTDB_VIRTUAL) && - (*nlocktypep == isc_rwlocktype_write || - NODE_TRYUPGRADE(lock, nlocktypep) == ISC_R_SUCCESS)) - { - /* - * We update the node's status only when we can - * get write access; otherwise, we leave others - * to this work. Periodical cleaning will - * eventually take the job as the last resort. - * We won't downgrade the lock, since other - * rdatasets are probably stale, too. - */ - - if (isc_refcount_current(&node->references) == 0) { - /* - * header->down can be non-NULL if the - * refcount has just decremented to 0 - * but dns__rbtdb_decref() has not - * performed clean_cache_node(), in - * which case we need to purge the stale - * headers first. - */ - clean_stale_headers(header); - if (*header_prev != NULL) { - (*header_prev)->next = header->next; - } else { - node->data = header->next; - } - dns_slabheader_destroy(&header); - } else { - dns__rbtdb_mark(header, - DNS_SLABHEADERATTR_ANCIENT); - RBTDB_HEADERNODE(header)->dirty = 1; - *header_prev = header; - } - } else { - *header_prev = header; - } - return (true); - } - return (false); -} - -static isc_result_t -cache_zonecut_callback(dns_rbtnode_t *node, dns_name_t *name, - void *arg DNS__DB_FLARG) { - rbtdb_search_t *search = arg; - dns_slabheader_t *header = NULL; - dns_slabheader_t *header_prev = NULL, *header_next = NULL; - dns_slabheader_t *dname_header = NULL, *sigdname_header = NULL; - isc_result_t result; - isc_rwlock_t *lock = NULL; - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - - REQUIRE(search->zonecut == NULL); - - /* - * Keep compiler silent. - */ - UNUSED(name); - - lock = &(search->rbtdb->node_locks[node->locknum].lock); - NODE_RDLOCK(lock, &nlocktype); - - /* - * Look for a DNAME or RRSIG DNAME rdataset. - */ - for (header = node->data; header != NULL; header = header_next) { - header_next = header->next; - if (check_stale_header(node, header, &nlocktype, lock, search, - &header_prev)) - { - /* Do nothing. */ - } else if (header->type == dns_rdatatype_dname && - EXISTS(header) && !ANCIENT(header)) - { - dname_header = header; - header_prev = header; - } else if (header->type == DNS_SIGTYPE(dns_rdatatype_dname) && - EXISTS(header) && !ANCIENT(header)) - { - sigdname_header = header; - header_prev = header; - } else { - header_prev = header; - } - } - - if (dname_header != NULL && - (!DNS_TRUST_PENDING(dname_header->trust) || - (search->options & DNS_DBFIND_PENDINGOK) != 0)) - { - /* - * We increment the reference count on node to ensure that - * search->zonecut_header will still be valid later. - */ - dns__rbtdb_newref(search->rbtdb, node, - nlocktype DNS__DB_FLARG_PASS); - search->zonecut = node; - search->zonecut_header = dname_header; - search->zonecut_sigheader = sigdname_header; - search->need_cleanup = true; - result = DNS_R_PARTIALMATCH; - } else { - result = DNS_R_CONTINUE; - } - - NODE_UNLOCK(lock, &nlocktype); - - return (result); -} - -static isc_result_t -find_deepest_zonecut(rbtdb_search_t *search, dns_rbtnode_t *node, - dns_dbnode_t **nodep, dns_name_t *foundname, - dns_rdataset_t *rdataset, - dns_rdataset_t *sigrdataset DNS__DB_FLARG) { - unsigned int i; - isc_result_t result = ISC_R_NOTFOUND; - dns_name_t name; - dns_rbtdb_t *rbtdb = NULL; - bool done; - - /* - * Caller must be holding the tree lock. - */ - - rbtdb = search->rbtdb; - i = search->chain.level_matches; - done = false; - do { - dns_slabheader_t *header = NULL; - dns_slabheader_t *header_prev = NULL, *header_next = NULL; - dns_slabheader_t *found = NULL, *foundsig = NULL; - isc_rwlock_t *lock = &rbtdb->node_locks[node->locknum].lock; - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - - NODE_RDLOCK(lock, &nlocktype); - - /* - * Look for NS and RRSIG NS rdatasets. - */ - for (header = node->data; header != NULL; header = header_next) - { - header_next = header->next; - if (check_stale_header(node, header, &nlocktype, lock, - search, &header_prev)) - { - /* Do nothing. */ - } else if (EXISTS(header) && !ANCIENT(header)) { - /* - * We've found an extant rdataset. See if - * we're interested in it. - */ - if (header->type == dns_rdatatype_ns) { - found = header; - if (foundsig != NULL) { - break; - } - } else if (header->type == - DNS_SIGTYPE(dns_rdatatype_ns)) - { - foundsig = header; - if (found != NULL) { - break; - } - } - header_prev = header; - } else { - header_prev = header; - } - } - - if (found != NULL) { - /* - * If we have to set foundname, we do it before - * anything else. If we were to set foundname after - * we had set nodep or bound the rdataset, then we'd - * have to undo that work if dns_name_concatenate() - * failed. By setting foundname first, there's - * nothing to undo if we have trouble. - */ - if (foundname != NULL) { - dns_name_init(&name, NULL); - dns_rbt_namefromnode(node, &name); - dns_name_copy(&name, foundname); - while (i > 0) { - dns_rbtnode_t *level_node = - search->chain.levels[--i]; - dns_name_init(&name, NULL); - dns_rbt_namefromnode(level_node, &name); - result = dns_name_concatenate( - foundname, &name, foundname, - NULL); - if (result != ISC_R_SUCCESS) { - if (nodep != NULL) { - *nodep = NULL; - } - goto node_exit; - } - } - } - result = DNS_R_DELEGATION; - if (nodep != NULL) { - dns__rbtdb_newref(search->rbtdb, node, - nlocktype DNS__DB_FLARG_PASS); - *nodep = (dns_dbnode_t *)node; - } - dns__rbtdb_bindrdataset(search->rbtdb, node, found, - search->now, nlocktype, - rdataset DNS__DB_FLARG_PASS); - if (foundsig != NULL) { - dns__rbtdb_bindrdataset( - search->rbtdb, node, foundsig, - search->now, nlocktype, - sigrdataset DNS__DB_FLARG_PASS); - } - if (need_headerupdate(found, search->now) || - (foundsig != NULL && - need_headerupdate(foundsig, search->now))) - { - if (nlocktype != isc_rwlocktype_write) { - NODE_FORCEUPGRADE(lock, &nlocktype); - POST(nlocktype); - } - if (need_headerupdate(found, search->now)) { - update_header(search->rbtdb, found, - search->now); - } - if (foundsig != NULL && - need_headerupdate(foundsig, search->now)) - { - update_header(search->rbtdb, foundsig, - search->now); - } - } - } - - node_exit: - NODE_UNLOCK(lock, &nlocktype); - - if (found == NULL && i > 0) { - i--; - node = search->chain.levels[i]; - } else { - done = true; - } - } while (!done); - - return (result); -} - -/* - * Look for a potentially covering NSEC in the cache where `name` - * is known not to exist. This uses the auxiliary NSEC tree to find - * the potential NSEC owner. If found, we update 'foundname', 'nodep', - * 'rdataset' and 'sigrdataset', and return DNS_R_COVERINGNSEC. - * Otherwise, return ISC_R_NOTFOUND. - */ -static isc_result_t -find_coveringnsec(rbtdb_search_t *search, const dns_name_t *name, - dns_dbnode_t **nodep, isc_stdtime_t now, - dns_name_t *foundname, dns_rdataset_t *rdataset, - dns_rdataset_t *sigrdataset DNS__DB_FLARG) { - dns_fixedname_t fprefix, forigin, ftarget, fixed; - dns_name_t *prefix = NULL, *origin = NULL; - dns_name_t *target = NULL, *fname = NULL; - dns_rbtnode_t *node = NULL; - dns_rbtnodechain_t chain; - isc_result_t result; - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - isc_rwlock_t *lock = NULL; - dns_typepair_t matchtype, sigmatchtype; - dns_slabheader_t *found = NULL, *foundsig = NULL; - dns_slabheader_t *header = NULL; - dns_slabheader_t *header_next = NULL, *header_prev = NULL; - - /* - * Look for the node in the auxilary tree. - */ - dns_rbtnodechain_init(&chain); - target = dns_fixedname_initname(&ftarget); - result = dns_rbt_findnode(search->rbtdb->nsec, name, target, &node, - &chain, DNS_RBTFIND_EMPTYDATA, NULL, NULL); - if (result != DNS_R_PARTIALMATCH) { - dns_rbtnodechain_reset(&chain); - return (ISC_R_NOTFOUND); - } - - prefix = dns_fixedname_initname(&fprefix); - origin = dns_fixedname_initname(&forigin); - target = dns_fixedname_initname(&ftarget); - fname = dns_fixedname_initname(&fixed); - - matchtype = DNS_TYPEPAIR_VALUE(dns_rdatatype_nsec, 0); - sigmatchtype = DNS_SIGTYPE(dns_rdatatype_nsec); - - /* - * Extract predecessor from chain. - */ - result = dns_rbtnodechain_current(&chain, prefix, origin, NULL); - dns_rbtnodechain_reset(&chain); - if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { - return (ISC_R_NOTFOUND); - } - - result = dns_name_concatenate(prefix, origin, target, NULL); - if (result != ISC_R_SUCCESS) { - return (ISC_R_NOTFOUND); - } - - /* - * Lookup the predecessor in the main tree. - */ - node = NULL; - result = dns_rbt_findnode(search->rbtdb->tree, target, fname, &node, - NULL, DNS_RBTFIND_EMPTYDATA, NULL, NULL); - if (result != ISC_R_SUCCESS) { - return (ISC_R_NOTFOUND); - } - - lock = &(search->rbtdb->node_locks[node->locknum].lock); - NODE_RDLOCK(lock, &nlocktype); - for (header = node->data; header != NULL; header = header_next) { - header_next = header->next; - if (check_stale_header(node, header, &nlocktype, lock, search, - &header_prev)) - { - continue; - } - if (NONEXISTENT(header) || DNS_TYPEPAIR_TYPE(header->type) == 0) - { - header_prev = header; - continue; - } - if (header->type == matchtype) { - found = header; - if (foundsig != NULL) { - break; - } - } else if (header->type == sigmatchtype) { - foundsig = header; - if (found != NULL) { - break; - } - } - header_prev = header; - } - if (found != NULL) { - dns__rbtdb_bindrdataset(search->rbtdb, node, found, now, - nlocktype, rdataset DNS__DB_FLARG_PASS); - if (foundsig != NULL) { - dns__rbtdb_bindrdataset(search->rbtdb, node, foundsig, - now, nlocktype, - sigrdataset DNS__DB_FLARG_PASS); - } - dns__rbtdb_newref(search->rbtdb, node, - nlocktype DNS__DB_FLARG_PASS); - - dns_name_copy(fname, foundname); - - *nodep = (dns_dbnode_t *)node; - result = DNS_R_COVERINGNSEC; - } else { - result = ISC_R_NOTFOUND; - } - NODE_UNLOCK(lock, &nlocktype); - return (result); -} - -static isc_result_t -cache_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version, - dns_rdatatype_t type, unsigned int options, isc_stdtime_t now, - dns_dbnode_t **nodep, dns_name_t *foundname, - dns_rdataset_t *rdataset, - dns_rdataset_t *sigrdataset DNS__DB_FLARG) { - dns_rbtnode_t *node = NULL; - isc_result_t result; - rbtdb_search_t search; - bool cname_ok = true; - bool found_noqname = false; - bool all_negative = true; - bool empty_node; - isc_rwlock_t *lock = NULL; - isc_rwlocktype_t tlocktype = isc_rwlocktype_none; - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - dns_slabheader_t *header = NULL; - dns_slabheader_t *header_prev = NULL, *header_next = NULL; - dns_slabheader_t *found = NULL, *nsheader = NULL; - dns_slabheader_t *foundsig = NULL, *nssig = NULL, *cnamesig = NULL; - dns_slabheader_t *update = NULL, *updatesig = NULL; - dns_slabheader_t *nsecheader = NULL, *nsecsig = NULL; - dns_typepair_t sigtype, negtype; - - UNUSED(version); - - REQUIRE(VALID_RBTDB((dns_rbtdb_t *)db)); - REQUIRE(version == NULL); - - if (now == 0) { - now = isc_stdtime_now(); - } - - search = (rbtdb_search_t){ - .rbtdb = (dns_rbtdb_t *)db, - .serial = 1, - .options = options, - .now = now, - }; - dns_fixedname_init(&search.zonecut_name); - dns_rbtnodechain_init(&search.chain); - - TREE_RDLOCK(&search.rbtdb->tree_lock, &tlocktype); - - /* - * Search down from the root of the tree. If, while going down, we - * encounter a callback node, cache_zonecut_callback() will search the - * rdatasets at the zone cut for a DNAME rdataset. - */ - result = dns_rbt_findnode(search.rbtdb->tree, name, foundname, &node, - &search.chain, DNS_RBTFIND_EMPTYDATA, - cache_zonecut_callback, &search); - - if (result == DNS_R_PARTIALMATCH) { - /* - * If dns_rbt_findnode discovered a covering DNAME skip - * looking for a covering NSEC. - */ - if ((search.options & DNS_DBFIND_COVERINGNSEC) != 0 && - (search.zonecut_header == NULL || - search.zonecut_header->type != dns_rdatatype_dname)) - { - result = find_coveringnsec( - &search, name, nodep, now, foundname, rdataset, - sigrdataset DNS__DB_FLARG_PASS); - if (result == DNS_R_COVERINGNSEC) { - goto tree_exit; - } - } - if (search.zonecut != NULL) { - result = setup_delegation( - &search, nodep, foundname, rdataset, - sigrdataset DNS__DB_FLARG_PASS); - goto tree_exit; - } else { - find_ns: - result = find_deepest_zonecut( - &search, node, nodep, foundname, rdataset, - sigrdataset DNS__DB_FLARG_PASS); - goto tree_exit; - } - } else if (result != ISC_R_SUCCESS) { - goto tree_exit; - } - - /* - * Certain DNSSEC types are not subject to CNAME matching - * (RFC4035, section 2.5 and RFC3007). - * - * We don't check for RRSIG, because we don't store RRSIG records - * directly. - */ - if (type == dns_rdatatype_key || type == dns_rdatatype_nsec) { - cname_ok = false; - } - - /* - * We now go looking for rdata... - */ - - lock = &(search.rbtdb->node_locks[node->locknum].lock); - NODE_RDLOCK(lock, &nlocktype); - - /* - * These pointers need to be reset here in case we did - * 'goto find_ns' from somewhere below. - */ - found = NULL; - foundsig = NULL; - sigtype = DNS_SIGTYPE(type); - negtype = DNS_TYPEPAIR_VALUE(0, type); - nsheader = NULL; - nsecheader = NULL; - nssig = NULL; - nsecsig = NULL; - cnamesig = NULL; - empty_node = true; - header_prev = NULL; - for (header = node->data; header != NULL; header = header_next) { - header_next = header->next; - if (check_stale_header(node, header, &nlocktype, lock, &search, - &header_prev)) - { - /* Do nothing. */ - } else if (EXISTS(header) && !ANCIENT(header)) { - /* - * We now know that there is at least one active - * non-stale rdataset at this node. - */ - empty_node = false; - if (header->noqname != NULL && - header->trust == dns_trust_secure) - { - found_noqname = true; - } - if (!NEGATIVE(header)) { - all_negative = false; - } - - /* - * If we found a type we were looking for, remember - * it. - */ - if (header->type == type || - (type == dns_rdatatype_any && - DNS_TYPEPAIR_TYPE(header->type) != 0) || - (cname_ok && header->type == dns_rdatatype_cname)) - { - /* - * We've found the answer. - */ - found = header; - if (header->type == dns_rdatatype_cname && - cname_ok) - { - /* - * If we've already got the - * CNAME RRSIG, use it. - */ - if (cnamesig != NULL) { - foundsig = cnamesig; - } else { - sigtype = DNS_SIGTYPE( - dns_rdatatype_cname); - } - } - } else if (header->type == sigtype) { - /* - * We've found the RRSIG rdataset for our - * target type. Remember it. - */ - foundsig = header; - } else if (header->type == RDATATYPE_NCACHEANY || - header->type == negtype) - { - /* - * We've found a negative cache entry. - */ - found = header; - } else if (header->type == dns_rdatatype_ns) { - /* - * Remember a NS rdataset even if we're - * not specifically looking for it, because - * we might need it later. - */ - nsheader = header; - } else if (header->type == - DNS_SIGTYPE(dns_rdatatype_ns)) - { - /* - * If we need the NS rdataset, we'll also - * need its signature. - */ - nssig = header; - } else if (header->type == dns_rdatatype_nsec) { - nsecheader = header; - } else if (header->type == - DNS_SIGTYPE(dns_rdatatype_nsec)) - { - nsecsig = header; - } else if (cname_ok && - header->type == - DNS_SIGTYPE(dns_rdatatype_cname)) - { - /* - * If we get a CNAME match, we'll also need - * its signature. - */ - cnamesig = header; - } - header_prev = header; - } else { - header_prev = header; - } - } - - if (empty_node) { - /* - * We have an exact match for the name, but there are no - * extant rdatasets. That means that this node doesn't - * meaningfully exist, and that we really have a partial match. - */ - NODE_UNLOCK(lock, &nlocktype); - if ((search.options & DNS_DBFIND_COVERINGNSEC) != 0) { - result = find_coveringnsec( - &search, name, nodep, now, foundname, rdataset, - sigrdataset DNS__DB_FLARG_PASS); - if (result == DNS_R_COVERINGNSEC) { - goto tree_exit; - } - } - goto find_ns; - } - - /* - * If we didn't find what we were looking for... - */ - if (found == NULL || - (DNS_TRUST_ADDITIONAL(found->trust) && - ((options & DNS_DBFIND_ADDITIONALOK) == 0)) || - (found->trust == dns_trust_glue && - ((options & DNS_DBFIND_GLUEOK) == 0)) || - (DNS_TRUST_PENDING(found->trust) && - ((options & DNS_DBFIND_PENDINGOK) == 0))) - { - /* - * Return covering NODATA NSEC record. - */ - if ((search.options & DNS_DBFIND_COVERINGNSEC) != 0 && - nsecheader != NULL) - { - if (nodep != NULL) { - dns__rbtdb_newref(search.rbtdb, node, - nlocktype DNS__DB_FLARG_PASS); - *nodep = (dns_dbnode_t *)node; - } - dns__rbtdb_bindrdataset(search.rbtdb, node, nsecheader, - search.now, nlocktype, - rdataset DNS__DB_FLARG_PASS); - if (need_headerupdate(nsecheader, search.now)) { - update = nsecheader; - } - if (nsecsig != NULL) { - dns__rbtdb_bindrdataset( - search.rbtdb, node, nsecsig, search.now, - nlocktype, - sigrdataset DNS__DB_FLARG_PASS); - if (need_headerupdate(nsecsig, search.now)) { - updatesig = nsecsig; - } - } - result = DNS_R_COVERINGNSEC; - goto node_exit; - } - - /* - * This name was from a wild card. Look for a covering NSEC. - */ - if (found == NULL && (found_noqname || all_negative) && - (search.options & DNS_DBFIND_COVERINGNSEC) != 0) - { - NODE_UNLOCK(lock, &nlocktype); - result = find_coveringnsec( - &search, name, nodep, now, foundname, rdataset, - sigrdataset DNS__DB_FLARG_PASS); - if (result == DNS_R_COVERINGNSEC) { - goto tree_exit; - } - goto find_ns; - } - - /* - * If there is an NS rdataset at this node, then this is the - * deepest zone cut. - */ - if (nsheader != NULL) { - if (nodep != NULL) { - dns__rbtdb_newref(search.rbtdb, node, - nlocktype DNS__DB_FLARG_PASS); - *nodep = (dns_dbnode_t *)node; - } - dns__rbtdb_bindrdataset(search.rbtdb, node, nsheader, - search.now, nlocktype, - rdataset DNS__DB_FLARG_PASS); - if (need_headerupdate(nsheader, search.now)) { - update = nsheader; - } - if (nssig != NULL) { - dns__rbtdb_bindrdataset( - search.rbtdb, node, nssig, search.now, - nlocktype, - sigrdataset DNS__DB_FLARG_PASS); - if (need_headerupdate(nssig, search.now)) { - updatesig = nssig; - } - } - result = DNS_R_DELEGATION; - goto node_exit; - } - - /* - * Go find the deepest zone cut. - */ - NODE_UNLOCK(lock, &nlocktype); - goto find_ns; - } - - /* - * We found what we were looking for, or we found a CNAME. - */ - - if (nodep != NULL) { - dns__rbtdb_newref(search.rbtdb, node, - nlocktype DNS__DB_FLARG_PASS); - *nodep = (dns_dbnode_t *)node; - } - - if (NEGATIVE(found)) { - /* - * We found a negative cache entry. - */ - if (NXDOMAIN(found)) { - result = DNS_R_NCACHENXDOMAIN; - } else { - result = DNS_R_NCACHENXRRSET; - } - } else if (type != found->type && type != dns_rdatatype_any && - found->type == dns_rdatatype_cname) - { - /* - * We weren't doing an ANY query and we found a CNAME instead - * of the type we were looking for, so we need to indicate - * that result to the caller. - */ - result = DNS_R_CNAME; - } else { - /* - * An ordinary successful query! - */ - result = ISC_R_SUCCESS; - } - - if (type != dns_rdatatype_any || result == DNS_R_NCACHENXDOMAIN || - result == DNS_R_NCACHENXRRSET) - { - dns__rbtdb_bindrdataset(search.rbtdb, node, found, search.now, - nlocktype, rdataset DNS__DB_FLARG_PASS); - if (need_headerupdate(found, search.now)) { - update = found; - } - if (!NEGATIVE(found) && foundsig != NULL) { - dns__rbtdb_bindrdataset(search.rbtdb, node, foundsig, - search.now, nlocktype, - sigrdataset DNS__DB_FLARG_PASS); - if (need_headerupdate(foundsig, search.now)) { - updatesig = foundsig; - } - } - } - -node_exit: - if ((update != NULL || updatesig != NULL) && - nlocktype != isc_rwlocktype_write) - { - NODE_FORCEUPGRADE(lock, &nlocktype); - POST(nlocktype); - } - if (update != NULL && need_headerupdate(update, search.now)) { - update_header(search.rbtdb, update, search.now); - } - if (updatesig != NULL && need_headerupdate(updatesig, search.now)) { - update_header(search.rbtdb, updatesig, search.now); - } - - NODE_UNLOCK(lock, &nlocktype); - -tree_exit: - TREE_UNLOCK(&search.rbtdb->tree_lock, &tlocktype); - - /* - * If we found a zonecut but aren't going to use it, we have to - * let go of it. - */ - if (search.need_cleanup) { - node = search.zonecut; - INSIST(node != NULL); - lock = &(search.rbtdb->node_locks[node->locknum].lock); - - NODE_RDLOCK(lock, &nlocktype); - dns__rbtdb_decref(search.rbtdb, node, 0, &nlocktype, &tlocktype, - true, false DNS__DB_FLARG_PASS); - NODE_UNLOCK(lock, &nlocktype); - INSIST(tlocktype == isc_rwlocktype_none); - } - - dns_rbtnodechain_reset(&search.chain); - - update_cachestats(search.rbtdb, result); - return (result); -} - -static isc_result_t -cache_findzonecut(dns_db_t *db, const dns_name_t *name, unsigned int options, - isc_stdtime_t now, dns_dbnode_t **nodep, - dns_name_t *foundname, dns_name_t *dcname, - dns_rdataset_t *rdataset, - dns_rdataset_t *sigrdataset DNS__DB_FLARG) { - dns_rbtnode_t *node = NULL; - isc_rwlock_t *lock = NULL; - isc_result_t result; - rbtdb_search_t search; - dns_slabheader_t *header = NULL; - dns_slabheader_t *header_prev = NULL, *header_next = NULL; - dns_slabheader_t *found = NULL, *foundsig = NULL; - unsigned int rbtoptions = DNS_RBTFIND_EMPTYDATA; - isc_rwlocktype_t tlocktype = isc_rwlocktype_none; - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - bool dcnull = (dcname == NULL); - - REQUIRE(VALID_RBTDB((dns_rbtdb_t *)db)); - - if (now == 0) { - now = isc_stdtime_now(); - } - - search = (rbtdb_search_t){ - .rbtdb = (dns_rbtdb_t *)db, - .serial = 1, - .options = options, - .now = now, - }; - dns_fixedname_init(&search.zonecut_name); - dns_rbtnodechain_init(&search.chain); - - if (dcnull) { - dcname = foundname; - } - - if ((options & DNS_DBFIND_NOEXACT) != 0) { - rbtoptions |= DNS_RBTFIND_NOEXACT; - } - - TREE_RDLOCK(&search.rbtdb->tree_lock, &tlocktype); - - /* - * Search down from the root of the tree. - */ - result = dns_rbt_findnode(search.rbtdb->tree, name, dcname, &node, - &search.chain, rbtoptions, NULL, &search); - - if (result == DNS_R_PARTIALMATCH) { - result = find_deepest_zonecut(&search, node, nodep, foundname, - rdataset, - sigrdataset DNS__DB_FLARG_PASS); - goto tree_exit; - } else if (result != ISC_R_SUCCESS) { - goto tree_exit; - } else if (!dcnull) { - dns_name_copy(dcname, foundname); - } - - /* - * We now go looking for an NS rdataset at the node. - */ - - lock = &(search.rbtdb->node_locks[node->locknum].lock); - NODE_RDLOCK(lock, &nlocktype); - - for (header = node->data; header != NULL; header = header_next) { - header_next = header->next; - if (check_stale_header(node, header, &nlocktype, lock, &search, - &header_prev)) - { - /* - * The function dns_rbt_findnode found us the a matching - * node for 'name' and stored the result in 'dcname'. - * This is the deepest known zonecut in our database. - * However, this node may be stale and if serve-stale - * is not enabled (in other words 'stale-answer-enable' - * is set to no), this node may not be used as a - * zonecut we know about. If so, find the deepest - * zonecut from this node up and return that instead. - */ - NODE_UNLOCK(lock, &nlocktype); - result = find_deepest_zonecut( - &search, node, nodep, foundname, rdataset, - sigrdataset DNS__DB_FLARG_PASS); - dns_name_copy(foundname, dcname); - goto tree_exit; - } else if (EXISTS(header) && !ANCIENT(header)) { - /* - * If we found a type we were looking for, remember - * it. - */ - if (header->type == dns_rdatatype_ns) { - /* - * Remember a NS rdataset even if we're - * not specifically looking for it, because - * we might need it later. - */ - found = header; - } else if (header->type == - DNS_SIGTYPE(dns_rdatatype_ns)) - { - /* - * If we need the NS rdataset, we'll also - * need its signature. - */ - foundsig = header; - } - header_prev = header; - } else { - header_prev = header; - } - } - - if (found == NULL) { - /* - * No NS records here. - */ - NODE_UNLOCK(lock, &nlocktype); - result = find_deepest_zonecut(&search, node, nodep, foundname, - rdataset, - sigrdataset DNS__DB_FLARG_PASS); - goto tree_exit; - } - - if (nodep != NULL) { - dns__rbtdb_newref(search.rbtdb, node, - nlocktype DNS__DB_FLARG_PASS); - *nodep = (dns_dbnode_t *)node; - } - - dns__rbtdb_bindrdataset(search.rbtdb, node, found, search.now, - nlocktype, rdataset DNS__DB_FLARG_PASS); - if (foundsig != NULL) { - dns__rbtdb_bindrdataset(search.rbtdb, node, foundsig, - search.now, nlocktype, - sigrdataset DNS__DB_FLARG_PASS); - } - - if (need_headerupdate(found, search.now) || - (foundsig != NULL && need_headerupdate(foundsig, search.now))) - { - if (nlocktype != isc_rwlocktype_write) { - NODE_FORCEUPGRADE(lock, &nlocktype); - POST(nlocktype); - } - if (need_headerupdate(found, search.now)) { - update_header(search.rbtdb, found, search.now); - } - if (foundsig != NULL && need_headerupdate(foundsig, search.now)) - { - update_header(search.rbtdb, foundsig, search.now); - } - } - - NODE_UNLOCK(lock, &nlocktype); - -tree_exit: - TREE_UNLOCK(&search.rbtdb->tree_lock, &tlocktype); - - INSIST(!search.need_cleanup); - - dns_rbtnodechain_reset(&search.chain); - - if (result == DNS_R_DELEGATION) { - result = ISC_R_SUCCESS; - } - - return (result); -} - -static isc_result_t -cache_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, - dns_rdatatype_t type, dns_rdatatype_t covers, - isc_stdtime_t now, dns_rdataset_t *rdataset, - dns_rdataset_t *sigrdataset DNS__DB_FLARG) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node; - dns_slabheader_t *header = NULL, *header_next = NULL; - dns_slabheader_t *found = NULL, *foundsig = NULL; - dns_typepair_t matchtype, sigmatchtype, negtype; - isc_result_t result; - isc_rwlock_t *lock = NULL; - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - - REQUIRE(VALID_RBTDB(rbtdb)); - REQUIRE(type != dns_rdatatype_any); - - UNUSED(version); - - result = ISC_R_SUCCESS; - - if (now == 0) { - now = isc_stdtime_now(); - } - - lock = &rbtdb->node_locks[rbtnode->locknum].lock; - NODE_RDLOCK(lock, &nlocktype); - - matchtype = DNS_TYPEPAIR_VALUE(type, covers); - negtype = DNS_TYPEPAIR_VALUE(0, type); - if (covers == 0) { - sigmatchtype = DNS_SIGTYPE(type); - } else { - sigmatchtype = 0; - } - - for (header = rbtnode->data; header != NULL; header = header_next) { - header_next = header->next; - if (!ACTIVE(header, now)) { - if ((header->ttl + STALE_TTL(header, rbtdb) < - now - RBTDB_VIRTUAL) && - (nlocktype == isc_rwlocktype_write || - NODE_TRYUPGRADE(lock, &nlocktype) == - ISC_R_SUCCESS)) - { - /* - * We update the node's status only when we - * can get write access. - * - * We don't check if refcurrent(rbtnode) == 0 - * and try to free like we do in cache_find(), - * because refcurrent(rbtnode) must be - * non-zero. This is so because 'node' is an - * argument to the function. - */ - dns__rbtdb_mark(header, - DNS_SLABHEADERATTR_ANCIENT); - RBTDB_HEADERNODE(header)->dirty = 1; - } - } else if (EXISTS(header) && !ANCIENT(header)) { - if (header->type == matchtype) { - found = header; - } else if (header->type == RDATATYPE_NCACHEANY || - header->type == negtype) - { - found = header; - } else if (header->type == sigmatchtype) { - foundsig = header; - } - } - } - if (found != NULL) { - dns__rbtdb_bindrdataset(rbtdb, rbtnode, found, now, nlocktype, - rdataset DNS__DB_FLARG_PASS); - if (!NEGATIVE(found) && foundsig != NULL) { - dns__rbtdb_bindrdataset(rbtdb, rbtnode, foundsig, now, - nlocktype, - sigrdataset DNS__DB_FLARG_PASS); - } - } - - NODE_UNLOCK(lock, &nlocktype); - - if (found == NULL) { - return (ISC_R_NOTFOUND); - } - - if (NEGATIVE(found)) { - /* - * We found a negative cache entry. - */ - if (NXDOMAIN(found)) { - result = DNS_R_NCACHENXDOMAIN; - } else { - result = DNS_R_NCACHENXRRSET; - } - } - - update_cachestats(rbtdb, result); - - return (result); -} - -static size_t -hashsize(dns_db_t *db) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - size_t size; - isc_rwlocktype_t tlocktype = isc_rwlocktype_none; - - REQUIRE(VALID_RBTDB(rbtdb)); - - TREE_RDLOCK(&rbtdb->tree_lock, &tlocktype); - size = dns_rbt_hashsize(rbtdb->tree); - TREE_UNLOCK(&rbtdb->tree_lock, &tlocktype); - - return (size); -} - -static isc_result_t -setcachestats(dns_db_t *db, isc_stats_t *stats) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - - REQUIRE(VALID_RBTDB(rbtdb)); - REQUIRE(IS_CACHE(rbtdb)); /* current restriction */ - REQUIRE(stats != NULL); - - isc_stats_attach(stats, &rbtdb->cachestats); - return (ISC_R_SUCCESS); -} - -static dns_stats_t * -getrrsetstats(dns_db_t *db) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - - REQUIRE(VALID_RBTDB(rbtdb)); - REQUIRE(IS_CACHE(rbtdb)); /* current restriction */ - - return (rbtdb->rrsetstats); -} - -static isc_result_t -setservestalettl(dns_db_t *db, dns_ttl_t ttl) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - - REQUIRE(VALID_RBTDB(rbtdb)); - REQUIRE(IS_CACHE(rbtdb)); - - /* currently no bounds checking. 0 means disable. */ - rbtdb->common.serve_stale_ttl = ttl; - return (ISC_R_SUCCESS); -} - -static isc_result_t -getservestalettl(dns_db_t *db, dns_ttl_t *ttl) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - - REQUIRE(VALID_RBTDB(rbtdb)); - REQUIRE(IS_CACHE(rbtdb)); - - *ttl = rbtdb->common.serve_stale_ttl; - return (ISC_R_SUCCESS); -} - -static isc_result_t -setservestalerefresh(dns_db_t *db, uint32_t interval) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - - REQUIRE(VALID_RBTDB(rbtdb)); - REQUIRE(IS_CACHE(rbtdb)); - - /* currently no bounds checking. 0 means disable. */ - rbtdb->serve_stale_refresh = interval; - return (ISC_R_SUCCESS); -} - -static isc_result_t -getservestalerefresh(dns_db_t *db, uint32_t *interval) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - - REQUIRE(VALID_RBTDB(rbtdb)); - REQUIRE(IS_CACHE(rbtdb)); - - *interval = rbtdb->serve_stale_refresh; - return (ISC_R_SUCCESS); -} - -static void -expiredata(dns_db_t *db, dns_dbnode_t *node, void *data) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node; - dns_slabheader_t *header = data; - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - isc_rwlocktype_t tlocktype = isc_rwlocktype_none; - - NODE_WRLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype); - dns__cacherbt_expireheader(header, &tlocktype, - dns_expire_flush DNS__DB_FILELINE); - NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype); - INSIST(tlocktype == isc_rwlocktype_none); -} - -dns_dbmethods_t dns__rbtdb_cachemethods = { - .destroy = dns__rbtdb_destroy, - .currentversion = dns__rbtdb_currentversion, - .newversion = dns__rbtdb_newversion, - .attachversion = dns__rbtdb_attachversion, - .closeversion = dns__rbtdb_closeversion, - .findnode = dns__rbtdb_findnode, - .find = cache_find, - .findzonecut = cache_findzonecut, - .attachnode = dns__rbtdb_attachnode, - .detachnode = dns__rbtdb_detachnode, - .createiterator = dns__rbtdb_createiterator, - .findrdataset = cache_findrdataset, - .allrdatasets = dns__rbtdb_allrdatasets, - .addrdataset = dns__rbtdb_addrdataset, - .subtractrdataset = dns__rbtdb_subtractrdataset, - .deleterdataset = dns__rbtdb_deleterdataset, - .nodecount = dns__rbtdb_nodecount, - .setloop = dns__rbtdb_setloop, - .getoriginnode = dns__rbtdb_getoriginnode, - .getrrsetstats = getrrsetstats, - .setcachestats = setcachestats, - .hashsize = hashsize, - .setservestalettl = setservestalettl, - .getservestalettl = getservestalettl, - .setservestalerefresh = setservestalerefresh, - .getservestalerefresh = getservestalerefresh, - .locknode = dns__rbtdb_locknode, - .unlocknode = dns__rbtdb_unlocknode, - .expiredata = expiredata, - .deletedata = dns__rbtdb_deletedata, - .setmaxrrperset = dns__rbtdb_setmaxrrperset, - .setmaxtypepername = dns__rbtdb_setmaxtypepername, -}; - -/* - * Caller must hold the node (write) lock. - */ -void -dns__cacherbt_expireheader(dns_slabheader_t *header, - isc_rwlocktype_t *tlocktypep, - dns_expire_t reason DNS__DB_FLARG) { - dns__rbtdb_setttl(header, 0); - dns__rbtdb_mark(header, DNS_SLABHEADERATTR_ANCIENT); - RBTDB_HEADERNODE(header)->dirty = 1; - - if (isc_refcount_current(&RBTDB_HEADERNODE(header)->references) == 0) { - isc_rwlocktype_t nlocktype = isc_rwlocktype_write; - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)header->db; - - /* - * If no one else is using the node, we can clean it up now. - * We first need to gain a new reference to the node to meet a - * requirement of dns__rbtdb_decref(). - */ - dns__rbtdb_newref(rbtdb, RBTDB_HEADERNODE(header), - nlocktype DNS__DB_FLARG_PASS); - dns__rbtdb_decref(rbtdb, RBTDB_HEADERNODE(header), 0, - &nlocktype, tlocktypep, true, - false DNS__DB_FLARG_PASS); - - if (rbtdb->cachestats == NULL) { - return; - } - - switch (reason) { - case dns_expire_ttl: - isc_stats_increment(rbtdb->cachestats, - dns_cachestatscounter_deletettl); - break; - case dns_expire_lru: - isc_stats_increment(rbtdb->cachestats, - dns_cachestatscounter_deletelru); - break; - default: - break; - } - } -} - -static size_t -rdataset_size(dns_slabheader_t *header) { - if (!NONEXISTENT(header)) { - return (dns_rdataslab_size((unsigned char *)header, - sizeof(*header))); - } - - return (sizeof(*header)); -} - -static size_t -expire_lru_headers(dns_rbtdb_t *rbtdb, unsigned int locknum, - isc_rwlocktype_t *tlocktypep, - size_t purgesize DNS__DB_FLARG) { - dns_slabheader_t *header = NULL; - size_t purged = 0; - - for (header = ISC_LIST_TAIL(rbtdb->lru[locknum]); - header != NULL && header->last_used <= rbtdb->last_used && - purged <= purgesize; - header = ISC_LIST_TAIL(rbtdb->lru[locknum])) - { - size_t header_size = rdataset_size(header); - - /* - * Unlink the entry at this point to avoid checking it - * again even if it's currently used someone else and - * cannot be purged at this moment. This entry won't be - * referenced any more (so unlinking is safe) since the - * TTL will be reset to 0. - */ - ISC_LIST_UNLINK(rbtdb->lru[locknum], header, link); - dns__cacherbt_expireheader(header, tlocktypep, - dns_expire_lru DNS__DB_FLARG_PASS); - purged += header_size; - } - - return (purged); -} - -/*% - * Purge some expired and/or stale (i.e. unused for some period) cache entries - * due to an overmem condition. To recover from this condition quickly, - * we clean up entries up to the size of newly added rdata that triggered - * the overmem; this is accessible via newheader. - * - * The LRU lists tails are processed in LRU order to the nearest second. - * - * A write lock on the tree must be held. - */ -void -dns__cacherbt_overmem(dns_rbtdb_t *rbtdb, dns_slabheader_t *newheader, - isc_rwlocktype_t *tlocktypep DNS__DB_FLARG) { - uint32_t locknum_start = rbtdb->lru_sweep++ % rbtdb->node_lock_count; - uint32_t locknum = locknum_start; - /* Size of added data, possible node and possible ENT node. */ - size_t purgesize = - rdataset_size(newheader) + - 2 * dns__rbtnode_getsize(RBTDB_HEADERNODE(newheader)); - size_t purged = 0; - isc_stdtime_t min_last_used = 0; - size_t max_passes = 8; - -again: - do { - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - NODE_WRLOCK(&rbtdb->node_locks[locknum].lock, &nlocktype); - - purged += expire_lru_headers(rbtdb, locknum, tlocktypep, - purgesize - - purged DNS__DB_FLARG_PASS); - - /* - * Work out the oldest remaining last_used values of the list - * tails as we walk across the array of lru lists. - */ - dns_slabheader_t *header = ISC_LIST_TAIL(rbtdb->lru[locknum]); - if (header != NULL && - (min_last_used == 0 || header->last_used < min_last_used)) - { - min_last_used = header->last_used; - } - NODE_UNLOCK(&rbtdb->node_locks[locknum].lock, &nlocktype); - locknum = (locknum + 1) % rbtdb->node_lock_count; - } while (locknum != locknum_start && purged <= purgesize); - - /* - * Update rbtdb->last_used if we have walked all the list tails and have - * not freed the required amount of memory. - */ - if (purged < purgesize) { - if (min_last_used != 0) { - rbtdb->last_used = min_last_used; - if (max_passes-- > 0) { - goto again; - } - } - } -} diff --git a/lib/dns/rbt-zonedb.c b/lib/dns/rbt-zonedb.c deleted file mode 100644 index f71bd72e5b..0000000000 --- a/lib/dns/rbt-zonedb.c +++ /dev/null @@ -1,2582 +0,0 @@ -/* - * Copyright (C) Internet Systems Consortium, Inc. ("ISC") - * - * SPDX-License-Identifier: MPL-2.0 - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, you can obtain one at https://mozilla.org/MPL/2.0/. - * - * See the COPYRIGHT file distributed with this work for additional - * information regarding copyright ownership. - */ - -/*! \file */ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "db_p.h" -#include "rbtdb_p.h" - -#define CHECK(op) \ - do { \ - result = (op); \ - if (result != ISC_R_SUCCESS) \ - goto failure; \ - } while (0) - -#define EXISTS(header) \ - ((atomic_load_acquire(&(header)->attributes) & \ - DNS_SLABHEADERATTR_NONEXISTENT) == 0) -#define NONEXISTENT(header) \ - ((atomic_load_acquire(&(header)->attributes) & \ - DNS_SLABHEADERATTR_NONEXISTENT) != 0) -#define IGNORE(header) \ - ((atomic_load_acquire(&(header)->attributes) & \ - DNS_SLABHEADERATTR_IGNORE) != 0) -#define RESIGN(header) \ - ((atomic_load_acquire(&(header)->attributes) & \ - DNS_SLABHEADERATTR_RESIGN) != 0) -#define ANCIENT(header) \ - ((atomic_load_acquire(&(header)->attributes) & \ - DNS_SLABHEADERATTR_ANCIENT) != 0) - -#define RBTDB_ATTR_LOADED 0x01 -#define RBTDB_ATTR_LOADING 0x02 - -static isc_result_t -findnsec3node(dns_db_t *db, const dns_name_t *name, bool create, - dns_dbnode_t **nodep DNS__DB_FLARG) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - - REQUIRE(VALID_RBTDB(rbtdb)); - - return (dns__rbtdb_findnodeintree(rbtdb, rbtdb->nsec3, name, create, - nodep DNS__DB_FLARG_PASS)); -} - -static isc_result_t -zone_zonecut_callback(dns_rbtnode_t *node, dns_name_t *name, - void *arg DNS__DB_FLARG) { - rbtdb_search_t *search = arg; - dns_slabheader_t *header = NULL, *header_next = NULL; - dns_slabheader_t *dname_header = NULL, *sigdname_header = NULL; - dns_slabheader_t *ns_header = NULL; - dns_slabheader_t *found = NULL; - isc_result_t result = DNS_R_CONTINUE; - dns_rbtnode_t *onode = NULL; - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - - /* - * We only want to remember the topmost zone cut, since it's the one - * that counts, so we'll just continue if we've already found a - * zonecut. - */ - if (search->zonecut != NULL) { - return (result); - } - - onode = search->rbtdb->origin_node; - - NODE_RDLOCK(&(search->rbtdb->node_locks[node->locknum].lock), - &nlocktype); - - /* - * Look for an NS or DNAME rdataset active in our version. - */ - for (header = node->data; header != NULL; header = header_next) { - header_next = header->next; - if (header->type == dns_rdatatype_ns || - header->type == dns_rdatatype_dname || - header->type == DNS_SIGTYPE(dns_rdatatype_dname)) - { - do { - if (header->serial <= search->serial && - !IGNORE(header)) - { - /* - * Is this a "this rdataset doesn't - * exist" record? - */ - if (NONEXISTENT(header)) { - header = NULL; - } - break; - } else { - header = header->down; - } - } while (header != NULL); - if (header != NULL) { - if (header->type == dns_rdatatype_dname) { - dname_header = header; - } else if (header->type == - DNS_SIGTYPE(dns_rdatatype_dname)) - { - sigdname_header = header; - } else if (node != onode || - IS_STUB(search->rbtdb)) - { - /* - * We've found an NS rdataset that - * isn't at the origin node. We check - * that they're not at the origin node, - * because otherwise we'd erroneously - * treat the zone top as if it were - * a delegation. - */ - ns_header = header; - } - } - } - } - - /* - * Did we find anything? - */ - if (!IS_STUB(search->rbtdb) && ns_header != NULL) { - /* - * Note that NS has precedence over DNAME if both exist - * in a zone. Otherwise DNAME take precedence over NS. - */ - found = ns_header; - search->zonecut_sigheader = NULL; - } else if (dname_header != NULL) { - found = dname_header; - search->zonecut_sigheader = sigdname_header; - } else if (ns_header != NULL) { - found = ns_header; - search->zonecut_sigheader = NULL; - } - - if (found != NULL) { - /* - * We increment the reference count on node to ensure that - * search->zonecut_header will still be valid later. - */ - dns__rbtdb_newref(search->rbtdb, node, - isc_rwlocktype_read DNS__DB_FLARG_PASS); - search->zonecut = node; - search->zonecut_header = found; - search->need_cleanup = true; - /* - * Since we've found a zonecut, anything beneath it is - * glue and is not subject to wildcard matching, so we - * may clear search->wild. - */ - search->wild = false; - if ((search->options & DNS_DBFIND_GLUEOK) == 0) { - /* - * If the caller does not want to find glue, then - * this is the best answer and the search should - * stop now. - */ - result = DNS_R_PARTIALMATCH; - } else { - dns_name_t *zcname = NULL; - - /* - * The search will continue beneath the zone cut. - * This may or may not be the best match. In case it - * is, we need to remember the node name. - */ - zcname = dns_fixedname_name(&search->zonecut_name); - dns_name_copy(name, zcname); - search->copy_name = true; - } - } else { - /* - * There is no zonecut at this node which is active in this - * version. - * - * If this is a "wild" node and the caller hasn't disabled - * wildcard matching, remember that we've seen a wild node - * in case we need to go searching for wildcard matches - * later on. - */ - if (node->wild && (search->options & DNS_DBFIND_NOWILD) == 0) { - search->wild = true; - } - } - - NODE_UNLOCK(&(search->rbtdb->node_locks[node->locknum].lock), - &nlocktype); - - return (result); -} - -static isc_result_t -setup_delegation(rbtdb_search_t *search, dns_dbnode_t **nodep, - dns_name_t *foundname, dns_rdataset_t *rdataset, - dns_rdataset_t *sigrdataset DNS__DB_FLARG) { - dns_name_t *zcname = NULL; - dns_typepair_t type; - dns_rbtnode_t *node = NULL; - - REQUIRE(search != NULL); - REQUIRE(search->zonecut != NULL); - REQUIRE(search->zonecut_header != NULL); - - /* - * The caller MUST NOT be holding any node locks. - */ - - node = search->zonecut; - type = search->zonecut_header->type; - - /* - * If we have to set foundname, we do it before anything else. - * If we were to set foundname after we had set nodep or bound the - * rdataset, then we'd have to undo that work if dns_name_copy() - * failed. By setting foundname first, there's nothing to undo if - * we have trouble. - */ - if (foundname != NULL && search->copy_name) { - zcname = dns_fixedname_name(&search->zonecut_name); - dns_name_copy(zcname, foundname); - } - if (nodep != NULL) { - /* - * Note that we don't have to increment the node's reference - * count here because we're going to use the reference we - * already have in the search block. - */ - *nodep = (dns_dbnode_t *)node; - search->need_cleanup = false; - } - if (rdataset != NULL) { - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - NODE_RDLOCK(&(search->rbtdb->node_locks[node->locknum].lock), - &nlocktype); - dns__rbtdb_bindrdataset(search->rbtdb, node, - search->zonecut_header, search->now, - isc_rwlocktype_read, - rdataset DNS__DB_FLARG_PASS); - if (sigrdataset != NULL && search->zonecut_sigheader != NULL) { - dns__rbtdb_bindrdataset( - search->rbtdb, node, search->zonecut_sigheader, - search->now, isc_rwlocktype_read, - sigrdataset DNS__DB_FLARG_PASS); - } - NODE_UNLOCK(&(search->rbtdb->node_locks[node->locknum].lock), - &nlocktype); - } - - if (type == dns_rdatatype_dname) { - return (DNS_R_DNAME); - } - return (DNS_R_DELEGATION); -} - -typedef enum { FORWARD, BACK } direction_t; - -/* - * Step backwards or forwards through the database until we find a - * node with data in it for the desired version. If 'nextname' is not NULL, - * and we found a predecessor or successor, save the name we found in it. - * Return true if we found a predecessor or successor. - */ -static bool -step(rbtdb_search_t *search, dns_rbtnodechain_t *chain, direction_t direction, - dns_name_t *nextname) { - dns_fixedname_t forigin; - dns_name_t *origin = NULL; - dns_name_t prefix; - dns_rbtdb_t *rbtdb = NULL; - dns_rbtnode_t *node = NULL; - isc_result_t result = ISC_R_SUCCESS; - dns_slabheader_t *header = NULL; - - rbtdb = search->rbtdb; - - dns_name_init(&prefix, NULL); - origin = dns_fixedname_initname(&forigin); - - while (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) { - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - node = NULL; - result = dns_rbtnodechain_current(chain, &prefix, origin, - &node); - if (result != ISC_R_SUCCESS) { - break; - } - NODE_RDLOCK(&(rbtdb->node_locks[node->locknum].lock), - &nlocktype); - for (header = node->data; header != NULL; header = header->next) - { - if (header->serial <= search->serial && - !IGNORE(header) && EXISTS(header)) - { - break; - } - } - NODE_UNLOCK(&(rbtdb->node_locks[node->locknum].lock), - &nlocktype); - if (header != NULL) { - break; - } - if (direction == FORWARD) { - result = dns_rbtnodechain_next(chain, NULL, NULL); - } else { - result = dns_rbtnodechain_prev(chain, NULL, NULL); - } - }; - if (result == ISC_R_SUCCESS) { - result = dns_name_concatenate(&prefix, origin, nextname, NULL); - } - if (result == ISC_R_SUCCESS) { - return (true); - } - return (false); -} - -/* - * Use step() to find the successor to the current name, and then - * check to see whether it's a subdomain of the current name. If so, - * then this is an empty non-terminal in the currently active version - * of the database. - */ -static bool -activeempty(rbtdb_search_t *search, dns_rbtnodechain_t *chain, - const dns_name_t *current) { - isc_result_t result; - dns_fixedname_t fnext; - dns_name_t *next = dns_fixedname_initname(&fnext); - - result = dns_rbtnodechain_next(chain, NULL, NULL); - if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { - return (false); - } - return (step(search, chain, FORWARD, next) && - dns_name_issubdomain(next, current)); -} - -static bool -wildcard_blocked(rbtdb_search_t *search, const dns_name_t *qname, - dns_name_t *wname) { - isc_result_t result; - dns_fixedname_t fnext; - dns_fixedname_t fprev; - dns_name_t *next = NULL, *prev = NULL; - dns_name_t name; - dns_name_t rname; - dns_name_t tname; - dns_rbtnodechain_t chain; - bool check_next = false; - bool check_prev = false; - unsigned int n; - - dns_name_init(&name, NULL); - dns_name_init(&tname, NULL); - dns_name_init(&rname, NULL); - next = dns_fixedname_initname(&fnext); - prev = dns_fixedname_initname(&fprev); - - /* - * The qname seems to have matched a wildcard, but we - * need to find out if there's an empty nonterminal node - * between the wildcard level and the qname. - * - * search->chain should now be pointing at the predecessor - * of the searched-for name. We are using a local copy of the - * chain so as not to change the state of search->chain. - * step() will walk backward until we find a predecessor with - * data. - */ - chain = search->chain; - check_prev = step(search, &chain, BACK, prev); - - /* Now reset the chain and look for a successor with data. */ - chain = search->chain; - result = dns_rbtnodechain_next(&chain, NULL, NULL); - if (result == ISC_R_SUCCESS) { - check_next = step(search, &chain, FORWARD, next); - } - - if (!check_prev && !check_next) { - /* No predecessor or successor was found at all? */ - return (false); - } - - dns_name_clone(qname, &rname); - - /* - * Remove the wildcard label to find the terminal name. - */ - n = dns_name_countlabels(wname); - dns_name_getlabelsequence(wname, 1, n - 1, &tname); - - do { - if ((check_prev && dns_name_issubdomain(prev, &rname)) || - (check_next && dns_name_issubdomain(next, &rname))) - { - return (true); - } - - /* - * Remove the leftmost label from the qname and check again. - */ - n = dns_name_countlabels(&rname); - dns_name_getlabelsequence(&rname, 1, n - 1, &rname); - } while (!dns_name_equal(&rname, &tname)); - - return (false); -} - -static isc_result_t -find_wildcard(rbtdb_search_t *search, dns_rbtnode_t **nodep, - const dns_name_t *qname) { - unsigned int i, j; - dns_rbtnode_t *node = NULL, *level_node = NULL, *wnode = NULL; - dns_slabheader_t *header = NULL; - isc_result_t result = ISC_R_NOTFOUND; - dns_name_t name; - dns_name_t *wname = NULL; - dns_fixedname_t fwname; - dns_rbtdb_t *rbtdb = NULL; - bool done, wild, active; - dns_rbtnodechain_t wchain; - - /* - * Caller must be holding the tree lock and MUST NOT be holding - * any node locks. - */ - - /* - * Examine each ancestor level. If the level's wild bit - * is set, then construct the corresponding wildcard name and - * search for it. If the wildcard node exists, and is active in - * this version, we're done. If not, then we next check to see - * if the ancestor is active in this version. If so, then there - * can be no possible wildcard match and again we're done. If not, - * continue the search. - */ - - rbtdb = search->rbtdb; - i = search->chain.level_matches; - done = false; - node = *nodep; - do { - isc_rwlock_t *lock = &rbtdb->node_locks[node->locknum].lock; - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - NODE_RDLOCK(lock, &nlocktype); - - /* - * First we try to figure out if this node is active in - * the search's version. We do this now, even though we - * may not need the information, because it simplifies the - * locking and code flow. - */ - for (header = node->data; header != NULL; header = header->next) - { - if (header->serial <= search->serial && - !IGNORE(header) && EXISTS(header) && - !ANCIENT(header)) - { - break; - } - } - if (header != NULL) { - active = true; - } else { - active = false; - } - - if (node->wild) { - wild = true; - } else { - wild = false; - } - - NODE_UNLOCK(lock, &nlocktype); - - if (wild) { - /* - * Construct the wildcard name for this level. - */ - dns_name_init(&name, NULL); - dns_rbt_namefromnode(node, &name); - wname = dns_fixedname_initname(&fwname); - result = dns_name_concatenate(dns_wildcardname, &name, - wname, NULL); - j = i; - while (result == ISC_R_SUCCESS && j != 0) { - j--; - level_node = search->chain.levels[j]; - dns_name_init(&name, NULL); - dns_rbt_namefromnode(level_node, &name); - result = dns_name_concatenate(wname, &name, - wname, NULL); - } - if (result != ISC_R_SUCCESS) { - break; - } - - wnode = NULL; - dns_rbtnodechain_init(&wchain); - result = dns_rbt_findnode( - rbtdb->tree, wname, NULL, &wnode, &wchain, - DNS_RBTFIND_EMPTYDATA, NULL, NULL); - if (result == ISC_R_SUCCESS) { - /* - * We have found the wildcard node. If it - * is active in the search's version, we're - * done. - */ - lock = &rbtdb->node_locks[wnode->locknum].lock; - NODE_RDLOCK(lock, &nlocktype); - for (header = wnode->data; header != NULL; - header = header->next) - { - if (header->serial <= search->serial && - !IGNORE(header) && EXISTS(header) && - !ANCIENT(header)) - { - break; - } - } - NODE_UNLOCK(lock, &nlocktype); - if (header != NULL || - activeempty(search, &wchain, wname)) - { - if (wildcard_blocked(search, qname, - wname)) - { - return (ISC_R_NOTFOUND); - } - /* - * The wildcard node is active! - * - * Note: result is still ISC_R_SUCCESS - * so we don't have to set it. - */ - *nodep = wnode; - break; - } - } else if (result != ISC_R_NOTFOUND && - result != DNS_R_PARTIALMATCH) - { - /* - * An error has occurred. Bail out. - */ - break; - } - } - - if (active) { - /* - * The level node is active. Any wildcarding - * present at higher levels has no - * effect and we're done. - */ - result = ISC_R_NOTFOUND; - break; - } - - if (i > 0) { - i--; - node = search->chain.levels[i]; - } else { - done = true; - } - } while (!done); - - return (result); -} - -static bool -matchparams(dns_slabheader_t *header, rbtdb_search_t *search) { - dns_rdata_t rdata = DNS_RDATA_INIT; - dns_rdata_nsec3_t nsec3; - unsigned char *raw = NULL; - unsigned int rdlen, count; - isc_region_t region; - isc_result_t result; - - REQUIRE(header->type == dns_rdatatype_nsec3); - - raw = (unsigned char *)header + sizeof(*header); - count = raw[0] * 256 + raw[1]; /* count */ - raw += DNS_RDATASET_COUNT + DNS_RDATASET_LENGTH; - - while (count-- > 0) { - rdlen = raw[0] * 256 + raw[1]; - raw += DNS_RDATASET_ORDER + DNS_RDATASET_LENGTH; - region.base = raw; - region.length = rdlen; - dns_rdata_fromregion(&rdata, search->rbtdb->common.rdclass, - dns_rdatatype_nsec3, ®ion); - raw += rdlen; - result = dns_rdata_tostruct(&rdata, &nsec3, NULL); - INSIST(result == ISC_R_SUCCESS); - if (nsec3.hash == search->rbtversion->hash && - nsec3.iterations == search->rbtversion->iterations && - nsec3.salt_length == search->rbtversion->salt_length && - memcmp(nsec3.salt, search->rbtversion->salt, - nsec3.salt_length) == 0) - { - return (true); - } - dns_rdata_reset(&rdata); - } - return (false); -} - -/* - * Find node of the NSEC/NSEC3 record that is 'name'. - */ -static isc_result_t -previous_closest_nsec(dns_rdatatype_t type, rbtdb_search_t *search, - dns_name_t *name, dns_name_t *origin, - dns_rbtnode_t **nodep, dns_rbtnodechain_t *nsecchain, - bool *firstp) { - dns_fixedname_t ftarget; - dns_name_t *target = NULL; - dns_rbtnode_t *nsecnode = NULL; - isc_result_t result; - - REQUIRE(nodep != NULL && *nodep == NULL); - REQUIRE(type == dns_rdatatype_nsec3 || firstp != NULL); - - if (type == dns_rdatatype_nsec3) { - result = dns_rbtnodechain_prev(&search->chain, NULL, NULL); - if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { - return (result); - } - result = dns_rbtnodechain_current(&search->chain, name, origin, - nodep); - return (result); - } - - target = dns_fixedname_initname(&ftarget); - - for (;;) { - if (*firstp) { - /* - * Construct the name of the second node to check. - * It is the first node sought in the NSEC tree. - */ - *firstp = false; - dns_rbtnodechain_init(nsecchain); - result = dns_name_concatenate(name, origin, target, - NULL); - if (result != ISC_R_SUCCESS) { - return (result); - } - nsecnode = NULL; - result = dns_rbt_findnode( - search->rbtdb->nsec, target, NULL, &nsecnode, - nsecchain, DNS_RBTFIND_EMPTYDATA, NULL, NULL); - if (result == ISC_R_SUCCESS) { - /* - * Since this was the first loop, finding the - * name in the NSEC tree implies that the first - * node checked in the main tree had an - * unacceptable NSEC record. - * Try the previous node in the NSEC tree. - */ - result = dns_rbtnodechain_prev(nsecchain, name, - origin); - if (result == DNS_R_NEWORIGIN) { - result = ISC_R_SUCCESS; - } - } else if (result == ISC_R_NOTFOUND || - result == DNS_R_PARTIALMATCH) - { - result = dns_rbtnodechain_current( - nsecchain, name, origin, NULL); - if (result == ISC_R_NOTFOUND) { - result = ISC_R_NOMORE; - } - } - } else { - /* - * This is a second or later trip through the auxiliary - * tree for the name of a third or earlier NSEC node in - * the main tree. Previous trips through the NSEC tree - * must have found nodes in the main tree with NSEC - * records. Perhaps they lacked signature records. - */ - result = dns_rbtnodechain_prev(nsecchain, name, origin); - if (result == DNS_R_NEWORIGIN) { - result = ISC_R_SUCCESS; - } - } - if (result != ISC_R_SUCCESS) { - return (result); - } - - /* - * Construct the name to seek in the main tree. - */ - result = dns_name_concatenate(name, origin, target, NULL); - if (result != ISC_R_SUCCESS) { - return (result); - } - - *nodep = NULL; - result = dns_rbt_findnode(search->rbtdb->tree, target, NULL, - nodep, &search->chain, - DNS_RBTFIND_EMPTYDATA, NULL, NULL); - if (result == ISC_R_SUCCESS) { - return (result); - } - - /* - * There should always be a node in the main tree with the - * same name as the node in the auxiliary NSEC tree, except for - * nodes in the auxiliary tree that are awaiting deletion. - */ - if (result != DNS_R_PARTIALMATCH && result != ISC_R_NOTFOUND) { - isc_log_write(DNS_LOGCATEGORY_DATABASE, - DNS_LOGMODULE_CACHE, ISC_LOG_ERROR, - "previous_closest_nsec(): %s", - isc_result_totext(result)); - return (DNS_R_BADDB); - } - } -} - -/* - * Find the NSEC/NSEC3 which is or before the current point on the - * search chain. For NSEC3 records only NSEC3 records that match the - * current NSEC3PARAM record are considered. - */ -static isc_result_t -find_closest_nsec(rbtdb_search_t *search, dns_dbnode_t **nodep, - dns_name_t *foundname, dns_rdataset_t *rdataset, - dns_rdataset_t *sigrdataset, dns_rbt_t *tree, - bool secure DNS__DB_FLARG) { - dns_rbtnode_t *node = NULL, *prevnode = NULL; - dns_slabheader_t *header = NULL, *header_next = NULL; - dns_rbtnodechain_t nsecchain; - bool empty_node; - isc_result_t result; - dns_fixedname_t fname, forigin; - dns_name_t *name = NULL, *origin = NULL; - dns_rdatatype_t type; - dns_typepair_t sigtype; - bool wraps; - bool first = true; - bool need_sig = secure; - - if (tree == search->rbtdb->nsec3) { - type = dns_rdatatype_nsec3; - sigtype = DNS_SIGTYPE(dns_rdatatype_nsec3); - wraps = true; - } else { - type = dns_rdatatype_nsec; - sigtype = DNS_SIGTYPE(dns_rdatatype_nsec); - wraps = false; - } - - /* - * Use the auxiliary tree only starting with the second node in the - * hope that the original node will be right much of the time. - */ - name = dns_fixedname_initname(&fname); - origin = dns_fixedname_initname(&forigin); -again: - node = NULL; - prevnode = NULL; - result = dns_rbtnodechain_current(&search->chain, name, origin, &node); - if (result != ISC_R_SUCCESS) { - return (result); - } - do { - dns_slabheader_t *found = NULL, *foundsig = NULL; - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - NODE_RDLOCK(&(search->rbtdb->node_locks[node->locknum].lock), - &nlocktype); - empty_node = true; - for (header = node->data; header != NULL; header = header_next) - { - header_next = header->next; - /* - * Look for an active, extant NSEC or RRSIG NSEC. - */ - do { - if (header->serial <= search->serial && - !IGNORE(header)) - { - /* - * Is this a "this rdataset doesn't - * exist" record? - */ - if (NONEXISTENT(header)) { - header = NULL; - } - break; - } else { - header = header->down; - } - } while (header != NULL); - if (header != NULL) { - /* - * We now know that there is at least one - * active rdataset at this node. - */ - empty_node = false; - if (header->type == type) { - found = header; - if (foundsig != NULL) { - break; - } - } else if (header->type == sigtype) { - foundsig = header; - if (found != NULL) { - break; - } - } - } - } - if (!empty_node) { - if (found != NULL && search->rbtversion->havensec3 && - found->type == dns_rdatatype_nsec3 && - !matchparams(found, search)) - { - empty_node = true; - found = NULL; - foundsig = NULL; - result = previous_closest_nsec( - type, search, name, origin, &prevnode, - NULL, NULL); - } else if (found != NULL && - (foundsig != NULL || !need_sig)) - { - /* - * We've found the right NSEC/NSEC3 record. - * - * Note: for this to really be the right - * NSEC record, it's essential that the NSEC - * records of any nodes obscured by a zone - * cut have been removed; we assume this is - * the case. - */ - result = dns_name_concatenate(name, origin, - foundname, NULL); - if (result == ISC_R_SUCCESS) { - if (nodep != NULL) { - dns__rbtdb_newref( - search->rbtdb, node, - isc_rwlocktype_read - DNS__DB_FLARG_PASS); - *nodep = (dns_dbnode_t *)node; - } - dns__rbtdb_bindrdataset( - search->rbtdb, node, found, - search->now, - isc_rwlocktype_read, - rdataset DNS__DB_FLARG_PASS); - if (foundsig != NULL) { - dns__rbtdb_bindrdataset( - search->rbtdb, node, - foundsig, search->now, - isc_rwlocktype_read, - sigrdataset - DNS__DB_FLARG_PASS); - } - } - } else if (found == NULL && foundsig == NULL) { - /* - * This node is active, but has no NSEC or - * RRSIG NSEC. That means it's glue or - * other obscured zone data that isn't - * relevant for our search. Treat the - * node as if it were empty and keep looking. - */ - empty_node = true; - result = previous_closest_nsec( - type, search, name, origin, &prevnode, - &nsecchain, &first); - } else { - /* - * We found an active node, but either the - * NSEC or the RRSIG NSEC is missing. This - * shouldn't happen. - */ - result = DNS_R_BADDB; - } - } else { - /* - * This node isn't active. We've got to keep - * looking. - */ - result = previous_closest_nsec(type, search, name, - origin, &prevnode, - &nsecchain, &first); - } - NODE_UNLOCK(&(search->rbtdb->node_locks[node->locknum].lock), - &nlocktype); - node = prevnode; - prevnode = NULL; - } while (empty_node && result == ISC_R_SUCCESS); - - if (!first) { - dns_rbtnodechain_invalidate(&nsecchain); - } - - if (result == ISC_R_NOMORE && wraps) { - result = dns_rbtnodechain_last(&search->chain, tree, NULL, - NULL); - if (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) { - wraps = false; - goto again; - } - } - - /* - * If the result is ISC_R_NOMORE, then we got to the beginning of - * the database and didn't find a NSEC record. This shouldn't - * happen. - */ - if (result == ISC_R_NOMORE) { - result = DNS_R_BADDB; - } - - return (result); -} - -static isc_result_t -zone_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version, - dns_rdatatype_t type, unsigned int options, - isc_stdtime_t now ISC_ATTR_UNUSED, dns_dbnode_t **nodep, - dns_name_t *foundname, dns_rdataset_t *rdataset, - dns_rdataset_t *sigrdataset DNS__DB_FLARG) { - dns_rbtnode_t *node = NULL; - isc_result_t result; - rbtdb_search_t search; - bool cname_ok = true; - bool close_version = false; - bool maybe_zonecut = false; - bool at_zonecut = false; - bool wild = false; - bool empty_node; - dns_slabheader_t *header = NULL, *header_next = NULL; - dns_slabheader_t *found = NULL, *nsecheader = NULL; - dns_slabheader_t *foundsig = NULL, *cnamesig = NULL, *nsecsig = NULL; - dns_typepair_t sigtype; - bool active; - isc_rwlock_t *lock = NULL; - dns_rbt_t *tree = NULL; - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - isc_rwlocktype_t tlocktype = isc_rwlocktype_none; - - REQUIRE(VALID_RBTDB((dns_rbtdb_t *)db)); - INSIST(version == NULL || - ((dns_rbtdb_version_t *)version)->rbtdb == (dns_rbtdb_t *)db); - - /* - * If the caller didn't supply a version, attach to the current - * version. - */ - if (version == NULL) { - dns__rbtdb_currentversion(db, &version); - close_version = true; - } - - search = (rbtdb_search_t){ - .rbtdb = (dns_rbtdb_t *)db, - .rbtversion = (dns_rbtdb_version_t *)version, - .serial = ((dns_rbtdb_version_t *)version)->serial, - .options = options, - }; - dns_fixedname_init(&search.zonecut_name); - dns_rbtnodechain_init(&search.chain); - - TREE_RDLOCK(&search.rbtdb->tree_lock, &tlocktype); - - /* - * Search down from the root of the tree. If, while going down, we - * encounter a callback node, zone_zonecut_callback() will search the - * rdatasets at the zone cut for active DNAME or NS rdatasets. - */ - tree = (options & DNS_DBFIND_FORCENSEC3) != 0 ? search.rbtdb->nsec3 - : search.rbtdb->tree; - result = dns_rbt_findnode(tree, name, foundname, &node, &search.chain, - DNS_RBTFIND_EMPTYDATA, zone_zonecut_callback, - &search); - - if (result == DNS_R_PARTIALMATCH) { - partial_match: - if (search.zonecut != NULL) { - result = setup_delegation( - &search, nodep, foundname, rdataset, - sigrdataset DNS__DB_FLARG_PASS); - goto tree_exit; - } - - if (search.wild) { - /* - * At least one of the levels in the search chain - * potentially has a wildcard. For each such level, - * we must see if there's a matching wildcard active - * in the current version. - */ - result = find_wildcard(&search, &node, name); - if (result == ISC_R_SUCCESS) { - dns_name_copy(name, foundname); - wild = true; - goto found; - } else if (result != ISC_R_NOTFOUND) { - goto tree_exit; - } - } - - active = false; - if ((options & DNS_DBFIND_FORCENSEC3) == 0) { - /* - * The NSEC3 tree won't have empty nodes, - * so it isn't necessary to check for them. - */ - dns_rbtnodechain_t chain = search.chain; - active = activeempty(&search, &chain, name); - } - - /* - * If we're here, then the name does not exist, is not - * beneath a zonecut, and there's no matching wildcard. - */ - if ((search.rbtversion->secure && - !search.rbtversion->havensec3) || - (search.options & DNS_DBFIND_FORCENSEC3) != 0) - { - result = find_closest_nsec( - &search, nodep, foundname, rdataset, - sigrdataset, tree, - search.rbtversion->secure DNS__DB_FLARG_PASS); - if (result == ISC_R_SUCCESS) { - result = active ? DNS_R_EMPTYNAME - : DNS_R_NXDOMAIN; - } - } else { - result = active ? DNS_R_EMPTYNAME : DNS_R_NXDOMAIN; - } - goto tree_exit; - } else if (result != ISC_R_SUCCESS) { - goto tree_exit; - } - -found: - /* - * We have found a node whose name is the desired name, or we - * have matched a wildcard. - */ - - if (search.zonecut != NULL) { - /* - * If we're beneath a zone cut, we don't want to look for - * CNAMEs because they're not legitimate zone glue. - */ - cname_ok = false; - } else { - /* - * The node may be a zone cut itself. If it might be one, - * make sure we check for it later. - * - * DS records live above the zone cut in ordinary zone so - * we want to ignore any referral. - * - * Stub zones don't have anything "above" the delegation so - * we always return a referral. - */ - if (node->find_callback && - ((node != search.rbtdb->origin_node && - !dns_rdatatype_atparent(type)) || - IS_STUB(search.rbtdb))) - { - maybe_zonecut = true; - } - } - - /* - * Certain DNSSEC types are not subject to CNAME matching - * (RFC4035, section 2.5 and RFC3007). - * - * We don't check for RRSIG, because we don't store RRSIG records - * directly. - */ - if (type == dns_rdatatype_key || type == dns_rdatatype_nsec) { - cname_ok = false; - } - - /* - * We now go looking for rdata... - */ - - lock = &search.rbtdb->node_locks[node->locknum].lock; - NODE_RDLOCK(lock, &nlocktype); - - found = NULL; - foundsig = NULL; - sigtype = DNS_SIGTYPE(type); - nsecheader = NULL; - nsecsig = NULL; - cnamesig = NULL; - empty_node = true; - for (header = node->data; header != NULL; header = header_next) { - header_next = header->next; - /* - * Look for an active, extant rdataset. - */ - do { - if (header->serial <= search.serial && !IGNORE(header)) - { - /* - * Is this a "this rdataset doesn't - * exist" record? - */ - if (NONEXISTENT(header)) { - header = NULL; - } - break; - } else { - header = header->down; - } - } while (header != NULL); - if (header != NULL) { - /* - * We now know that there is at least one active - * rdataset at this node. - */ - empty_node = false; - - /* - * Do special zone cut handling, if requested. - */ - if (maybe_zonecut && header->type == dns_rdatatype_ns) { - /* - * We increment the reference count on node to - * ensure that search->zonecut_header will - * still be valid later. - */ - dns__rbtdb_newref(search.rbtdb, node, - nlocktype DNS__DB_FLARG_PASS); - search.zonecut = node; - search.zonecut_header = header; - search.zonecut_sigheader = NULL; - search.need_cleanup = true; - maybe_zonecut = false; - at_zonecut = true; - /* - * It is not clear if KEY should still be - * allowed at the parent side of the zone - * cut or not. It is needed for RFC3007 - * validated updates. - */ - if ((search.options & DNS_DBFIND_GLUEOK) == 0 && - type != dns_rdatatype_nsec && - type != dns_rdatatype_key) - { - /* - * Glue is not OK, but any answer we - * could return would be glue. Return - * the delegation. - */ - found = NULL; - break; - } - if (found != NULL && foundsig != NULL) { - break; - } - } - - /* - * If the NSEC3 record doesn't match the chain - * we are using behave as if it isn't here. - */ - if (header->type == dns_rdatatype_nsec3 && - !matchparams(header, &search)) - { - NODE_UNLOCK(lock, &nlocktype); - goto partial_match; - } - /* - * If we found a type we were looking for, - * remember it. - */ - if (header->type == type || type == dns_rdatatype_any || - (header->type == dns_rdatatype_cname && cname_ok)) - { - /* - * We've found the answer! - */ - found = header; - if (header->type == dns_rdatatype_cname && - cname_ok) - { - /* - * We may be finding a CNAME instead - * of the desired type. - * - * If we've already got the CNAME RRSIG, - * use it, otherwise change sigtype - * so that we find it. - */ - if (cnamesig != NULL) { - foundsig = cnamesig; - } else { - sigtype = DNS_SIGTYPE( - dns_rdatatype_cname); - } - } - /* - * If we've got all we need, end the search. - */ - if (!maybe_zonecut && foundsig != NULL) { - break; - } - } else if (header->type == sigtype) { - /* - * We've found the RRSIG rdataset for our - * target type. Remember it. - */ - foundsig = header; - /* - * If we've got all we need, end the search. - */ - if (!maybe_zonecut && found != NULL) { - break; - } - } else if (header->type == dns_rdatatype_nsec && - !search.rbtversion->havensec3) - { - /* - * Remember a NSEC rdataset even if we're - * not specifically looking for it, because - * we might need it later. - */ - nsecheader = header; - } else if (header->type == - DNS_SIGTYPE(dns_rdatatype_nsec) && - !search.rbtversion->havensec3) - { - /* - * If we need the NSEC rdataset, we'll also - * need its signature. - */ - nsecsig = header; - } else if (cname_ok && - header->type == - DNS_SIGTYPE(dns_rdatatype_cname)) - { - /* - * If we get a CNAME match, we'll also need - * its signature. - */ - cnamesig = header; - } - } - } - - if (empty_node) { - /* - * We have an exact match for the name, but there are no - * active rdatasets in the desired version. That means that - * this node doesn't exist in the desired version, and that - * we really have a partial match. - */ - if (!wild) { - NODE_UNLOCK(lock, &nlocktype); - goto partial_match; - } - } - - /* - * If we didn't find what we were looking for... - */ - if (found == NULL) { - if (search.zonecut != NULL) { - /* - * We were trying to find glue at a node beneath a - * zone cut, but didn't. - * - * Return the delegation. - */ - NODE_UNLOCK(lock, &nlocktype); - result = setup_delegation( - &search, nodep, foundname, rdataset, - sigrdataset DNS__DB_FLARG_PASS); - goto tree_exit; - } - /* - * The desired type doesn't exist. - */ - result = DNS_R_NXRRSET; - if (search.rbtversion->secure && - !search.rbtversion->havensec3 && - (nsecheader == NULL || nsecsig == NULL)) - { - /* - * The zone is secure but there's no NSEC, - * or the NSEC has no signature! - */ - if (!wild) { - result = DNS_R_BADDB; - goto node_exit; - } - - NODE_UNLOCK(lock, &nlocktype); - result = find_closest_nsec( - &search, nodep, foundname, rdataset, - sigrdataset, search.rbtdb->tree, - search.rbtversion->secure DNS__DB_FLARG_PASS); - if (result == ISC_R_SUCCESS) { - result = DNS_R_EMPTYWILD; - } - goto tree_exit; - } - if (nodep != NULL) { - dns__rbtdb_newref(search.rbtdb, node, - nlocktype DNS__DB_FLARG_PASS); - *nodep = (dns_dbnode_t *)node; - } - if ((search.rbtversion->secure && - !search.rbtversion->havensec3)) - { - dns__rbtdb_bindrdataset(search.rbtdb, node, nsecheader, - 0, nlocktype, - rdataset DNS__DB_FLARG_PASS); - if (nsecsig != NULL) { - dns__rbtdb_bindrdataset( - search.rbtdb, node, nsecsig, 0, - nlocktype, - sigrdataset DNS__DB_FLARG_PASS); - } - } - if (wild) { - foundname->attributes.wildcard = true; - } - goto node_exit; - } - - /* - * We found what we were looking for, or we found a CNAME. - */ - - if (type != found->type && type != dns_rdatatype_any && - found->type == dns_rdatatype_cname) - { - /* - * We weren't doing an ANY query and we found a CNAME instead - * of the type we were looking for, so we need to indicate - * that result to the caller. - */ - result = DNS_R_CNAME; - } else if (search.zonecut != NULL) { - /* - * If we're beneath a zone cut, we must indicate that the - * result is glue, unless we're actually at the zone cut - * and the type is NSEC or KEY. - */ - if (search.zonecut == node) { - /* - * It is not clear if KEY should still be - * allowed at the parent side of the zone - * cut or not. It is needed for RFC3007 - * validated updates. - */ - if (type == dns_rdatatype_nsec || - type == dns_rdatatype_nsec3 || - type == dns_rdatatype_key) - { - result = ISC_R_SUCCESS; - } else if (type == dns_rdatatype_any) { - result = DNS_R_ZONECUT; - } else { - result = DNS_R_GLUE; - } - } else { - result = DNS_R_GLUE; - } - } else { - /* - * An ordinary successful query! - */ - result = ISC_R_SUCCESS; - } - - if (nodep != NULL) { - if (!at_zonecut) { - dns__rbtdb_newref(search.rbtdb, node, - nlocktype DNS__DB_FLARG_PASS); - } else { - search.need_cleanup = false; - } - *nodep = (dns_dbnode_t *)node; - } - - if (type != dns_rdatatype_any) { - dns__rbtdb_bindrdataset(search.rbtdb, node, found, 0, nlocktype, - rdataset DNS__DB_FLARG_PASS); - if (foundsig != NULL) { - dns__rbtdb_bindrdataset(search.rbtdb, node, foundsig, 0, - nlocktype, - sigrdataset DNS__DB_FLARG_PASS); - } - } - - if (wild) { - foundname->attributes.wildcard = true; - } - -node_exit: - NODE_UNLOCK(lock, &nlocktype); - -tree_exit: - TREE_UNLOCK(&search.rbtdb->tree_lock, &tlocktype); - - /* - * If we found a zonecut but aren't going to use it, we have to - * let go of it. - */ - if (search.need_cleanup) { - node = search.zonecut; - INSIST(node != NULL); - lock = &(search.rbtdb->node_locks[node->locknum].lock); - - NODE_RDLOCK(lock, &nlocktype); - dns__rbtdb_decref(search.rbtdb, node, 0, &nlocktype, &tlocktype, - true, false DNS__DB_FLARG_PASS); - NODE_UNLOCK(lock, &nlocktype); - INSIST(tlocktype == isc_rwlocktype_none); - } - - if (close_version) { - dns__rbtdb_closeversion(db, &version, false DNS__DB_FLARG_PASS); - } - - dns_rbtnodechain_reset(&search.chain); - - return (result); -} - -static isc_result_t -zone_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, - dns_rdatatype_t type, dns_rdatatype_t covers, - isc_stdtime_t now, dns_rdataset_t *rdataset, - dns_rdataset_t *sigrdataset DNS__DB_FLARG) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node; - dns_slabheader_t *header = NULL, *header_next = NULL; - dns_slabheader_t *found = NULL, *foundsig = NULL; - uint32_t serial; - dns_rbtdb_version_t *rbtversion = (dns_rbtdb_version_t *)version; - bool close_version = false; - dns_typepair_t matchtype, sigmatchtype; - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - - REQUIRE(VALID_RBTDB(rbtdb)); - REQUIRE(type != dns_rdatatype_any); - INSIST(rbtversion == NULL || rbtversion->rbtdb == rbtdb); - - if (rbtversion == NULL) { - dns__rbtdb_currentversion( - db, (dns_dbversion_t **)(void *)(&rbtversion)); - close_version = true; - } - serial = rbtversion->serial; - now = 0; - - NODE_RDLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype); - - matchtype = DNS_TYPEPAIR_VALUE(type, covers); - if (covers == 0) { - sigmatchtype = DNS_SIGTYPE(type); - } else { - sigmatchtype = 0; - } - - for (header = rbtnode->data; header != NULL; header = header_next) { - header_next = header->next; - do { - if (header->serial <= serial && !IGNORE(header)) { - /* - * Is this a "this rdataset doesn't - * exist" record? - */ - if (NONEXISTENT(header)) { - header = NULL; - } - break; - } else { - header = header->down; - } - } while (header != NULL); - if (header != NULL) { - /* - * We have an active, extant rdataset. If it's a - * type we're looking for, remember it. - */ - if (header->type == matchtype) { - found = header; - if (foundsig != NULL) { - break; - } - } else if (header->type == sigmatchtype) { - foundsig = header; - if (found != NULL) { - break; - } - } - } - } - if (found != NULL) { - dns__rbtdb_bindrdataset(rbtdb, rbtnode, found, now, - isc_rwlocktype_read, - rdataset DNS__DB_FLARG_PASS); - if (foundsig != NULL) { - dns__rbtdb_bindrdataset(rbtdb, rbtnode, foundsig, now, - isc_rwlocktype_read, - sigrdataset DNS__DB_FLARG_PASS); - } - } - - NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype); - - if (close_version) { - dns__rbtdb_closeversion( - db, (dns_dbversion_t **)(void *)(&rbtversion), - false DNS__DB_FLARG_PASS); - } - - if (found == NULL) { - return (ISC_R_NOTFOUND); - } - - return (ISC_R_SUCCESS); -} - -static bool -delegating_type(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, dns_typepair_t type) { - if (type == dns_rdatatype_dname || - (type == dns_rdatatype_ns && - (node != rbtdb->origin_node || IS_STUB(rbtdb)))) - { - return (true); - } - return (false); -} - -/* - * load a non-NSEC3 node in the main tree and optionally to the auxiliary NSEC - */ -static isc_result_t -loadnode(dns_rbtdb_t *rbtdb, const dns_name_t *name, dns_rbtnode_t **nodep, - bool hasnsec) { - isc_result_t noderesult, nsecresult, tmpresult; - dns_rbtnode_t *nsecnode = NULL, *node = NULL; - - noderesult = dns_rbt_addnode(rbtdb->tree, name, &node); - if (!hasnsec) { - goto done; - } - if (noderesult == ISC_R_EXISTS) { - /* - * Add a node to the auxiliary NSEC tree for an old node - * just now getting an NSEC record. - */ - if (node->nsec == DNS_DB_NSEC_HAS_NSEC) { - goto done; - } - } else if (noderesult != ISC_R_SUCCESS) { - goto done; - } - - /* - * Build the auxiliary tree for NSECs as we go. - * This tree speeds searches for closest NSECs that would otherwise - * need to examine many irrelevant nodes in large TLDs. - * - * Add nodes to the auxiliary tree after corresponding nodes have - * been added to the main tree. - */ - nsecresult = dns_rbt_addnode(rbtdb->nsec, name, &nsecnode); - if (nsecresult == ISC_R_SUCCESS) { - nsecnode->nsec = DNS_DB_NSEC_NSEC; - node->nsec = DNS_DB_NSEC_HAS_NSEC; - goto done; - } - - if (nsecresult == ISC_R_EXISTS) { -#if 1 /* 0 */ - isc_log_write(DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, - ISC_LOG_WARNING, - "addnode: NSEC node already exists"); -#endif /* if 1 */ - node->nsec = DNS_DB_NSEC_HAS_NSEC; - goto done; - } - - if (noderesult == ISC_R_SUCCESS) { - /* - * Remove the node we just added above. - */ - tmpresult = dns_rbt_deletenode(rbtdb->tree, node, false); - if (tmpresult != ISC_R_SUCCESS) { - isc_log_write(DNS_LOGCATEGORY_DATABASE, - DNS_LOGMODULE_CACHE, ISC_LOG_WARNING, - "loading_addrdataset: " - "dns_rbt_deletenode: %s after " - "dns_rbt_addnode(NSEC): %s", - isc_result_totext(tmpresult), - isc_result_totext(noderesult)); - } - } - - /* - * Set the error condition to be returned. - */ - noderesult = nsecresult; - -done: - if (noderesult == ISC_R_SUCCESS || noderesult == ISC_R_EXISTS) { - *nodep = node; - } - - return (noderesult); -} - -static isc_result_t -loading_addrdataset(void *arg, const dns_name_t *name, - dns_rdataset_t *rdataset DNS__DB_FLARG) { - rbtdb_load_t *loadctx = arg; - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)loadctx->db; - dns_rbtnode_t *node = NULL; - isc_result_t result; - isc_region_t region; - dns_slabheader_t *newheader = NULL; - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - - REQUIRE(rdataset->rdclass == rbtdb->common.rdclass); - - /* - * SOA records are only allowed at top of zone. - */ - if (rdataset->type == dns_rdatatype_soa && - !dns_name_equal(name, &rbtdb->common.origin)) - { - return (DNS_R_NOTZONETOP); - } - - if (rdataset->type != dns_rdatatype_nsec3 && - rdataset->covers != dns_rdatatype_nsec3) - { - dns__zonerbt_addwildcards(rbtdb, name, false); - } - - if (dns_name_iswildcard(name)) { - /* - * NS record owners cannot legally be wild cards. - */ - if (rdataset->type == dns_rdatatype_ns) { - return (DNS_R_INVALIDNS); - } - /* - * NSEC3 record owners cannot legally be wild cards. - */ - if (rdataset->type == dns_rdatatype_nsec3) { - return (DNS_R_INVALIDNSEC3); - } - result = dns__zonerbt_wildcardmagic(rbtdb, name, false); - if (result != ISC_R_SUCCESS) { - return (result); - } - } - - if (rdataset->type == dns_rdatatype_nsec3 || - rdataset->covers == dns_rdatatype_nsec3) - { - result = dns_rbt_addnode(rbtdb->nsec3, name, &node); - if (result == ISC_R_SUCCESS) { - node->nsec = DNS_DB_NSEC_NSEC3; - } - } else if (rdataset->type == dns_rdatatype_nsec) { - result = loadnode(rbtdb, name, &node, true); - } else { - result = loadnode(rbtdb, name, &node, false); - } - if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS) { - return (result); - } - if (result == ISC_R_SUCCESS) { - node->locknum = node->hashval % rbtdb->node_lock_count; - } - - result = dns_rdataslab_fromrdataset(rdataset, rbtdb->common.mctx, - ®ion, sizeof(dns_slabheader_t), - rbtdb->maxrrperset); - if (result != ISC_R_SUCCESS) { - return (result); - } - newheader = (dns_slabheader_t *)region.base; - *newheader = (dns_slabheader_t){ - .type = DNS_TYPEPAIR_VALUE(rdataset->type, rdataset->covers), - .ttl = rdataset->ttl + loadctx->now, - .trust = rdataset->trust, - .node = (dns_dbnode_t *)node, - .serial = 1, - .count = 1, - }; - - dns_slabheader_reset(newheader, (dns_db_t *)rbtdb, - (dns_dbnode_t *)node); - dns_slabheader_setownercase(newheader, name); - - if ((rdataset->attributes & DNS_RDATASETATTR_RESIGN) != 0) { - DNS_SLABHEADER_SETATTR(newheader, DNS_SLABHEADERATTR_RESIGN); - newheader->resign = - (isc_stdtime_t)(dns_time64_from32(rdataset->resign) >> - 1); - newheader->resign_lsb = rdataset->resign & 0x1; - } - - NODE_WRLOCK(&rbtdb->node_locks[node->locknum].lock, &nlocktype); - result = dns__rbtdb_add(rbtdb, node, name, rbtdb->current_version, - newheader, DNS_DBADD_MERGE, true, NULL, - 0 DNS__DB_FLARG_PASS); - NODE_UNLOCK(&rbtdb->node_locks[node->locknum].lock, &nlocktype); - - if (result == ISC_R_SUCCESS && - delegating_type(rbtdb, node, rdataset->type)) - { - node->find_callback = 1; - } else if (result == DNS_R_UNCHANGED) { - result = ISC_R_SUCCESS; - } - - return (result); -} - -static isc_result_t -beginload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) { - rbtdb_load_t *loadctx = NULL; - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - - REQUIRE(DNS_CALLBACK_VALID(callbacks)); - REQUIRE(VALID_RBTDB(rbtdb)); - - loadctx = isc_mem_get(rbtdb->common.mctx, sizeof(*loadctx)); - - loadctx->db = db; - loadctx->now = 0; - - RWLOCK(&rbtdb->lock, isc_rwlocktype_write); - - REQUIRE((rbtdb->attributes & - (RBTDB_ATTR_LOADED | RBTDB_ATTR_LOADING)) == 0); - rbtdb->attributes |= RBTDB_ATTR_LOADING; - - RWUNLOCK(&rbtdb->lock, isc_rwlocktype_write); - - callbacks->add = loading_addrdataset; - callbacks->add_private = loadctx; - - return (ISC_R_SUCCESS); -} - -static isc_result_t -endload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) { - rbtdb_load_t *loadctx = NULL; - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - - REQUIRE(VALID_RBTDB(rbtdb)); - REQUIRE(DNS_CALLBACK_VALID(callbacks)); - loadctx = callbacks->add_private; - REQUIRE(loadctx != NULL); - REQUIRE(loadctx->db == db); - - RWLOCK(&rbtdb->lock, isc_rwlocktype_write); - - REQUIRE((rbtdb->attributes & RBTDB_ATTR_LOADING) != 0); - REQUIRE((rbtdb->attributes & RBTDB_ATTR_LOADED) == 0); - - rbtdb->attributes &= ~RBTDB_ATTR_LOADING; - rbtdb->attributes |= RBTDB_ATTR_LOADED; - - /* - * If there's a KEY rdataset at the zone origin containing a - * zone key, we consider the zone secure. - */ - if (rbtdb->origin_node != NULL) { - dns_rbtdb_version_t *rbtversion = rbtdb->current_version; - RWUNLOCK(&rbtdb->lock, isc_rwlocktype_write); - dns__rbtdb_setsecure(db, rbtversion, - (dns_dbnode_t *)rbtdb->origin_node); - } else { - RWUNLOCK(&rbtdb->lock, isc_rwlocktype_write); - } - - callbacks->add = NULL; - callbacks->add_private = NULL; - - isc_mem_put(rbtdb->common.mctx, loadctx, sizeof(*loadctx)); - - return (ISC_R_SUCCESS); -} - -static bool -issecure(dns_db_t *db) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - bool secure; - - REQUIRE(VALID_RBTDB(rbtdb)); - - RWLOCK(&rbtdb->lock, isc_rwlocktype_read); - secure = rbtdb->current_version->secure; - RWUNLOCK(&rbtdb->lock, isc_rwlocktype_read); - - return (secure); -} - -static isc_result_t -getnsec3parameters(dns_db_t *db, dns_dbversion_t *version, dns_hash_t *hash, - uint8_t *flags, uint16_t *iterations, unsigned char *salt, - size_t *salt_length) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - isc_result_t result = ISC_R_NOTFOUND; - dns_rbtdb_version_t *rbtversion = (dns_rbtdb_version_t *)version; - - REQUIRE(VALID_RBTDB(rbtdb)); - INSIST(rbtversion == NULL || rbtversion->rbtdb == rbtdb); - - RWLOCK(&rbtdb->lock, isc_rwlocktype_read); - if (rbtversion == NULL) { - rbtversion = rbtdb->current_version; - } - - if (rbtversion->havensec3) { - if (hash != NULL) { - *hash = rbtversion->hash; - } - if (salt != NULL && salt_length != NULL) { - REQUIRE(*salt_length >= rbtversion->salt_length); - memmove(salt, rbtversion->salt, - rbtversion->salt_length); - } - if (salt_length != NULL) { - *salt_length = rbtversion->salt_length; - } - if (iterations != NULL) { - *iterations = rbtversion->iterations; - } - if (flags != NULL) { - *flags = rbtversion->flags; - } - result = ISC_R_SUCCESS; - } - RWUNLOCK(&rbtdb->lock, isc_rwlocktype_read); - - return (result); -} - -static isc_result_t -getsize(dns_db_t *db, dns_dbversion_t *version, uint64_t *records, - uint64_t *xfrsize) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - isc_result_t result = ISC_R_SUCCESS; - dns_rbtdb_version_t *rbtversion = (dns_rbtdb_version_t *)version; - - REQUIRE(VALID_RBTDB(rbtdb)); - INSIST(rbtversion == NULL || rbtversion->rbtdb == rbtdb); - - RWLOCK(&rbtdb->lock, isc_rwlocktype_read); - if (rbtversion == NULL) { - rbtversion = rbtdb->current_version; - } - - RWLOCK(&rbtversion->rwlock, isc_rwlocktype_read); - SET_IF_NOT_NULL(records, rbtversion->records); - - SET_IF_NOT_NULL(xfrsize, rbtversion->xfrsize); - RWUNLOCK(&rbtversion->rwlock, isc_rwlocktype_read); - RWUNLOCK(&rbtdb->lock, isc_rwlocktype_read); - - return (result); -} - -static isc_result_t -setsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, isc_stdtime_t resign) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - dns_slabheader_t *header, oldheader; - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - - REQUIRE(VALID_RBTDB(rbtdb)); - REQUIRE(!IS_CACHE(rbtdb)); - REQUIRE(rdataset != NULL); - REQUIRE(rdataset->methods == &dns_rdataslab_rdatasetmethods); - - header = dns_slabheader_fromrdataset(rdataset); - - NODE_WRLOCK(&rbtdb->node_locks[RBTDB_HEADERNODE(header)->locknum].lock, - &nlocktype); - - oldheader = *header; - - /* - * Only break the heap invariant (by adjusting resign and resign_lsb) - * if we are going to be restoring it by calling isc_heap_increased - * or isc_heap_decreased. - */ - if (resign != 0) { - header->resign = (isc_stdtime_t)(dns_time64_from32(resign) >> - 1); - header->resign_lsb = resign & 0x1; - } - if (header->heap_index != 0) { - INSIST(RESIGN(header)); - if (resign == 0) { - isc_heap_delete( - rbtdb->heaps[RBTDB_HEADERNODE(header)->locknum], - header->heap_index); - header->heap_index = 0; - header->heap = NULL; - } else if (rbtdb->sooner(header, &oldheader)) { - isc_heap_increased( - rbtdb->heaps[RBTDB_HEADERNODE(header)->locknum], - header->heap_index); - } else if (rbtdb->sooner(&oldheader, header)) { - isc_heap_decreased( - rbtdb->heaps[RBTDB_HEADERNODE(header)->locknum], - header->heap_index); - } - } else if (resign != 0) { - DNS_SLABHEADER_SETATTR(header, DNS_SLABHEADERATTR_RESIGN); - dns__zonerbt_resigninsert( - rbtdb, RBTDB_HEADERNODE(header)->locknum, header); - } - NODE_UNLOCK(&rbtdb->node_locks[RBTDB_HEADERNODE(header)->locknum].lock, - &nlocktype); - return (ISC_R_SUCCESS); -} - -static isc_result_t -getsigningtime(dns_db_t *db, isc_stdtime_t *resign, dns_name_t *foundname, - dns_typepair_t *typepair) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - dns_slabheader_t *header = NULL, *this = NULL; - unsigned int i; - isc_result_t result = ISC_R_NOTFOUND; - unsigned int locknum = 0; - isc_rwlocktype_t tlocktype = isc_rwlocktype_none; - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - - REQUIRE(VALID_RBTDB(rbtdb)); - REQUIRE(resign != NULL); - REQUIRE(foundname != NULL); - REQUIRE(typepair != NULL); - - TREE_RDLOCK(&rbtdb->tree_lock, &tlocktype); - - for (i = 0; i < rbtdb->node_lock_count; i++) { - NODE_RDLOCK(&rbtdb->node_locks[i].lock, &nlocktype); - - /* - * Find for the earliest signing time among all of the - * heaps, each of which is covered by a different bucket - * lock. - */ - this = isc_heap_element(rbtdb->heaps[i], 1); - if (this == NULL) { - /* Nothing found; unlock and try the next heap. */ - NODE_UNLOCK(&rbtdb->node_locks[i].lock, &nlocktype); - continue; - } - - if (header == NULL) { - /* - * Found a signing time: retain the bucket lock and - * preserve the lock number so we can unlock it - * later. - */ - header = this; - locknum = i; - nlocktype = isc_rwlocktype_none; - } else if (rbtdb->sooner(this, header)) { - /* - * Found an earlier signing time; release the - * previous bucket lock and retain this one instead. - */ - NODE_UNLOCK(&rbtdb->node_locks[locknum].lock, - &nlocktype); - header = this; - locknum = i; - } else { - /* - * Earliest signing time in this heap isn't - * an improvement; unlock and try the next heap. - */ - NODE_UNLOCK(&rbtdb->node_locks[i].lock, &nlocktype); - } - } - - if (header != NULL) { - nlocktype = isc_rwlocktype_read; - /* - * Found something; pass back the answer and unlock - * the bucket. - */ - *resign = RESIGN(header) - ? (header->resign << 1) | header->resign_lsb - : 0; - dns_rbt_fullnamefromnode(RBTDB_HEADERNODE(header), foundname); - *typepair = header->type; - - NODE_UNLOCK(&rbtdb->node_locks[locknum].lock, &nlocktype); - - result = ISC_R_SUCCESS; - } - - TREE_UNLOCK(&rbtdb->tree_lock, &tlocktype); - - return (result); -} - -static isc_result_t -setgluecachestats(dns_db_t *db, isc_stats_t *stats) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - - REQUIRE(VALID_RBTDB(rbtdb)); - REQUIRE(!IS_CACHE(rbtdb) && !IS_STUB(rbtdb)); - REQUIRE(stats != NULL); - - isc_stats_attach(stats, &rbtdb->gluecachestats); - return (ISC_R_SUCCESS); -} - -static dns_glue_t * -new_gluelist(isc_mem_t *mctx, dns_name_t *name) { - dns_glue_t *glue = isc_mem_get(mctx, sizeof(*glue)); - *glue = (dns_glue_t){ 0 }; - dns_name_t *gluename = dns_fixedname_initname(&glue->fixedname); - - dns_name_copy(name, gluename); - - return (glue); -} - -static isc_result_t -glue_nsdname_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype, - dns_rdataset_t *unused DNS__DB_FLARG) { - dns_glue_additionaldata_ctx_t *ctx = NULL; - isc_result_t result; - dns_fixedname_t fixedname_a; - dns_name_t *name_a = NULL; - dns_rdataset_t rdataset_a, sigrdataset_a; - dns_rbtnode_t *node_a = NULL; - dns_fixedname_t fixedname_aaaa; - dns_name_t *name_aaaa = NULL; - dns_rdataset_t rdataset_aaaa, sigrdataset_aaaa; - dns_rbtnode_t *node_aaaa = NULL; - dns_glue_t *glue = NULL; - - UNUSED(unused); - - /* - * NS records want addresses in additional records. - */ - INSIST(qtype == dns_rdatatype_a); - - ctx = (dns_glue_additionaldata_ctx_t *)arg; - - name_a = dns_fixedname_initname(&fixedname_a); - dns_rdataset_init(&rdataset_a); - dns_rdataset_init(&sigrdataset_a); - - name_aaaa = dns_fixedname_initname(&fixedname_aaaa); - dns_rdataset_init(&rdataset_aaaa); - dns_rdataset_init(&sigrdataset_aaaa); - - result = zone_find(ctx->db, name, ctx->version, dns_rdatatype_a, - DNS_DBFIND_GLUEOK, 0, (dns_dbnode_t **)&node_a, - name_a, &rdataset_a, - &sigrdataset_a DNS__DB_FLARG_PASS); - if (result == DNS_R_GLUE) { - glue = new_gluelist(ctx->db->mctx, name_a); - - dns_rdataset_init(&glue->rdataset_a); - dns_rdataset_init(&glue->sigrdataset_a); - dns_rdataset_init(&glue->rdataset_aaaa); - dns_rdataset_init(&glue->sigrdataset_aaaa); - - dns_rdataset_clone(&rdataset_a, &glue->rdataset_a); - if (dns_rdataset_isassociated(&sigrdataset_a)) { - dns_rdataset_clone(&sigrdataset_a, - &glue->sigrdataset_a); - } - } - - result = zone_find(ctx->db, name, ctx->version, dns_rdatatype_aaaa, - DNS_DBFIND_GLUEOK, 0, (dns_dbnode_t **)&node_aaaa, - name_aaaa, &rdataset_aaaa, - &sigrdataset_aaaa DNS__DB_FLARG_PASS); - if (result == DNS_R_GLUE) { - if (glue == NULL) { - glue = new_gluelist(ctx->db->mctx, name_aaaa); - - dns_rdataset_init(&glue->rdataset_a); - dns_rdataset_init(&glue->sigrdataset_a); - dns_rdataset_init(&glue->rdataset_aaaa); - dns_rdataset_init(&glue->sigrdataset_aaaa); - } else { - INSIST(node_a == node_aaaa); - INSIST(dns_name_equal(name_a, name_aaaa)); - } - - dns_rdataset_clone(&rdataset_aaaa, &glue->rdataset_aaaa); - if (dns_rdataset_isassociated(&sigrdataset_aaaa)) { - dns_rdataset_clone(&sigrdataset_aaaa, - &glue->sigrdataset_aaaa); - } - } - - /* - * If the currently processed NS record is in-bailiwick, mark any glue - * RRsets found for it with DNS_RDATASETATTR_REQUIRED. Note that for - * simplicity, glue RRsets for all in-bailiwick NS records are marked - * this way, even though dns_message_rendersection() only checks the - * attributes for the first rdataset associated with the first name - * added to the ADDITIONAL section. - */ - if (glue != NULL && dns_name_issubdomain(name, ctx->nodename)) { - if (dns_rdataset_isassociated(&glue->rdataset_a)) { - glue->rdataset_a.attributes |= - DNS_RDATASETATTR_REQUIRED; - } - if (dns_rdataset_isassociated(&glue->rdataset_aaaa)) { - glue->rdataset_aaaa.attributes |= - DNS_RDATASETATTR_REQUIRED; - } - } - - if (glue != NULL) { - glue->next = ctx->glue_list; - ctx->glue_list = glue; - } - - result = ISC_R_SUCCESS; - - if (dns_rdataset_isassociated(&rdataset_a)) { - dns_rdataset_disassociate(&rdataset_a); - } - if (dns_rdataset_isassociated(&sigrdataset_a)) { - dns_rdataset_disassociate(&sigrdataset_a); - } - - if (dns_rdataset_isassociated(&rdataset_aaaa)) { - dns_rdataset_disassociate(&rdataset_aaaa); - } - if (dns_rdataset_isassociated(&sigrdataset_aaaa)) { - dns_rdataset_disassociate(&sigrdataset_aaaa); - } - - if (node_a != NULL) { - dns__db_detachnode(ctx->db, - (dns_dbnode_t **)&node_a DNS__DB_FLARG_PASS); - } - if (node_aaaa != NULL) { - dns__db_detachnode( - ctx->db, - (dns_dbnode_t **)&node_aaaa DNS__DB_FLARG_PASS); - } - - return (result); -} - -#define IS_REQUIRED_GLUE(r) (((r)->attributes & DNS_RDATASETATTR_REQUIRED) != 0) - -static void -addglue_to_message(dns_glue_t *ge, dns_message_t *msg) { - for (; ge != NULL; ge = ge->next) { - dns_name_t *name = NULL; - dns_rdataset_t *rdataset_a = NULL; - dns_rdataset_t *sigrdataset_a = NULL; - dns_rdataset_t *rdataset_aaaa = NULL; - dns_rdataset_t *sigrdataset_aaaa = NULL; - dns_name_t *gluename = dns_fixedname_name(&ge->fixedname); - bool prepend_name = false; - - dns_message_gettempname(msg, &name); - - dns_name_copy(gluename, name); - - if (dns_rdataset_isassociated(&ge->rdataset_a)) { - dns_message_gettemprdataset(msg, &rdataset_a); - } - - if (dns_rdataset_isassociated(&ge->sigrdataset_a)) { - dns_message_gettemprdataset(msg, &sigrdataset_a); - } - - if (dns_rdataset_isassociated(&ge->rdataset_aaaa)) { - dns_message_gettemprdataset(msg, &rdataset_aaaa); - } - - if (dns_rdataset_isassociated(&ge->sigrdataset_aaaa)) { - dns_message_gettemprdataset(msg, &sigrdataset_aaaa); - } - - if (rdataset_a != NULL) { - dns_rdataset_clone(&ge->rdataset_a, rdataset_a); - ISC_LIST_APPEND(name->list, rdataset_a, link); - if (IS_REQUIRED_GLUE(rdataset_a)) { - prepend_name = true; - } - } - - if (sigrdataset_a != NULL) { - dns_rdataset_clone(&ge->sigrdataset_a, sigrdataset_a); - ISC_LIST_APPEND(name->list, sigrdataset_a, link); - } - - if (rdataset_aaaa != NULL) { - dns_rdataset_clone(&ge->rdataset_aaaa, rdataset_aaaa); - ISC_LIST_APPEND(name->list, rdataset_aaaa, link); - if (IS_REQUIRED_GLUE(rdataset_aaaa)) { - prepend_name = true; - } - } - if (sigrdataset_aaaa != NULL) { - dns_rdataset_clone(&ge->sigrdataset_aaaa, - sigrdataset_aaaa); - ISC_LIST_APPEND(name->list, sigrdataset_aaaa, link); - } - - dns_message_addname(msg, name, DNS_SECTION_ADDITIONAL); - - /* - * When looking for required glue, dns_message_rendersection() - * only processes the first rdataset associated with the first - * name added to the ADDITIONAL section. dns_message_addname() - * performs an append on the list of names in a given section, - * so if any glue record was marked as required, we need to - * move the name it is associated with to the beginning of the - * list for the ADDITIONAL section or else required glue might - * not be rendered. - */ - if (prepend_name) { - ISC_LIST_UNLINK(msg->sections[DNS_SECTION_ADDITIONAL], - name, link); - ISC_LIST_PREPEND(msg->sections[DNS_SECTION_ADDITIONAL], - name, link); - } - } -} - -static dns_glue_t * -newglue(dns_db_t *db, dns_dbversion_t *dbversion, dns_rbtnode_t *node, - dns_rdataset_t *rdataset) { - dns_fixedname_t nodename; - dns_glue_additionaldata_ctx_t ctx = { - .db = db, - .version = dbversion, - .nodename = dns_fixedname_initname(&nodename), - }; - - /* - * Get the owner name of the NS RRset - it will be necessary for - * identifying required glue in glue_nsdname_cb() (by - * determining which NS records in the delegation are - * in-bailiwick). - */ - dns__rbtdb_nodefullname(db, (dns_dbnode_t *)node, ctx.nodename); - - (void)dns_rdataset_additionaldata(rdataset, dns_rootname, - glue_nsdname_cb, &ctx); - - return (ctx.glue_list); -} - -/* FIXME: Perhaps we can squash dns_gluenode_t with - * dns_glue_additionaldata_ctx_t */ - -static dns_gluenode_t * -new_gluenode(dns_db_t *db, dns_dbversion_t *dbversion, dns_rbtnode_t *node, - dns_rdataset_t *rdataset) { - dns_gluenode_t *gluenode = isc_mem_get(db->mctx, sizeof(*gluenode)); - *gluenode = (dns_gluenode_t){ - .glue = newglue(db, dbversion, node, rdataset), - .db = db, - }; - - isc_mem_attach(db->mctx, &gluenode->mctx); - dns_db_attachnode(db, (dns_dbnode_t *)node, - (dns_dbnode_t **)&gluenode->node); - - return (gluenode); -} - -static uint32_t -rbtnode_hash(const dns_rbtnode_t *node) { - uintptr_t key = (uintptr_t)node; - return (isc_hash32(&key, sizeof(key), true)); -} - -static int -rbtnode_match(struct cds_lfht_node *ht_node, const void *key) { - const dns_gluenode_t *gluenode = - caa_container_of(ht_node, dns_gluenode_t, ht_node); - const dns_rbtnode_t *node = key; - - return (gluenode->node == node); -} - -static uint32_t -gluenode_hash(const dns_gluenode_t *gluenode) { - return (rbtnode_hash(gluenode->node)); -} - -static int -gluenode_match(struct cds_lfht_node *ht_node, const void *key) { - const dns_gluenode_t *gluenode = key; - - return (rbtnode_match(ht_node, gluenode->node)); -} - -static void -addglue(dns_db_t *db, dns_dbversion_t *dbversion, dns_rdataset_t *rdataset, - dns_message_t *msg) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - dns_rbtdb_version_t *version = (dns_rbtdb_version_t *)dbversion; - dns_rbtnode_t *node = (dns_rbtnode_t *)rdataset->slab.node; - dns_gluenode_t *gluenode = NULL; - - REQUIRE(rdataset->type == dns_rdatatype_ns); - REQUIRE(rbtdb == (dns_rbtdb_t *)rdataset->slab.db); - REQUIRE(rbtdb == version->rbtdb); - REQUIRE(!IS_CACHE(rbtdb) && !IS_STUB(rbtdb)); - - /* - * The glue table cache that forms a part of the DB version - * structure is not explicitly bounded and there's no cache - * cleaning. The zone data size itself is an implicit bound. - * - * The key into the glue hashtable is the node pointer. This is - * because the glue hashtable is a property of the DB version, - * and the glue is keyed for the ownername/NS tuple. We don't - * bother with using an expensive dns_name_t comparison here as - * the node pointer is a fixed value that won't change for a DB - * version and can be compared directly. - */ - - rcu_read_lock(); - - struct cds_lfht_iter iter; - cds_lfht_lookup(version->glue_table, rbtnode_hash(node), rbtnode_match, - node, &iter); - - gluenode = cds_lfht_entry(cds_lfht_iter_get_node(&iter), dns_gluenode_t, - ht_node); - if (gluenode == NULL) { - /* No cached glue was found in the table. Get new glue. */ - gluenode = new_gluenode(db, dbversion, node, rdataset); - - struct cds_lfht_node *ht_node = cds_lfht_add_unique( - version->glue_table, gluenode_hash(gluenode), - gluenode_match, gluenode, &gluenode->ht_node); - - if (ht_node != &gluenode->ht_node) { - dns__rbtdb_free_gluenode_rcu(&gluenode->rcu_head); - - gluenode = cds_lfht_entry(ht_node, dns_gluenode_t, - ht_node); - } - } - - INSIST(gluenode != NULL); - - dns_glue_t *glue = gluenode->glue; - isc_statscounter_t counter = dns_gluecachestatscounter_hits_present; - - if (glue != NULL) { - /* We have a cached result. Add it to the message and return. */ - addglue_to_message(glue, msg); - } else { - counter = dns_gluecachestatscounter_hits_absent; - } - - rcu_read_unlock(); - - if (rbtdb->gluecachestats != NULL) { - isc_stats_increment(rbtdb->gluecachestats, counter); - } -} - -dns_dbmethods_t dns__rbtdb_zonemethods = { - .destroy = dns__rbtdb_destroy, - .beginload = beginload, - .endload = endload, - .currentversion = dns__rbtdb_currentversion, - .newversion = dns__rbtdb_newversion, - .attachversion = dns__rbtdb_attachversion, - .closeversion = dns__rbtdb_closeversion, - .findnode = dns__rbtdb_findnode, - .find = zone_find, - .attachnode = dns__rbtdb_attachnode, - .detachnode = dns__rbtdb_detachnode, - .createiterator = dns__rbtdb_createiterator, - .findrdataset = zone_findrdataset, - .allrdatasets = dns__rbtdb_allrdatasets, - .addrdataset = dns__rbtdb_addrdataset, - .subtractrdataset = dns__rbtdb_subtractrdataset, - .deleterdataset = dns__rbtdb_deleterdataset, - .issecure = issecure, - .nodecount = dns__rbtdb_nodecount, - .setloop = dns__rbtdb_setloop, - .getoriginnode = dns__rbtdb_getoriginnode, - .getnsec3parameters = getnsec3parameters, - .findnsec3node = findnsec3node, - .setsigningtime = setsigningtime, - .getsigningtime = getsigningtime, - .getsize = getsize, - .setgluecachestats = setgluecachestats, - .locknode = dns__rbtdb_locknode, - .unlocknode = dns__rbtdb_unlocknode, - .addglue = addglue, - .deletedata = dns__rbtdb_deletedata, - .nodefullname = dns__rbtdb_nodefullname, - .setmaxrrperset = dns__rbtdb_setmaxrrperset, - .setmaxtypepername = dns__rbtdb_setmaxtypepername, -}; - -void -dns__zonerbt_resigninsert(dns_rbtdb_t *rbtdb, int idx, - dns_slabheader_t *newheader) { - INSIST(!IS_CACHE(rbtdb)); - INSIST(newheader->heap_index == 0); - INSIST(!ISC_LINK_LINKED(newheader, link)); - - isc_heap_insert(rbtdb->heaps[idx], newheader); - newheader->heap = rbtdb->heaps[idx]; -} - -void -dns__zonerbt_resigndelete(dns_rbtdb_t *rbtdb, dns_rbtdb_version_t *version, - dns_slabheader_t *header DNS__DB_FLARG) { - /* - * Remove the old header from the heap - */ - if (header != NULL && header->heap_index != 0) { - isc_heap_delete(rbtdb->heaps[RBTDB_HEADERNODE(header)->locknum], - header->heap_index); - header->heap_index = 0; - if (version != NULL) { - dns__rbtdb_newref( - rbtdb, RBTDB_HEADERNODE(header), - isc_rwlocktype_write DNS__DB_FLARG_PASS); - ISC_LIST_APPEND(version->resigned_list, header, link); - } - } -} - -isc_result_t -dns__zonerbt_wildcardmagic(dns_rbtdb_t *rbtdb, const dns_name_t *name, - bool lock) { - isc_result_t result; - dns_name_t foundname; - dns_offsets_t offsets; - unsigned int n; - dns_rbtnode_t *node = NULL; - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - - dns_name_init(&foundname, offsets); - n = dns_name_countlabels(name); - INSIST(n >= 2); - n--; - dns_name_getlabelsequence(name, 1, n, &foundname); - result = dns_rbt_addnode(rbtdb->tree, &foundname, &node); - if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS) { - return (result); - } - if (result == ISC_R_SUCCESS) { - node->nsec = DNS_DB_NSEC_NORMAL; - } - node->find_callback = 1; - if (lock) { - NODE_WRLOCK(&rbtdb->node_locks[node->locknum].lock, &nlocktype); - } - node->wild = 1; - if (lock) { - NODE_UNLOCK(&rbtdb->node_locks[node->locknum].lock, &nlocktype); - } - return (ISC_R_SUCCESS); -} - -isc_result_t -dns__zonerbt_addwildcards(dns_rbtdb_t *rbtdb, const dns_name_t *name, - bool lock) { - isc_result_t result; - dns_name_t foundname; - dns_offsets_t offsets; - unsigned int n, l, i; - - dns_name_init(&foundname, offsets); - n = dns_name_countlabels(name); - l = dns_name_countlabels(&rbtdb->common.origin); - i = l + 1; - while (i < n) { - dns_rbtnode_t *node = NULL; - dns_name_getlabelsequence(name, n - i, i, &foundname); - if (dns_name_iswildcard(&foundname)) { - result = dns__zonerbt_wildcardmagic(rbtdb, &foundname, - lock); - if (result != ISC_R_SUCCESS) { - return (result); - } - result = dns_rbt_addnode(rbtdb->tree, &foundname, - &node); - if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS) { - return (result); - } - if (result == ISC_R_SUCCESS) { - node->nsec = DNS_DB_NSEC_NORMAL; - } - } - i++; - } - return (ISC_R_SUCCESS); -} diff --git a/lib/dns/rbt.c b/lib/dns/rbt.c deleted file mode 100644 index 1c7cddc685..0000000000 --- a/lib/dns/rbt.c +++ /dev/null @@ -1,2943 +0,0 @@ -/* - * Copyright (C) Internet Systems Consortium, Inc. ("ISC") - * - * SPDX-License-Identifier: MPL-2.0 - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, you can obtain one at https://mozilla.org/MPL/2.0/. - * - * See the COPYRIGHT file distributed with this work for additional - * information regarding copyright ownership. - */ - -/*! \file */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define CHECK(x) \ - do { \ - result = (x); \ - if (result != ISC_R_SUCCESS) \ - goto cleanup; \ - } while (0) - -#define RBT_MAGIC ISC_MAGIC('R', 'B', 'T', '+') -#define VALID_RBT(rbt) ISC_MAGIC_VALID(rbt, RBT_MAGIC) - -/* - * XXXDCL Since parent pointers were added in again, I could remove all of the - * chain junk, and replace with dns_rbt_firstnode, _previousnode, _nextnode, - * _lastnode. This would involve pretty major change to the API. - */ -#define CHAIN_MAGIC ISC_MAGIC('0', '-', '0', '-') -#define VALID_CHAIN(chain) ISC_MAGIC_VALID(chain, CHAIN_MAGIC) - -#define RBT_HASH_NEXTTABLE(hindex) ((hindex == 0) ? 1 : 0) - -struct dns_rbt { - unsigned int magic; - isc_mem_t *mctx; - dns_rbtnode_t *root; - void (*data_deleter)(void *, void *); - void *deleter_arg; - unsigned int nodecount; - uint8_t hashbits[2]; - dns_rbtnode_t **hashtable[2]; - uint8_t hindex; - uint32_t hiter; -}; - -#define IS_EMPTY(node) ((node)->data == NULL) - -#define WANTEMPTYDATA_OR_DATA(options, node) \ - ((options & DNS_RBTFIND_EMPTYDATA) != 0 || node->data != NULL) - -/*% - * The variable length stuff stored after the node has the following - * structure. - * - * NAME_DATA{1..255} OLDOFFSETLEN{1} OFFSETS{1..128} - * - * NAME_DATA contains the name of the node when it was created. - * OLDOFFSETLEN contains the length of OFFSETS when the node was created. - * OFFSETS contains the offsets into name for each label when the node - * was created. - */ - -#define NAME(node) ((unsigned char *)((node) + 1)) -#define OFFSETS(node) (NAME(node) + node->oldnamelen + 1) -#define OLDOFFSETLEN(node) (OFFSETS(node)[-1]) - -#define NODE_SIZE(node) \ - (sizeof(*node) + node->oldnamelen + OLDOFFSETLEN(node) + 1) - -/*% - * Color management. - */ -#define RED 0 -#define BLACK 1 -#define IS_RED(node) ((node) != NULL && (node)->color == RED) -#define IS_BLACK(node) ((node) == NULL || (node)->color == BLACK) - -/*% - * Chain management. - * - * The "ancestors" member of chains were removed, with their job now - * being wholly handled by parent pointers (which didn't exist, because - * of memory concerns, when chains were first implemented). - */ -#define ADD_LEVEL(chain, node) \ - do { \ - INSIST((chain)->level_count < DNS_RBT_LEVELBLOCK); \ - (chain)->levels[(chain)->level_count++] = (node); \ - } while (0) - -/* - * Initialize a dns_name_t that refers to a node's name. - */ -static void -node_name(dns_rbtnode_t *node, dns_name_t *name) { - name->length = node->namelen; - name->labels = node->offsetlen; - name->ndata = NAME(node); - name->offsets = OFFSETS(node); - name->attributes = (struct dns_name_attrs){ .absolute = node->absolute, - .readonly = true }; -} - -#ifdef DEBUG -/* - * A little something to help out in GDB. - */ -dns_name_t -Name(dns_rbtnode_t *node); -dns_name_t -Name(dns_rbtnode_t *node) { - dns_name_t name; - - dns_name_init(&name, NULL); - if (node != NULL) { - node_name(node, &name); - } - - return (name); -} -#endif /* DEBUG */ - -/* - * Upper node is the parent of the root of the passed node's - * subtree. The passed node must not be NULL. - */ -static dns_rbtnode_t * -get_upper_node(dns_rbtnode_t *node) { - return (node->uppernode); -} - -size_t -dns__rbtnode_getdistance(dns_rbtnode_t *node) { - size_t nodes = 1; - - while (node != NULL) { - if (node->is_root) { - break; - } - nodes++; - node = node->parent; - } - - return (nodes); -} - -/* - * Forward declarations. - */ -static dns_rbtnode_t * -rbtnode_new(isc_mem_t *mctx, const dns_name_t *name); - -static void -hashtable_new(dns_rbt_t *rbt, uint8_t index, uint8_t bits); -static void -hashtable_free(dns_rbt_t *rbt, uint8_t index); - -static void -hash_node(dns_rbt_t *rbt, dns_rbtnode_t *node, const dns_name_t *name); - -static void -unhash_node(dns_rbt_t *rbt, dns_rbtnode_t *node); - -static uint32_t -rehash_bits(dns_rbt_t *rbt, size_t newcount); -static void -hashtable_rehash(dns_rbt_t *rbt, uint32_t newbits); -static void -hashtable_rehash_one(dns_rbt_t *rbt); -static void -maybe_rehash(dns_rbt_t *rbt, size_t size); -static bool -rehashing_in_progress(dns_rbt_t *rbt); - -#define TRY_NEXTTABLE(hindex, rbt) \ - (hindex == rbt->hindex && rehashing_in_progress(rbt)) - -static void -rotate_left(dns_rbtnode_t *node, dns_rbtnode_t **rootp); -static void -rotate_right(dns_rbtnode_t *node, dns_rbtnode_t **rootp); - -static void -addonlevel(dns_rbtnode_t *node, dns_rbtnode_t *current, int order, - dns_rbtnode_t **rootp); - -static void -deletefromlevel(dns_rbtnode_t *item, dns_rbtnode_t **rootp); - -static void -deletetreeflat(dns_rbt_t *rbt, unsigned int quantum, bool unhash, - dns_rbtnode_t **nodep); - -static void -printnodename(dns_rbtnode_t *node, bool quoted, FILE *f); - -static void -freenode(dns_rbt_t *rbt, dns_rbtnode_t **nodep); - -unsigned int -dns__rbtnode_namelen(dns_rbtnode_t *node) { - dns_name_t current; - unsigned int len = 0; - - REQUIRE(DNS_RBTNODE_VALID(node)); - - dns_name_init(¤t, NULL); - - do { - if (node != NULL) { - node_name(node, ¤t); - len += current.length; - } else { - len += 1; - break; - } - - node = get_upper_node(node); - } while (!dns_name_isabsolute(¤t)); - - return (len); -} - -unsigned int -dns__rbtnode_getsize(dns_rbtnode_t *node) { - REQUIRE(DNS_RBTNODE_VALID(node)); - - return (NODE_SIZE(node)); -} - -/* - * Initialize a red/black tree of trees. - */ -isc_result_t -dns_rbt_create(isc_mem_t *mctx, dns_rbtdeleter_t deleter, void *deleter_arg, - dns_rbt_t **rbtp) { - dns_rbt_t *rbt; - - REQUIRE(mctx != NULL); - REQUIRE(rbtp != NULL && *rbtp == NULL); - REQUIRE(deleter == NULL ? deleter_arg == NULL : 1); - - rbt = isc_mem_get(mctx, sizeof(*rbt)); - *rbt = (dns_rbt_t){ - .data_deleter = deleter, - .deleter_arg = deleter_arg, - }; - - isc_mem_attach(mctx, &rbt->mctx); - - hashtable_new(rbt, 0, ISC_HASH_MIN_BITS); - - rbt->magic = RBT_MAGIC; - - *rbtp = rbt; - - return (ISC_R_SUCCESS); -} - -/* - * Deallocate a red/black tree of trees. - */ -isc_result_t -dns_rbt_destroy(dns_rbt_t **rbtp, unsigned int quantum) { - dns_rbt_t *rbt; - - REQUIRE(rbtp != NULL && VALID_RBT(*rbtp)); - - rbt = *rbtp; - - deletetreeflat(rbt, quantum, false, &rbt->root); - if (rbt->root != NULL) { - return (ISC_R_QUOTA); - } - - *rbtp = NULL; - - INSIST(rbt->nodecount == 0); - - if (rbt->hashtable[0] != NULL) { - hashtable_free(rbt, 0); - } - if (rbt->hashtable[1] != NULL) { - hashtable_free(rbt, 1); - } - - rbt->magic = 0; - - isc_mem_putanddetach(&rbt->mctx, rbt, sizeof(*rbt)); - return (ISC_R_SUCCESS); -} - -unsigned int -dns_rbt_nodecount(dns_rbt_t *rbt) { - REQUIRE(VALID_RBT(rbt)); - - return (rbt->nodecount); -} - -size_t -dns_rbt_hashsize(dns_rbt_t *rbt) { - REQUIRE(VALID_RBT(rbt)); - - uint8_t hashbits = (rbt->hashbits[0] > rbt->hashbits[1]) - ? rbt->hashbits[0] - : rbt->hashbits[1]; - - return (1 << hashbits); -} - -static isc_result_t -chain_name(dns_rbtnodechain_t *chain, dns_name_t *name, - bool include_chain_end) { - dns_name_t nodename; - isc_result_t result = ISC_R_SUCCESS; - int i; - - dns_name_init(&nodename, NULL); - - if (include_chain_end && chain->end != NULL) { - node_name(chain->end, &nodename); - dns_name_copy(&nodename, name); - } else { - dns_name_reset(name); - } - - for (i = (int)chain->level_count - 1; i >= 0; i--) { - node_name(chain->levels[i], &nodename); - result = dns_name_concatenate(name, &nodename, name, NULL); - - if (result != ISC_R_SUCCESS) { - return (result); - } - } - return (result); -} - -static isc_result_t -move_chain_to_last(dns_rbtnodechain_t *chain, dns_rbtnode_t *node) { - do { - /* - * Go as far right and then down as much as possible, - * as long as the rightmost node has a down pointer. - */ - while (node->right != NULL) { - node = node->right; - } - - if (node->down == NULL) { - break; - } - - ADD_LEVEL(chain, node); - node = node->down; - } while (1); - - chain->end = node; - - return (ISC_R_SUCCESS); -} - -/* - * Add 'name' to tree, initializing its data pointer with 'data'. - */ - -isc_result_t -dns_rbt_addnode(dns_rbt_t *rbt, const dns_name_t *name, dns_rbtnode_t **nodep) { - /* - * Does this thing have too many variables or what? - */ - dns_rbtnode_t **root, *parent, *child, *current, *new_current; - dns_name_t *add_name, *new_name, current_name, *prefix, *suffix; - dns_fixedname_t fixedcopy, fixedprefix, fixedsuffix, fnewname; - dns_offsets_t current_offsets; - dns_namereln_t compared; - isc_result_t result = ISC_R_SUCCESS; - unsigned int level_count; - unsigned int common_labels; - unsigned int nlabels, hlabels; - int order; - - REQUIRE(VALID_RBT(rbt)); - REQUIRE(dns_name_isabsolute(name)); - REQUIRE(nodep != NULL && *nodep == NULL); - - /* - * Dear future BIND developer, - * - * After you have tried attempting to optimize this routine by - * using the hashtable and have realized your folly, please - * append another cross ("X") below as a warning to the next - * future BIND developer: - * - * Number of victim developers: X - * - * I wish the past developer had included such a notice. - * - * Long form: Unlike dns_rbt_findnode(), this function does not - * lend itself to be optimized using the hashtable: - * - * 1. In the subtree where the insertion occurs, this function - * needs to have the insertion point and the order where the - * lookup terminated (i.e., at the insertion point where left or - * right child is NULL). This cannot be determined from the - * hashtable, so at least in that subtree, a BST O(log N) lookup - * is necessary. - * - * 2. Our RBT nodes contain not only single labels but label - * sequences to optimize space usage. So at every level, we have - * to look for a match in the hashtable for all superdomains in - * the rest of the name we're searching. This is an O(N) - * operation at least, here N being the label size of name, each - * of which is a hashtable lookup involving dns_name_equal() - * comparisons. - */ - - /* - * Create a copy of the name so the original name structure is - * not modified. - */ - add_name = dns_fixedname_initname(&fixedcopy); - INSIST(add_name != NULL); - dns_name_clone(name, add_name); - - if (rbt->root == NULL) { - new_current = rbtnode_new(rbt->mctx, add_name); - rbt->nodecount++; - new_current->is_root = 1; - new_current->uppernode = NULL; - rbt->root = new_current; - *nodep = new_current; - hash_node(rbt, new_current, name); - return (ISC_R_SUCCESS); - } - - level_count = 0; - - prefix = dns_fixedname_initname(&fixedprefix); - suffix = dns_fixedname_initname(&fixedsuffix); - - INSIST(prefix != NULL); - INSIST(suffix != NULL); - - root = &rbt->root; - INSIST((*root)->is_root); - parent = NULL; - current = NULL; - child = *root; - dns_name_init(¤t_name, current_offsets); - new_name = dns_fixedname_initname(&fnewname); - nlabels = dns_name_countlabels(name); - hlabels = 0; - - do { - current = child; - - node_name(current, ¤t_name); - compared = dns_name_fullcompare(add_name, ¤t_name, &order, - &common_labels); - - if (compared == dns_namereln_equal) { - *nodep = current; - result = ISC_R_EXISTS; - break; - } - - if (compared == dns_namereln_none) { - if (order < 0) { - parent = current; - child = current->left; - } else if (order > 0) { - parent = current; - child = current->right; - } - } else { - /* - * This name has some suffix in common with the - * name at the current node. If the name at - * the current node is shorter, that means the - * new name should be in a subtree. If the - * name at the current node is longer, that means - * the down pointer to this tree should point - * to a new tree that has the common suffix, and - * the non-common parts of these two names should - * start a new tree. - */ - hlabels += common_labels; - if (compared == dns_namereln_subdomain) { - /* - * All of the existing labels are in common, - * so the new name is in a subtree. - * Whack off the common labels for the - * not-in-common part to be searched for - * in the next level. - */ - dns_name_split(add_name, common_labels, - add_name, NULL); - - /* - * Follow the down pointer (possibly NULL). - */ - root = ¤t->down; - - INSIST(*root == NULL || - ((*root)->is_root && - (*root)->parent == current)); - - parent = NULL; - child = current->down; - - INSIST(level_count < DNS_RBT_LEVELBLOCK); - level_count++; - } else { - /* - * The number of labels in common is fewer - * than the number of labels at the current - * node, so the current node must be adjusted - * to have just the common suffix, and a down - * pointer made to a new tree. - */ - - INSIST(compared == - dns_namereln_commonancestor || - compared == dns_namereln_contains); - - /* - * Ensure the number of levels in the tree - * does not exceed the number of logical - * levels allowed by DNSSEC. - * - * XXXDCL need a better error result? - */ - if (level_count >= DNS_RBT_LEVELBLOCK) { - result = ISC_R_NOSPACE; - break; - } - - /* - * Split the name into two parts, a prefix - * which is the not-in-common parts of the - * two names and a suffix that is the common - * parts of them. - */ - dns_name_split(¤t_name, common_labels, - prefix, suffix); - new_current = rbtnode_new(rbt->mctx, suffix); - - /* - * Reproduce the tree attributes of the - * current node. - */ - new_current->is_root = current->is_root; - if (current->nsec == DNS_DB_NSEC_HAS_NSEC) { - new_current->nsec = DNS_DB_NSEC_NORMAL; - } else { - new_current->nsec = current->nsec; - } - new_current->parent = current->parent; - new_current->left = current->left; - new_current->right = current->right; - new_current->color = current->color; - - /* - * Fix pointers that were to the current node. - */ - if (parent != NULL) { - if (parent->left == current) { - parent->left = new_current; - } else { - parent->right = new_current; - } - } - if (new_current->left != NULL) { - new_current->left->parent = new_current; - } - if (new_current->right != NULL) { - new_current->right->parent = - new_current; - } - if (*root == current) { - *root = new_current; - } - - current->namelen = prefix->length; - current->offsetlen = prefix->labels; - - /* - * Set up the new root of the next level. - * By definition it will not be the top - * level tree, so clear the absolute flag. - */ - current->is_root = 1; - current->parent = new_current; - new_current->down = current; - root = &new_current->down; - - new_current->uppernode = current->uppernode; - current->uppernode = new_current; - - INSIST(level_count < DNS_RBT_LEVELBLOCK); - level_count++; - - current->left = NULL; - current->right = NULL; - - current->color = BLACK; - current->absolute = false; - - rbt->nodecount++; - dns_name_getlabelsequence(name, - nlabels - hlabels, - hlabels, new_name); - hash_node(rbt, new_current, new_name); - - if (common_labels == - dns_name_countlabels(add_name)) - { - /* - * The name has been added by pushing - * the not-in-common parts down to - * a new level. - */ - *nodep = new_current; - return (ISC_R_SUCCESS); - } else { - /* - * The current node has no data, - * because it is just a placeholder. - * Its data pointer is already NULL - * from rbtnode_new()), so there's - * nothing more to do to it. - * - * The not-in-common parts of the new - * name will be inserted into the new - * level following this loop. - */ - dns_name_split(add_name, common_labels, - add_name, NULL); - result = ISC_R_SUCCESS; - break; - } - } - } - } while (child != NULL); - - if (result == ISC_R_SUCCESS) { - new_current = rbtnode_new(rbt->mctx, add_name); - } - - if (result == ISC_R_SUCCESS) { - if (*root == NULL) { - new_current->uppernode = current; - } else { - new_current->uppernode = (*root)->parent; - } - - addonlevel(new_current, current, order, root); - rbt->nodecount++; - *nodep = new_current; - hash_node(rbt, new_current, name); - } - - return (result); -} - -/* - * Find the node for "name" in the tree of trees. - */ -isc_result_t -dns__rbt_findnode(dns_rbt_t *rbt, const dns_name_t *name, dns_name_t *foundname, - dns_rbtnode_t **node, dns_rbtnodechain_t *chain, - unsigned int options, dns_rbtfindcallback_t callback, - void *callback_arg DNS__DB_FLARG) { - dns_rbtnode_t *current, *last_compared; - dns_rbtnodechain_t localchain; - dns_name_t *search_name, current_name, *callback_name; - dns_fixedname_t fixedcallbackname, fixedsearchname; - dns_namereln_t compared; - isc_result_t result, saved_result; - unsigned int common_labels; - unsigned int hlabels = 0; - int order; - uint8_t hindex; - - REQUIRE(VALID_RBT(rbt)); - REQUIRE(dns_name_isabsolute(name)); - REQUIRE(node != NULL && *node == NULL); - REQUIRE((options & (DNS_RBTFIND_NOEXACT | DNS_RBTFIND_NOPREDECESSOR)) != - (DNS_RBTFIND_NOEXACT | DNS_RBTFIND_NOPREDECESSOR)); - - /* - * If there is a chain it needs to appear to be in a sane state, - * otherwise a chain is still needed to generate foundname and - * callback_name. - */ - if (chain == NULL) { - options |= DNS_RBTFIND_NOPREDECESSOR; - chain = &localchain; - dns_rbtnodechain_init(chain); - } else { - dns_rbtnodechain_reset(chain); - } - - if (rbt->root == NULL) { - return (ISC_R_NOTFOUND); - } - - /* - * Appease GCC about variables it incorrectly thinks are - * possibly used uninitialized. - */ - compared = dns_namereln_none; - last_compared = NULL; - order = 0; - - callback_name = dns_fixedname_initname(&fixedcallbackname); - - /* - * search_name is the name segment being sought in each tree level. - * By using a fixedname, the search_name will definitely have offsets - * for use by any splitting. By using dns_name_clone, no name data - * should be copied. - */ - search_name = dns_fixedname_initname(&fixedsearchname); - INSIST(search_name != NULL); - dns_name_clone(name, search_name); - - dns_name_init(¤t_name, NULL); - - saved_result = ISC_R_SUCCESS; - current = rbt->root; - - while (current != NULL) { - node_name(current, ¤t_name); - compared = dns_name_fullcompare(search_name, ¤t_name, - &order, &common_labels); - /* - * last_compared is used as a shortcut to start (or - * continue rather) finding the stop-node of the search - * when hashing was used (see much below in this - * function). - */ - last_compared = current; - - if (compared == dns_namereln_equal) { - break; - } - - if (compared == dns_namereln_none) { - /* - * Here, current is pointing at a subtree root - * node. We try to find a matching node using - * the hashtable. We can get one of 3 results - * here: (a) we locate the matching node, (b) we - * find a node to which the current node has a - * subdomain relation, (c) we fail to find (a) - * or (b). - */ - - dns_name_t hash_name; - dns_rbtnode_t *hnode; - dns_rbtnode_t *up_current; - unsigned int nlabels; - unsigned int tlabels = 1; - uint32_t hashval; - uint32_t hash; - - /* - * The case of current not being a subtree root, - * that means a left or right pointer was - * followed, only happens when the algorithm - * fell through to the traditional binary search - * because of a bitstring label. Since we - * dropped the bitstring support, this should - * not happen. - */ - INSIST(current->is_root); - - nlabels = dns_name_countlabels(search_name); - - /* - * current is the root of the current level, so - * its parent is the same as its "up" pointer. - */ - up_current = current->parent; - dns_name_init(&hash_name, NULL); - - hashagain: - hindex = rbt->hindex; - /* - * Compute the hash over the full absolute - * name. Look for the smallest suffix match at - * this tree level (hlevel), and then at every - * iteration, look for the next smallest suffix - * match (add another subdomain label to the - * absolute name being hashed). - */ - dns_name_getlabelsequence(name, nlabels - tlabels, - hlabels + tlabels, - &hash_name); - hashval = dns_name_hash(&hash_name); - - dns_name_getlabelsequence(search_name, - nlabels - tlabels, tlabels, - &hash_name); - - nexttable: - /* - * Walk all the nodes in the hash bucket pointed - * by the computed hash value. - */ - - hash = isc_hash_bits32(hashval, rbt->hashbits[hindex]); - - for (hnode = rbt->hashtable[hindex][hash]; - hnode != NULL; hnode = hnode->hashnext) - { - dns_name_t hnode_name; - - if (hashval != hnode->hashval) { - continue; - } - /* - * This checks that the hashed label sequence - * being looked up is at the same tree level, so - * that we don't match a labelsequence from some - * other subdomain. - */ - if (get_upper_node(hnode) != up_current) { - continue; - } - - dns_name_init(&hnode_name, NULL); - node_name(hnode, &hnode_name); - if (dns_name_equal(&hnode_name, &hash_name)) { - break; - } - } - - if (hnode != NULL) { - current = hnode; - /* - * This is an optimization. If hashing found - * the right node, the next call to - * dns_name_fullcompare() would obviously - * return _equal or _subdomain. Determine - * which of those would be the case by - * checking if the full name was hashed. Then - * make it look like dns_name_fullcompare - * was called and jump to the right place. - */ - if (tlabels == nlabels) { - compared = dns_namereln_equal; - break; - } else { - common_labels = tlabels; - compared = dns_namereln_subdomain; - goto subdomain; - } - } - - if (TRY_NEXTTABLE(hindex, rbt)) { - /* - * Rehashing in progress, check the other table - */ - hindex = RBT_HASH_NEXTTABLE(rbt->hindex); - goto nexttable; - } - - if (tlabels++ < nlabels) { - goto hashagain; - } - - /* - * All of the labels have been tried against the hash - * table. - */ - current = NULL; - continue; - } else { - /* - * The names have some common suffix labels. - * - * If the number in common are equal in length to - * the current node's name length, then follow the - * down pointer and search in the new tree. - */ - if (compared == dns_namereln_subdomain) { - subdomain: - /* - * Whack off the current node's common parts - * for the name to search in the next level. - */ - dns_name_split(search_name, common_labels, - search_name, NULL); - hlabels += common_labels; - /* - * This might be the closest enclosing name. - */ - if (WANTEMPTYDATA_OR_DATA(options, current)) { - *node = current; - } - - /* - * Point the chain to the next level. This - * needs to be done before 'current' is pointed - * there because the callback in the next - * block of code needs the current 'current', - * but in the event the callback requests that - * the search be stopped then the - * DNS_R_PARTIALMATCH code at the end of this - * function needs the chain pointed to the - * next level. - */ - ADD_LEVEL(chain, current); - - /* - * The caller may want to interrupt the - * downward search when certain special nodes - * are traversed. If this is a special node, - * the callback is used to learn what the - * caller wants to do. - */ - if (callback != NULL && current->find_callback) - { - result = chain_name( - chain, callback_name, false); - if (result != ISC_R_SUCCESS) { - dns_rbtnodechain_reset(chain); - return (result); - } - - result = - (callback)(current, - callback_name, - callback_arg - DNS__DB_FLARG_PASS); - if (result != DNS_R_CONTINUE) { - saved_result = result; - /* - * Treat this node as if it - * had no down pointer. - */ - current = NULL; - break; - } - } - - /* - * Finally, head to the next tree level. - */ - current = current->down; - } else { - /* - * Though there are labels in common, the - * entire name at this node is not common - * with the search name so the search - * name does not exist in the tree. - */ - INSIST(compared == - dns_namereln_commonancestor || - compared == dns_namereln_contains); - - current = NULL; - } - } - } - - /* - * If current is not NULL, NOEXACT is not disallowing exact matches, - * and either the node has data or an empty node is ok, return - * ISC_R_SUCCESS to indicate an exact match. - */ - if (current != NULL && (options & DNS_RBTFIND_NOEXACT) == 0 && - WANTEMPTYDATA_OR_DATA(options, current)) - { - /* - * Found an exact match. - */ - chain->end = current; - chain->level_matches = chain->level_count; - - if (foundname != NULL) { - result = chain_name(chain, foundname, true); - } else { - result = ISC_R_SUCCESS; - } - - if (result == ISC_R_SUCCESS) { - *node = current; - result = saved_result; - } else { - *node = NULL; - } - } else { - /* - * Did not find an exact match (or did not want one). - */ - if (*node != NULL) { - /* - * ... but found a partially matching superdomain. - * Unwind the chain to the partial match node - * to set level_matches to the level above the node, - * and then to derive the name. - * - * chain->level_count is guaranteed to be at least 1 - * here because by definition of finding a superdomain, - * the chain is pointed to at least the first subtree. - */ - chain->level_matches = chain->level_count - 1; - - while (chain->levels[chain->level_matches] != *node) { - INSIST(chain->level_matches > 0); - chain->level_matches--; - } - - if (foundname != NULL) { - unsigned int saved_count = chain->level_count; - - chain->level_count = chain->level_matches + 1; - - result = chain_name(chain, foundname, false); - - chain->level_count = saved_count; - } else { - result = ISC_R_SUCCESS; - } - - if (result == ISC_R_SUCCESS) { - result = DNS_R_PARTIALMATCH; - } - } else { - result = ISC_R_NOTFOUND; - } - - if (current != NULL) { - /* - * There was an exact match but either - * DNS_RBTFIND_NOEXACT was set, or - * DNS_RBTFIND_EMPTYDATA was set and the node had no - * data. A policy decision was made to set the - * chain to the exact match, but this is subject - * to change if it becomes apparent that something - * else would be more useful. It is important that - * this case is handled here, because the predecessor - * setting code below assumes the match was not exact. - */ - INSIST(((options & DNS_RBTFIND_NOEXACT) != 0) || - ((options & DNS_RBTFIND_EMPTYDATA) == 0 && - current->data == NULL)); - chain->end = current; - } else if ((options & DNS_RBTFIND_NOPREDECESSOR) != 0) { - /* - * Ensure the chain points nowhere. - */ - chain->end = NULL; - } else { - /* - * Since there was no exact match, the chain argument - * needs to be pointed at the DNSSEC predecessor of - * the search name. - */ - if (compared == dns_namereln_subdomain) { - /* - * Attempted to follow a down pointer that was - * NULL, which means the searched for name was - * a subdomain of a terminal name in the tree. - * Since there are no existing subdomains to - * order against, the terminal name is the - * predecessor. - */ - INSIST(chain->level_count > 0); - INSIST(chain->level_matches < - chain->level_count); - chain->end = - chain->levels[--chain->level_count]; - } else { - isc_result_t result2; - - /* - * Point current to the node that stopped - * the search. - * - * With the hashing modification that has been - * added to the algorithm, the stop node of a - * standard binary search is not known. So it - * has to be found. There is probably a more - * clever way of doing this. - * - * The assignment of current to NULL when - * the relationship is *not* dns_namereln_none, - * even though it later gets set to the same - * last_compared anyway, is simply to not push - * the while loop in one more level of - * indentation. - */ - if (compared == dns_namereln_none) { - current = last_compared; - } else { - current = NULL; - } - - while (current != NULL) { - node_name(current, ¤t_name); - compared = dns_name_fullcompare( - search_name, ¤t_name, - &order, &common_labels); - POST(compared); - - last_compared = current; - - /* - * Standard binary search movement. - */ - if (order < 0) { - current = current->left; - } else { - current = current->right; - } - } - - current = last_compared; - - /* - * Reached a point within a level tree that - * positively indicates the name is not - * present, but the stop node could be either - * less than the desired name (order > 0) or - * greater than the desired name (order < 0). - * - * If the stop node is less, it is not - * necessarily the predecessor. If the stop - * node has a down pointer, then the real - * predecessor is at the end of a level below - * (not necessarily the next level). - * Move down levels until the rightmost node - * does not have a down pointer. - * - * When the stop node is greater, it is - * the successor. All the logic for finding - * the predecessor is handily encapsulated - * in dns_rbtnodechain_prev. In the event - * that the search name is less than anything - * else in the tree, the chain is reset. - * XXX DCL What is the best way for the caller - * to know that the search name has - * no predecessor? - */ - - if (order > 0) { - if (current->down != NULL) { - ADD_LEVEL(chain, current); - - result2 = move_chain_to_last( - chain, current->down); - - if (result2 != ISC_R_SUCCESS) { - result = result2; - } - } else { - /* - * Ah, the pure and simple - * case. The stop node is the - * predecessor. - */ - chain->end = current; - } - } else { - INSIST(order < 0); - - chain->end = current; - - result2 = dns_rbtnodechain_prev( - chain, NULL, NULL); - if (result2 == ISC_R_SUCCESS || - result2 == DNS_R_NEWORIGIN) - { - /* Nothing. */ - } else if (result2 == ISC_R_NOMORE) { - /* - * There is no predecessor. - */ - dns_rbtnodechain_reset(chain); - } else { - result = result2; - } - } - } - } - } - - ENSURE(*node == NULL || DNS_RBTNODE_VALID(*node)); - - return (result); -} - -/* - * Remove a node from the tree of trees. - * - * NOTE WELL: deletion is *not* symmetric with addition; that is, reversing - * a sequence of additions to be deletions will not generally get the - * tree back to the state it started in. For example, if the addition - * of "b.c" caused the node "a.b.c" to be split, pushing "a" to its own level, - * then the subsequent deletion of "b.c" will not cause "a" to be pulled up, - * restoring "a.b.c". The RBT *used* to do this kind of rejoining, but it - * turned out to be a bad idea because it could corrupt an active nodechain - * that had "b.c" as one of its levels -- and the RBT has no idea what - * nodechains are in use by callers, so it can't even *try* to helpfully - * fix them up (which would probably be doomed to failure anyway). - * - * Similarly, it is possible to leave the tree in a state where a supposedly - * deleted node still exists. The first case of this is obvious; take - * the tree which has "b.c" on one level, pointing to "a". Now deleted "b.c". - * It was just established in the previous paragraph why we can't pull "a" - * back up to its parent level. But what happens when "a" then gets deleted? - * "b.c" is left hanging around without data or children. This condition - * is actually pretty easy to detect, but ... should it really be removed? - * Is a chain pointing to it? An iterator? Who knows! (Note that the - * references structure member cannot be looked at because it is private to - * rbtdb.) This is ugly and makes me unhappy, but after hours of trying to - * make it more aesthetically proper and getting nowhere, this is the way it - * is going to stay until such time as it proves to be a *real* problem. - * - * Finally, for reference, note that the original routine that did node - * joining was called join_nodes(). It has been excised, living now only - * in the CVS history, but comments have been left behind that point to it just - * in case someone wants to muck with this some more. - * - * The one positive aspect of all of this is that joining used to have a - * case where it might fail. Without trying to join, now this function always - * succeeds. It still returns isc_result_t, though, so the API wouldn't change. - */ -isc_result_t -dns_rbt_deletenode(dns_rbt_t *rbt, dns_rbtnode_t *node, bool recurse) { - dns_rbtnode_t *parent; - - REQUIRE(VALID_RBT(rbt)); - REQUIRE(DNS_RBTNODE_VALID(node)); - INSIST(rbt->nodecount != 0); - - if (node->down != NULL) { - if (recurse) { - node->down->parent = NULL; - deletetreeflat(rbt, 0, true, &node->down); - } else { - if (node->data != NULL && rbt->data_deleter != NULL) { - rbt->data_deleter(node->data, rbt->deleter_arg); - } - node->data = NULL; - - /* - * Since there is at least one node below this one and - * no recursion was requested, the deletion is - * complete. The down node from this node might be all - * by itself on a single level, so join_nodes() could - * be used to collapse the tree (with all the caveats - * of the comment at the start of this function). - * But join_nodes() function has now been removed. - */ - return (ISC_R_SUCCESS); - } - } - - /* - * Note the node that points to the level of the node - * that is being deleted. If the deleted node is the - * top level, parent will be set to NULL. - */ - parent = get_upper_node(node); - - /* - * This node now has no down pointer, so now it needs - * to be removed from this level. - */ - deletefromlevel(node, parent == NULL ? &rbt->root : &parent->down); - - if (node->data != NULL && rbt->data_deleter != NULL) { - rbt->data_deleter(node->data, rbt->deleter_arg); - } - - unhash_node(rbt, node); -#if DNS_RBT_USEMAGIC - node->magic = 0; -#endif /* if DNS_RBT_USEMAGIC */ - isc_refcount_destroy(&node->references); - - freenode(rbt, &node); - - /* - * This function never fails. - */ - return (ISC_R_SUCCESS); -} - -void -dns_rbt_namefromnode(dns_rbtnode_t *node, dns_name_t *name) { - REQUIRE(DNS_RBTNODE_VALID(node)); - REQUIRE(name != NULL); - REQUIRE(name->offsets == NULL); - - node_name(node, name); -} - -isc_result_t -dns_rbt_fullnamefromnode(dns_rbtnode_t *node, dns_name_t *name) { - dns_name_t current; - isc_result_t result; - - REQUIRE(DNS_RBTNODE_VALID(node)); - REQUIRE(name != NULL); - REQUIRE(name->buffer != NULL); - - dns_name_init(¤t, NULL); - dns_name_reset(name); - - do { - INSIST(node != NULL); - - node_name(node, ¤t); - - result = dns_name_concatenate(name, ¤t, name, NULL); - if (result != ISC_R_SUCCESS) { - break; - } - - node = get_upper_node(node); - } while (!dns_name_isabsolute(name)); - - return (result); -} - -char * -dns_rbt_formatnodename(dns_rbtnode_t *node, char *printname, - unsigned int size) { - dns_fixedname_t fixedname; - dns_name_t *name; - isc_result_t result; - - REQUIRE(DNS_RBTNODE_VALID(node)); - REQUIRE(printname != NULL); - - name = dns_fixedname_initname(&fixedname); - result = dns_rbt_fullnamefromnode(node, name); - if (result == ISC_R_SUCCESS) { - dns_name_format(name, printname, size); - } else { - snprintf(printname, size, "", - isc_result_totext(result)); - } - - return (printname); -} - -static dns_rbtnode_t * -rbtnode_new(isc_mem_t *mctx, const dns_name_t *name) { - dns_rbtnode_t *node = NULL; - isc_region_t region; - unsigned int labels; - size_t nodelen; - - REQUIRE(name->offsets != NULL); - - dns_name_toregion(name, ®ion); - labels = dns_name_countlabels(name); - ENSURE(labels > 0); - - /* - * Allocate space for the node structure, the name, and the offsets. - */ - nodelen = sizeof(dns_rbtnode_t) + region.length + labels + 1; - node = isc_mem_get(mctx, nodelen); - *node = (dns_rbtnode_t){ - .color = BLACK, - .nsec = DNS_DB_NSEC_NORMAL, - }; - - ISC_LINK_INIT(node, deadlink); - - isc_refcount_init(&node->references, 0); - - /* - * The following is stored to make reconstructing a name from the - * stored value in the node easy: the length of the name, the number - * of labels, whether the name is absolute or not, the name itself, - * and the name's offsets table. - * - * XXX RTH - * The offsets table could be made smaller by eliminating the - * first offset, which is always 0. This requires changes to - * lib/dns/name.c. - * - * Note: OLDOFFSETLEN *must* be assigned *after* OLDNAMELEN is assigned - * as it uses OLDNAMELEN. - */ - node->oldnamelen = node->namelen = region.length; - OLDOFFSETLEN(node) = node->offsetlen = labels; - node->absolute = name->attributes.absolute; - - memmove(NAME(node), region.base, region.length); - memmove(OFFSETS(node), name->offsets, labels); - -#if DNS_RBT_USEMAGIC - node->magic = DNS_RBTNODE_MAGIC; -#endif /* if DNS_RBT_USEMAGIC */ - return (node); -} - -/* - * Add a node to the hash table - */ -static void -hash_add_node(dns_rbt_t *rbt, dns_rbtnode_t *node, const dns_name_t *name) { - uint32_t hash; - - REQUIRE(name != NULL); - - node->hashval = dns_name_hash(name); - - hash = isc_hash_bits32(node->hashval, rbt->hashbits[rbt->hindex]); - node->hashnext = rbt->hashtable[rbt->hindex][hash]; - - rbt->hashtable[rbt->hindex][hash] = node; -} - -/* - * Initialize hash table - */ -static void -hashtable_new(dns_rbt_t *rbt, uint8_t index, uint8_t bits) { - REQUIRE(rbt->hashbits[index] == 0U); - REQUIRE(rbt->hashtable[index] == NULL); - REQUIRE(bits >= ISC_HASH_MIN_BITS); - REQUIRE(bits < ISC_HASH_MAX_BITS); - - rbt->hashbits[index] = bits; - - rbt->hashtable[index] = isc_mem_cget(rbt->mctx, - ISC_HASHSIZE(rbt->hashbits[index]), - sizeof(dns_rbtnode_t *)); -} - -static void -hashtable_free(dns_rbt_t *rbt, uint8_t index) { - isc_mem_cput(rbt->mctx, rbt->hashtable[index], - ISC_HASHSIZE(rbt->hashbits[index]), - sizeof(dns_rbtnode_t *)); - - rbt->hashbits[index] = 0U; - rbt->hashtable[index] = NULL; -} - -static uint32_t -rehash_bits(dns_rbt_t *rbt, size_t newcount) { - uint32_t newbits = rbt->hashbits[rbt->hindex]; - - while (newcount >= ISC_HASHSIZE(newbits) && newbits < ISC_HASH_MAX_BITS) - { - newbits += 1; - } - - return (newbits); -} - -/* - * Rebuild the hashtable to reduce the load factor - */ -static void -hashtable_rehash(dns_rbt_t *rbt, uint32_t newbits) { - uint8_t oldindex = rbt->hindex; - uint32_t oldbits = rbt->hashbits[oldindex]; - uint8_t newindex = RBT_HASH_NEXTTABLE(oldindex); - - REQUIRE(rbt->hashbits[oldindex] >= ISC_HASH_MIN_BITS); - REQUIRE(rbt->hashbits[oldindex] <= ISC_HASH_MAX_BITS); - REQUIRE(rbt->hashtable[oldindex] != NULL); - - REQUIRE(newbits <= ISC_HASH_MAX_BITS); - REQUIRE(rbt->hashbits[newindex] == 0U); - REQUIRE(rbt->hashtable[newindex] == NULL); - - REQUIRE(newbits > oldbits); - - hashtable_new(rbt, newindex, newbits); - - rbt->hindex = newindex; - - hashtable_rehash_one(rbt); -} - -static void -hashtable_rehash_one(dns_rbt_t *rbt) { - dns_rbtnode_t **newtable = rbt->hashtable[rbt->hindex]; - uint32_t oldsize = - ISC_HASHSIZE(rbt->hashbits[RBT_HASH_NEXTTABLE(rbt->hindex)]); - dns_rbtnode_t **oldtable = - rbt->hashtable[RBT_HASH_NEXTTABLE(rbt->hindex)]; - dns_rbtnode_t *node = NULL; - dns_rbtnode_t *nextnode; - - /* Find first non-empty node */ - while (rbt->hiter < oldsize && oldtable[rbt->hiter] == NULL) { - rbt->hiter++; - } - - /* Rehashing complete */ - if (rbt->hiter == oldsize) { - hashtable_free(rbt, RBT_HASH_NEXTTABLE(rbt->hindex)); - rbt->hiter = 0; - return; - } - - /* Move the first non-empty node from old hashtable to new hashtable */ - for (node = oldtable[rbt->hiter]; node != NULL; node = nextnode) { - uint32_t hash = isc_hash_bits32(node->hashval, - rbt->hashbits[rbt->hindex]); - nextnode = node->hashnext; - node->hashnext = newtable[hash]; - newtable[hash] = node; - } - - oldtable[rbt->hiter] = NULL; - - rbt->hiter++; -} - -static void -maybe_rehash(dns_rbt_t *rbt, size_t newcount) { - uint32_t newbits = rehash_bits(rbt, newcount); - - if (rbt->hashbits[rbt->hindex] < newbits && - newbits <= ISC_HASH_MAX_BITS) - { - hashtable_rehash(rbt, newbits); - } -} - -static bool -rehashing_in_progress(dns_rbt_t *rbt) { - return (rbt->hashtable[RBT_HASH_NEXTTABLE(rbt->hindex)] != NULL); -} - -static bool -hashtable_is_overcommited(dns_rbt_t *rbt) { - return (rbt->nodecount >= (ISC_HASHSIZE(rbt->hashbits[rbt->hindex]) * - ISC_HASH_OVERCOMMIT)); -} - -/* - * Add a node to the hash table. Rehash the hashtable if the node count - * rises above a critical level. - */ -static void -hash_node(dns_rbt_t *rbt, dns_rbtnode_t *node, const dns_name_t *name) { - REQUIRE(DNS_RBTNODE_VALID(node)); - - if (rehashing_in_progress(rbt)) { - /* Rehash in progress */ - hashtable_rehash_one(rbt); - } else if (hashtable_is_overcommited(rbt)) { - /* Rehash requested */ - maybe_rehash(rbt, rbt->nodecount); - } - - hash_add_node(rbt, node, name); -} - -/* - * Remove a node from the hash table - */ -static void -unhash_node(dns_rbt_t *rbt, dns_rbtnode_t *dnode) { - uint32_t hash; - uint8_t hindex = rbt->hindex; - dns_rbtnode_t *hnode; - - REQUIRE(DNS_RBTNODE_VALID(dnode)); - - /* - * The node could be either in: - * a) current table: no rehashing in progress, or - * b) current table: the node has been already moved, or - * c) other table: the node hasn't been moved yet. - */ -nexttable: - hash = isc_hash_bits32(dnode->hashval, rbt->hashbits[hindex]); - - hnode = rbt->hashtable[hindex][hash]; - - if (hnode == dnode) { - rbt->hashtable[hindex][hash] = hnode->hashnext; - return; - } else { - for (; hnode != NULL; hnode = hnode->hashnext) { - if (hnode->hashnext == dnode) { - hnode->hashnext = dnode->hashnext; - return; - } - } - } - - if (TRY_NEXTTABLE(hindex, rbt)) { - /* Rehashing in progress, delete from the other table */ - hindex = RBT_HASH_NEXTTABLE(hindex); - goto nexttable; - } - - /* We haven't found any matching node, this should not be possible. */ - UNREACHABLE(); -} - -static void -rotate_left(dns_rbtnode_t *node, dns_rbtnode_t **rootp) { - dns_rbtnode_t *child; - - REQUIRE(DNS_RBTNODE_VALID(node)); - REQUIRE(rootp != NULL); - - child = node->right; - INSIST(child != NULL); - - node->right = child->left; - if (child->left != NULL) { - child->left->parent = node; - } - child->left = node; - - child->parent = node->parent; - - if (node->is_root) { - *rootp = child; - child->is_root = 1; - node->is_root = 0; - } else { - if (node->parent->left == node) { - node->parent->left = child; - } else { - node->parent->right = child; - } - } - - node->parent = child; -} - -static void -rotate_right(dns_rbtnode_t *node, dns_rbtnode_t **rootp) { - dns_rbtnode_t *child; - - REQUIRE(DNS_RBTNODE_VALID(node)); - REQUIRE(rootp != NULL); - - child = node->left; - INSIST(child != NULL); - - node->left = child->right; - if (child->right != NULL) { - child->right->parent = node; - } - child->right = node; - - child->parent = node->parent; - - if (node->is_root) { - *rootp = child; - child->is_root = 1; - node->is_root = 0; - } else { - if (node->parent->left == node) { - node->parent->left = child; - } else { - node->parent->right = child; - } - } - - node->parent = child; -} - -/* - * This is the real workhorse of the insertion code, because it does the - * true red/black tree on a single level. - */ -static void -addonlevel(dns_rbtnode_t *node, dns_rbtnode_t *current, int order, - dns_rbtnode_t **rootp) { - dns_rbtnode_t *child, *root, *parent, *grandparent; - dns_name_t add_name, current_name; - dns_offsets_t add_offsets, current_offsets; - - REQUIRE(rootp != NULL); - REQUIRE(DNS_RBTNODE_VALID(node) && node->left == NULL && - node->right == NULL); - REQUIRE(current != NULL); - - root = *rootp; - if (root == NULL) { - /* - * First node of a level. - */ - node->color = BLACK; - node->is_root = 1; - node->parent = current; - *rootp = node; - return; - } - - child = root; - POST(child); - - dns_name_init(&add_name, add_offsets); - node_name(node, &add_name); - - dns_name_init(¤t_name, current_offsets); - node_name(current, ¤t_name); - - if (order < 0) { - INSIST(current->left == NULL); - current->left = node; - } else { - INSIST(current->right == NULL); - current->right = node; - } - - INSIST(node->parent == NULL); - node->parent = current; - - node->color = RED; - - while (node != root && IS_RED(node->parent)) { - /* - * XXXDCL could do away with separate parent and grandparent - * variables. They are vestiges of the days before parent - * pointers. However, they make the code a little clearer. - */ - - parent = node->parent; - grandparent = parent->parent; - - if (parent == grandparent->left) { - child = grandparent->right; - if (child != NULL && IS_RED(child)) { - parent->color = BLACK; - child->color = BLACK; - grandparent->color = RED; - node = grandparent; - } else { - if (node == parent->right) { - rotate_left(parent, &root); - node = parent; - parent = node->parent; - grandparent = parent->parent; - } - parent->color = BLACK; - grandparent->color = RED; - rotate_right(grandparent, &root); - } - } else { - child = grandparent->left; - if (child != NULL && IS_RED(child)) { - parent->color = BLACK; - child->color = BLACK; - grandparent->color = RED; - node = grandparent; - } else { - if (node == parent->left) { - rotate_right(parent, &root); - node = parent; - parent = node->parent; - grandparent = parent->parent; - } - parent->color = BLACK; - grandparent->color = RED; - rotate_left(grandparent, &root); - } - } - } - - root->color = BLACK; - ENSURE(root->is_root); - *rootp = root; - - return; -} - -/* - * This is the real workhorse of the deletion code, because it does the - * true red/black tree on a single level. - */ -static void -deletefromlevel(dns_rbtnode_t *item, dns_rbtnode_t **rootp) { - dns_rbtnode_t *child, *sibling, *parent; - dns_rbtnode_t *successor; - - REQUIRE(item != NULL); - - /* - * Verify that the parent history is (apparently) correct. - */ - INSIST((item->is_root && *rootp == item) || - (!item->is_root && - (item->parent->left == item || item->parent->right == item))); - - child = NULL; - - if (item->left == NULL) { - if (item->right == NULL) { - if (item->is_root) { - /* - * This is the only item in the tree. - */ - *rootp = NULL; - return; - } - } else { - /* - * This node has one child, on the right. - */ - child = item->right; - } - } else if (item->right == NULL) { - /* - * This node has one child, on the left. - */ - child = item->left; - } else { - dns_rbtnode_t *saved_parent, *saved_right; - int saved_color; - - /* - * This node has two children, so it cannot be directly - * deleted. Find its immediate in-order successor and - * move it to this location, then do the deletion at the - * old site of the successor. - */ - successor = item->right; - while (successor->left != NULL) { - successor = successor->left; - } - - /* - * The successor cannot possibly have a left child; - * if there is any child, it is on the right. - */ - if (successor->right != NULL) { - child = successor->right; - } - - /* - * Swap the two nodes; it would be simpler to just replace - * the value being deleted with that of the successor, - * but this rigamarole is done so the caller has complete - * control over the pointers (and memory allocation) of - * all of nodes. If just the key value were removed from - * the tree, the pointer to the node would be unchanged. - */ - - /* - * First, put the successor in the tree location of the - * node to be deleted. Save its existing tree pointer - * information, which will be needed when linking up - * delete to the successor's old location. - */ - saved_parent = successor->parent; - saved_right = successor->right; - saved_color = successor->color; - - if (item->is_root) { - *rootp = successor; - successor->is_root = true; - item->is_root = false; - } else if (item->parent->left == item) { - item->parent->left = successor; - } else { - item->parent->right = successor; - } - - successor->parent = item->parent; - successor->left = item->left; - successor->right = item->right; - successor->color = item->color; - - if (successor->left != NULL) { - successor->left->parent = successor; - } - if (successor->right != successor) { - successor->right->parent = successor; - } - - /* - * Now relink the node to be deleted into the - * successor's previous tree location. - */ - INSIST(!item->is_root); - - if (saved_parent == item) { - /* - * Node being deleted was successor's parent. - */ - successor->right = item; - item->parent = successor; - } else { - saved_parent->left = item; - item->parent = saved_parent; - } - - /* - * Original location of successor node has no left. - */ - item->left = NULL; - item->right = saved_right; - item->color = saved_color; - } - - /* - * Remove the node by removing the links from its parent. - */ - if (!item->is_root) { - if (item->parent->left == item) { - item->parent->left = child; - } else { - item->parent->right = child; - } - - if (child != NULL) { - child->parent = item->parent; - } - } else { - /* - * This is the root being deleted, and at this point - * it is known to have just one child. - */ - *rootp = child; - child->is_root = 1; - child->parent = item->parent; - } - - /* - * Fix color violations. - */ - if (IS_BLACK(item)) { - parent = item->parent; - - while (child != *rootp && IS_BLACK(child)) { - INSIST(child == NULL || !child->is_root); - - if (parent->left == child) { - sibling = parent->right; - - if (IS_RED(sibling)) { - sibling->color = BLACK; - parent->color = RED; - rotate_left(parent, rootp); - sibling = parent->right; - } - - INSIST(sibling != NULL); - - if (IS_BLACK(sibling->left) && - IS_BLACK(sibling->right)) - { - sibling->color = RED; - child = parent; - } else { - if (IS_BLACK(sibling->right)) { - sibling->left->color = BLACK; - sibling->color = RED; - rotate_right(sibling, rootp); - sibling = parent->right; - } - - sibling->color = parent->color; - parent->color = BLACK; - INSIST(sibling->right != NULL); - sibling->right->color = BLACK; - rotate_left(parent, rootp); - child = *rootp; - } - } else { - /* - * Child is parent's right child. - * Everything is done the same as above, - * except mirrored. - */ - sibling = parent->left; - - if (IS_RED(sibling)) { - sibling->color = BLACK; - parent->color = RED; - rotate_right(parent, rootp); - sibling = parent->left; - } - - INSIST(sibling != NULL); - - if (IS_BLACK(sibling->left) && - IS_BLACK(sibling->right)) - { - sibling->color = RED; - child = parent; - } else { - if (IS_BLACK(sibling->left)) { - sibling->right->color = BLACK; - sibling->color = RED; - rotate_left(sibling, rootp); - sibling = parent->left; - } - - sibling->color = parent->color; - parent->color = BLACK; - INSIST(sibling->left != NULL); - sibling->left->color = BLACK; - rotate_right(parent, rootp); - child = *rootp; - } - } - - parent = child->parent; - } - - if (IS_RED(child)) { - child->color = BLACK; - } - } -} - -static void -freenode(dns_rbt_t *rbt, dns_rbtnode_t **nodep) { - dns_rbtnode_t *node = *nodep; - *nodep = NULL; - - isc_mem_put(rbt->mctx, node, NODE_SIZE(node)); - - rbt->nodecount--; -} - -static void -deletetreeflat(dns_rbt_t *rbt, unsigned int quantum, bool unhash, - dns_rbtnode_t **nodep) { - dns_rbtnode_t *root = *nodep; - - while (root != NULL) { - /* - * If there is a left, right or down node, walk into it - * and iterate. - */ - if (root->left != NULL) { - dns_rbtnode_t *node = root; - root = root->left; - node->left = NULL; - } else if (root->right != NULL) { - dns_rbtnode_t *node = root; - root = root->right; - node->right = NULL; - } else if (root->down != NULL) { - dns_rbtnode_t *node = root; - root = root->down; - node->down = NULL; - } else { - /* - * There are no left, right or down nodes, so we - * can free this one and go back to its parent. - */ - dns_rbtnode_t *node = root; - root = root->parent; - - if (rbt->data_deleter != NULL && node->data != NULL) { - rbt->data_deleter(node->data, rbt->deleter_arg); - } - if (unhash) { - unhash_node(rbt, node); - } - /* - * Note: we don't call unhash_node() here as we - * are destroying the complete RBT tree. - */ -#if DNS_RBT_USEMAGIC - node->magic = 0; -#endif /* if DNS_RBT_USEMAGIC */ - freenode(rbt, &node); - if (quantum != 0 && --quantum == 0) { - break; - } - } - } - - *nodep = root; -} - -static size_t -getheight_helper(dns_rbtnode_t *node) { - size_t dl, dr; - size_t this_height, down_height; - - if (node == NULL) { - return (0); - } - - dl = getheight_helper(node->left); - dr = getheight_helper(node->right); - - this_height = ISC_MAX(dl + 1, dr + 1); - down_height = getheight_helper(node->down); - - return (ISC_MAX(this_height, down_height)); -} - -size_t -dns__rbt_getheight(dns_rbt_t *rbt) { - return (getheight_helper(rbt->root)); -} - -static bool -check_properties_helper(dns_rbtnode_t *node) { - if (node == NULL) { - return (true); - } - - if (IS_RED(node)) { - /* Root nodes must be BLACK. */ - if (node->is_root) { - return (false); - } - - /* Both children of RED nodes must be BLACK. */ - if (IS_RED(node->left) || IS_RED(node->right)) { - return (false); - } - } - - if ((node->down != NULL) && (!node->down->is_root)) { - return (false); - } - - if (node->is_root) { - if ((node->parent != NULL) && (node->parent->down != node)) { - return (false); - } - - if (get_upper_node(node) != node->parent) { - return (false); - } - } - - /* If node is assigned to the down_ pointer of its parent, it is - * a subtree root and must have the flag set. - */ - if (((!node->parent) || (node->parent->down == node)) && - (!node->is_root)) - { - return (false); - } - - /* Repeat tests with this node's children. */ - return (check_properties_helper(node->left) && - check_properties_helper(node->right) && - check_properties_helper(node->down)); -} - -static bool -check_black_distance_helper(dns_rbtnode_t *node, size_t *distance) { - size_t dl, dr, dd; - - if (node == NULL) { - *distance = 1; - return (true); - } - - if (!check_black_distance_helper(node->left, &dl)) { - return (false); - } - - if (!check_black_distance_helper(node->right, &dr)) { - return (false); - } - - if (!check_black_distance_helper(node->down, &dd)) { - return (false); - } - - /* Left and right side black node counts must match. */ - if (dl != dr) { - return (false); - } - - if (IS_BLACK(node)) { - dl++; - } - - *distance = dl; - - return (true); -} - -bool -dns__rbt_checkproperties(dns_rbt_t *rbt) { - size_t dd; - - if (!check_properties_helper(rbt->root)) { - return (false); - } - - /* Path from a given node to all its leaves must contain the - * same number of BLACK child nodes. This is done separately - * here instead of inside check_properties_helper() as - * it would take (n log n) complexity otherwise. - */ - return (check_black_distance_helper(rbt->root, &dd)); -} - -static void -dns_rbt_indent(FILE *f, int depth) { - int i; - - fprintf(f, "%4d ", depth); - - for (i = 0; i < depth; i++) { - fprintf(f, "- "); - } -} - -void -dns_rbt_printnodeinfo(dns_rbtnode_t *n, FILE *f) { - if (n == NULL) { - fprintf(f, "Null node\n"); - return; - } - - fprintf(f, "Node info for nodename: "); - printnodename(n, true, f); - fprintf(f, "\n"); - - fprintf(f, "n = %p\n", n); - - fprintf(f, "node lock address = %u\n", n->locknum); - - fprintf(f, "Parent: %p\n", n->parent); - fprintf(f, "Right: %p\n", n->right); - fprintf(f, "Left: %p\n", n->left); - fprintf(f, "Down: %p\n", n->down); - fprintf(f, "Data: %p\n", n->data); -} - -static void -printnodename(dns_rbtnode_t *node, bool quoted, FILE *f) { - isc_region_t r; - dns_name_t name; - char buffer[DNS_NAME_FORMATSIZE]; - dns_offsets_t offsets; - - r.length = node->namelen; - r.base = NAME(node); - - dns_name_init(&name, offsets); - dns_name_fromregion(&name, &r); - - dns_name_format(&name, buffer, sizeof(buffer)); - - if (quoted) { - fprintf(f, "\"%s\"", buffer); - } else { - fprintf(f, "%s", buffer); - } -} - -static void -print_text_helper(dns_rbtnode_t *root, dns_rbtnode_t *parent, int depth, - const char *direction, void (*data_printer)(FILE *, void *), - FILE *f) { - dns_rbt_indent(f, depth); - - if (root != NULL) { - printnodename(root, true, f); - fprintf(f, " (%s, %s", direction, - root->color == RED ? "RED" : "BLACK"); - - if ((!root->is_root && root->parent != parent) || - (root->is_root && depth > 0 && root->parent->down != root)) - { - fprintf(f, " (BAD parent pointer! -> "); - if (root->parent != NULL) { - printnodename(root->parent, true, f); - } else { - fprintf(f, "NULL"); - } - fprintf(f, ")"); - } - - fprintf(f, ")"); - - if (root->data != NULL && data_printer != NULL) { - fprintf(f, " data@%p: ", root->data); - data_printer(f, root->data); - } - fprintf(f, "\n"); - - depth++; - - if (root->color == RED && IS_RED(root->left)) { - fprintf(f, "** Red/Red color violation on left\n"); - } - print_text_helper(root->left, root, depth, "left", data_printer, - f); - - if (root->color == RED && IS_RED(root->right)) { - fprintf(f, "** Red/Red color violation on right\n"); - } - print_text_helper(root->right, root, depth, "right", - data_printer, f); - - print_text_helper(root->down, NULL, depth, "down", data_printer, - f); - } else { - fprintf(f, "NULL (%s)\n", direction); - } -} - -void -dns_rbt_printtext(dns_rbt_t *rbt, void (*data_printer)(FILE *, void *), - FILE *f) { - REQUIRE(VALID_RBT(rbt)); - - print_text_helper(rbt->root, NULL, 0, "root", data_printer, f); -} - -static int -print_dot_helper(dns_rbtnode_t *node, unsigned int *nodecount, - bool show_pointers, FILE *f) { - unsigned int l, r, d; - - if (node == NULL) { - return (0); - } - - l = print_dot_helper(node->left, nodecount, show_pointers, f); - r = print_dot_helper(node->right, nodecount, show_pointers, f); - d = print_dot_helper(node->down, nodecount, show_pointers, f); - - *nodecount += 1; - - fprintf(f, "node%u[label = \" | ", *nodecount); - printnodename(node, false, f); - fprintf(f, "|"); - - if (show_pointers) { - fprintf(f, "| n=%p| p=%p", node, node->parent); - } - - fprintf(f, "\"] ["); - - if (IS_RED(node)) { - fprintf(f, "color=red"); - } else { - fprintf(f, "color=black"); - } - - /* XXXMUKS: verify that IS_ROOT() indicates subtree root and not - * forest root. - */ - if (node->is_root) { - fprintf(f, ",penwidth=3"); - } - - if (node->data == NULL) { - fprintf(f, ",style=filled,fillcolor=lightgrey"); - } - - fprintf(f, "];\n"); - - if (node->left != NULL) { - fprintf(f, "\"node%u\":f0 -> \"node%u\":f1;\n", *nodecount, l); - } - - if (node->down != NULL) { - fprintf(f, "\"node%u\":f1 -> \"node%u\":f1 [penwidth=5];\n", - *nodecount, d); - } - if (node->right != NULL) { - fprintf(f, "\"node%u\":f2 -> \"node%u\":f1;\n", *nodecount, r); - } - - return (*nodecount); -} - -void -dns_rbt_printdot(dns_rbt_t *rbt, bool show_pointers, FILE *f) { - unsigned int nodecount = 0; - - REQUIRE(VALID_RBT(rbt)); - - fprintf(f, "digraph g {\n"); - fprintf(f, "node [shape = record,height=.1];\n"); - print_dot_helper(rbt->root, &nodecount, show_pointers, f); - fprintf(f, "}\n"); -} - -/* - * Chain Functions - */ - -void -dns_rbtnodechain_init(dns_rbtnodechain_t *chain) { - REQUIRE(chain != NULL); - - /* - * Initialize 'chain'. - */ - chain->end = NULL; - chain->level_count = 0; - chain->level_matches = 0; - memset(chain->levels, 0, sizeof(chain->levels)); - - chain->magic = CHAIN_MAGIC; -} - -isc_result_t -dns_rbtnodechain_current(dns_rbtnodechain_t *chain, dns_name_t *name, - dns_name_t *origin, dns_rbtnode_t **node) { - isc_result_t result = ISC_R_SUCCESS; - - REQUIRE(VALID_CHAIN(chain)); - - SET_IF_NOT_NULL(node, chain->end); - - if (chain->end == NULL) { - return (ISC_R_NOTFOUND); - } - - if (name != NULL) { - node_name(chain->end, name); - - if (chain->level_count == 0) { - /* - * Names in the top level tree are all absolute. - * Always make 'name' relative. - */ - INSIST(dns_name_isabsolute(name)); - - /* - * This is cheaper than - * dns_name_getlabelsequence(). - */ - name->labels--; - name->length--; - name->attributes.absolute = false; - } - } - - if (origin != NULL) { - if (chain->level_count > 0) { - result = chain_name(chain, origin, false); - } else { - dns_name_copy(dns_rootname, origin); - } - } - - return (result); -} - -isc_result_t -dns_rbtnodechain_prev(dns_rbtnodechain_t *chain, dns_name_t *name, - dns_name_t *origin) { - dns_rbtnode_t *current, *previous, *predecessor; - isc_result_t result = ISC_R_SUCCESS; - bool new_origin = false; - - REQUIRE(VALID_CHAIN(chain) && chain->end != NULL); - - predecessor = NULL; - - current = chain->end; - - if (current->left != NULL) { - /* - * Moving left one then right as far as possible is the - * previous node, at least for this level. - */ - current = current->left; - - while (current->right != NULL) { - current = current->right; - } - - predecessor = current; - } else { - /* - * No left links, so move toward the root. If at any - * point on the way there the link from parent to child - * is a right link, then the parent is the previous - * node, at least for this level. - */ - while (!current->is_root) { - previous = current; - current = current->parent; - - if (current->right == previous) { - predecessor = current; - break; - } - } - } - - if (predecessor != NULL) { - /* - * Found a predecessor node in this level. It might not - * really be the predecessor, however. - */ - if (predecessor->down != NULL) { - /* - * The predecessor is really down at least one - * level. Go down and as far right as possible, - * and repeat as long as the rightmost node has - * a down pointer. - */ - do { - /* - * XXX DCL Need to do something about - * origins here. See whether to go down, - * and if so whether it is truly what - * Bob calls a new origin. - */ - ADD_LEVEL(chain, predecessor); - predecessor = predecessor->down; - - /* XXX DCL duplicated from above; clever - * way to unduplicate? */ - - while (predecessor->right != NULL) { - predecessor = predecessor->right; - } - } while (predecessor->down != NULL); - - /* XXX DCL probably needs work on the concept */ - if (origin != NULL) { - new_origin = true; - } - } - } else if (chain->level_count > 0) { - /* - * Dang, didn't find a predecessor in this level. - * Got to the root of this level without having - * traversed any right links. Ascend the tree one - * level; the node that points to this tree is the - * predecessor. - */ - INSIST(chain->level_count > 0 && current->is_root); - predecessor = chain->levels[--chain->level_count]; - - /* XXX DCL probably needs work on the concept */ - /* - * Don't declare an origin change when the new origin is - * "." at the top level tree, because "." is declared as - * the origin for the second level tree. - */ - if (origin != NULL && - (chain->level_count > 0 || predecessor->offsetlen > 1)) - { - new_origin = true; - } - } - - if (predecessor != NULL) { - chain->end = predecessor; - - if (new_origin) { - result = dns_rbtnodechain_current(chain, name, origin, - NULL); - if (result == ISC_R_SUCCESS) { - result = DNS_R_NEWORIGIN; - } - } else { - result = dns_rbtnodechain_current(chain, name, NULL, - NULL); - } - } else { - result = ISC_R_NOMORE; - } - - return (result); -} - -isc_result_t -dns_rbtnodechain_down(dns_rbtnodechain_t *chain, dns_name_t *name, - dns_name_t *origin) { - dns_rbtnode_t *current, *successor; - isc_result_t result = ISC_R_SUCCESS; - bool new_origin = false; - - REQUIRE(VALID_CHAIN(chain) && chain->end != NULL); - - successor = NULL; - - current = chain->end; - - if (current->down != NULL) { - /* - * Don't declare an origin change when the new origin is - * "." at the second level tree, because "." is already - * declared as the origin for the top level tree. - */ - if (chain->level_count > 0 || current->offsetlen > 1) { - new_origin = true; - } - - ADD_LEVEL(chain, current); - current = current->down; - - while (current->left != NULL) { - current = current->left; - } - - successor = current; - } - - if (successor != NULL) { - chain->end = successor; - - /* - * It is not necessary to use dns_rbtnodechain_current - * like the other functions because this function will - * never find a node in the topmost level. This is - * because the root level will never be more than one - * name, and everything in the megatree is a successor - * to that node, down at the second level or below. - */ - - if (name != NULL) { - node_name(chain->end, name); - } - - if (new_origin) { - if (origin != NULL) { - result = chain_name(chain, origin, false); - } - - if (result == ISC_R_SUCCESS) { - result = DNS_R_NEWORIGIN; - } - } else { - result = ISC_R_SUCCESS; - } - } else { - result = ISC_R_NOMORE; - } - - return (result); -} - -isc_result_t -dns_rbtnodechain_nextflat(dns_rbtnodechain_t *chain, dns_name_t *name) { - dns_rbtnode_t *current, *previous, *successor; - isc_result_t result = ISC_R_SUCCESS; - - REQUIRE(VALID_CHAIN(chain) && chain->end != NULL); - - successor = NULL; - - current = chain->end; - - if (current->right == NULL) { - while (!current->is_root) { - previous = current; - current = current->parent; - - if (current->left == previous) { - successor = current; - break; - } - } - } else { - current = current->right; - - while (current->left != NULL) { - current = current->left; - } - - successor = current; - } - - if (successor != NULL) { - chain->end = successor; - - if (name != NULL) { - node_name(chain->end, name); - } - - result = ISC_R_SUCCESS; - } else { - result = ISC_R_NOMORE; - } - - return (result); -} - -isc_result_t -dns_rbtnodechain_next(dns_rbtnodechain_t *chain, dns_name_t *name, - dns_name_t *origin) { - dns_rbtnode_t *current, *previous, *successor; - isc_result_t result = ISC_R_SUCCESS; - bool new_origin = false; - - REQUIRE(VALID_CHAIN(chain) && chain->end != NULL); - - successor = NULL; - - current = chain->end; - - /* - * If there is a level below this node, the next node is the - * leftmost node of the next level. - */ - if (current->down != NULL) { - /* - * Don't declare an origin change when the new origin is - * "." at the second level tree, because "." is already - * declared as the origin for the top level tree. - */ - if (chain->level_count > 0 || current->offsetlen > 1) { - new_origin = true; - } - - ADD_LEVEL(chain, current); - current = current->down; - - while (current->left != NULL) { - current = current->left; - } - - successor = current; - } else if (current->right == NULL) { - /* - * The successor is up, either in this level or a - * previous one. Head back toward the root of the tree, - * looking for any path that was via a left link; the - * successor is the node that has that left link. In - * the event the root of the level is reached without - * having traversed any left links, ascend one level and - * look for either a right link off the point of ascent, - * or search for a left link upward again, repeating - * ascends until either case is true. - */ - do { - while (!current->is_root) { - previous = current; - current = current->parent; - - if (current->left == previous) { - successor = current; - break; - } - } - - if (successor == NULL) { - /* - * Reached the root without having - * traversed any left pointers, so this - * level is done. - */ - if (chain->level_count == 0) { - /* - * If the tree we are iterating - * over was modified since this - * chain was initialized in a - * way that caused node splits - * to occur, "current" may now - * be pointing to a root node - * which appears to be at level - * 0, but still has a parent. If - * that happens, abort. - * Otherwise, we are done - * looking for a successor as we - * really reached the root node - * on level 0. - */ - INSIST(current->parent == NULL); - break; - } - - current = chain->levels[--chain->level_count]; - new_origin = true; - - if (current->right != NULL) { - break; - } - } - } while (successor == NULL); - } - - if (successor == NULL && current->right != NULL) { - current = current->right; - - while (current->left != NULL) { - current = current->left; - } - - successor = current; - } - - if (successor != NULL) { - /* - * If we determine that the current node is the - * successor to itself, we will run into an infinite - * loop, so abort instead. - */ - INSIST(chain->end != successor); - - chain->end = successor; - - /* - * It is not necessary to use dns_rbtnodechain_current - * like the other functions because this function will - * never find a node in the topmost level. This is - * because the root level will never be more than one - * name, and everything in the megatree is a successor - * to that node, down at the second level or below. - */ - - if (name != NULL) { - node_name(chain->end, name); - } - - if (new_origin) { - if (origin != NULL) { - result = chain_name(chain, origin, false); - } - - if (result == ISC_R_SUCCESS) { - result = DNS_R_NEWORIGIN; - } - } else { - result = ISC_R_SUCCESS; - } - } else { - result = ISC_R_NOMORE; - } - - return (result); -} - -isc_result_t -dns_rbtnodechain_first(dns_rbtnodechain_t *chain, dns_rbt_t *rbt, - dns_name_t *name, dns_name_t *origin) - -{ - isc_result_t result; - - REQUIRE(VALID_RBT(rbt)); - REQUIRE(VALID_CHAIN(chain)); - - dns_rbtnodechain_reset(chain); - - chain->end = rbt->root; - - result = dns_rbtnodechain_current(chain, name, origin, NULL); - - if (result == ISC_R_SUCCESS) { - result = DNS_R_NEWORIGIN; - } - - return (result); -} - -isc_result_t -dns_rbtnodechain_last(dns_rbtnodechain_t *chain, dns_rbt_t *rbt, - dns_name_t *name, dns_name_t *origin) - -{ - isc_result_t result; - - REQUIRE(VALID_RBT(rbt)); - REQUIRE(VALID_CHAIN(chain)); - - dns_rbtnodechain_reset(chain); - - result = move_chain_to_last(chain, rbt->root); - if (result != ISC_R_SUCCESS) { - return (result); - } - - result = dns_rbtnodechain_current(chain, name, origin, NULL); - - if (result == ISC_R_SUCCESS) { - result = DNS_R_NEWORIGIN; - } - - return (result); -} - -void -dns_rbtnodechain_reset(dns_rbtnodechain_t *chain) { - REQUIRE(VALID_CHAIN(chain)); - - /* - * Free any dynamic storage associated with 'chain', and then - * reinitialize 'chain'. - */ - chain->end = NULL; - chain->level_count = 0; - chain->level_matches = 0; -} - -void -dns_rbtnodechain_invalidate(dns_rbtnodechain_t *chain) { - /* - * Free any dynamic storage associated with 'chain', and then - * invalidate 'chain'. - */ - - dns_rbtnodechain_reset(chain); - - chain->magic = 0; -} - -/* XXXMUKS: - * - * - worth removing inline as static functions are inlined automatically - * where suitable by modern compilers. - * - bump the size of dns_rbt.nodecount to size_t. - * - the dumpfile header also contains a nodecount that is unsigned - * int. If large files (> 2^32 nodes) are to be supported, the - * allocation for this field should be increased. - */ diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c deleted file mode 100644 index 4b217b72e0..0000000000 --- a/lib/dns/rbtdb.c +++ /dev/null @@ -1,5000 +0,0 @@ -/* - * Copyright (C) Internet Systems Consortium, Inc. ("ISC") - * - * SPDX-License-Identifier: MPL-2.0 - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, you can obtain one at https://mozilla.org/MPL/2.0/. - * - * See the COPYRIGHT file distributed with this work for additional - * information regarding copyright ownership. - */ - -/*! \file */ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "db_p.h" -#include "rbtdb_p.h" - -#define CHECK(op) \ - do { \ - result = (op); \ - if (result != ISC_R_SUCCESS) \ - goto failure; \ - } while (0) - -#define EXISTS(header) \ - ((atomic_load_acquire(&(header)->attributes) & \ - DNS_SLABHEADERATTR_NONEXISTENT) == 0) -#define NONEXISTENT(header) \ - ((atomic_load_acquire(&(header)->attributes) & \ - DNS_SLABHEADERATTR_NONEXISTENT) != 0) -#define IGNORE(header) \ - ((atomic_load_acquire(&(header)->attributes) & \ - DNS_SLABHEADERATTR_IGNORE) != 0) -#define NXDOMAIN(header) \ - ((atomic_load_acquire(&(header)->attributes) & \ - DNS_SLABHEADERATTR_NXDOMAIN) != 0) -#define STALE(header) \ - ((atomic_load_acquire(&(header)->attributes) & \ - DNS_SLABHEADERATTR_STALE) != 0) -#define STALE_WINDOW(header) \ - ((atomic_load_acquire(&(header)->attributes) & \ - DNS_SLABHEADERATTR_STALE_WINDOW) != 0) -#define RESIGN(header) \ - ((atomic_load_acquire(&(header)->attributes) & \ - DNS_SLABHEADERATTR_RESIGN) != 0) -#define OPTOUT(header) \ - ((atomic_load_acquire(&(header)->attributes) & \ - DNS_SLABHEADERATTR_OPTOUT) != 0) -#define NEGATIVE(header) \ - ((atomic_load_acquire(&(header)->attributes) & \ - DNS_SLABHEADERATTR_NEGATIVE) != 0) -#define PREFETCH(header) \ - ((atomic_load_acquire(&(header)->attributes) & \ - DNS_SLABHEADERATTR_PREFETCH) != 0) -#define CASESET(header) \ - ((atomic_load_acquire(&(header)->attributes) & \ - DNS_SLABHEADERATTR_CASESET) != 0) -#define ZEROTTL(header) \ - ((atomic_load_acquire(&(header)->attributes) & \ - DNS_SLABHEADERATTR_ZEROTTL) != 0) -#define ANCIENT(header) \ - ((atomic_load_acquire(&(header)->attributes) & \ - DNS_SLABHEADERATTR_ANCIENT) != 0) -#define STATCOUNT(header) \ - ((atomic_load_acquire(&(header)->attributes) & \ - DNS_SLABHEADERATTR_STATCOUNT) != 0) - -#define STALE_TTL(header, rbtdb) \ - (NXDOMAIN(header) ? 0 : rbtdb->common.serve_stale_ttl) - -#define ACTIVE(header, now) \ - (((header)->ttl > (now)) || ((header)->ttl == (now) && ZEROTTL(header))) - -#define DEFAULT_NODE_LOCK_COUNT 7 /*%< Should be prime. */ - -#define EXPIREDOK(rbtiterator) \ - (((rbtiterator)->common.options & DNS_DB_EXPIREDOK) != 0) - -#define STALEOK(rbtiterator) \ - (((rbtiterator)->common.options & DNS_DB_STALEOK) != 0) - -#define KEEPSTALE(rbtdb) ((rbtdb)->common.serve_stale_ttl > 0) - -#define RBTDBITER_NSEC3_ORIGIN_NODE(rbtdb, iterator) \ - ((iterator)->current == &(iterator)->nsec3chain && \ - (iterator)->node == (rbtdb)->nsec3_origin_node) - -/*% - * Number of buckets for cache DB entries (locks, LRU lists, TTL heaps). - * There is a tradeoff issue about configuring this value: if this is too - * small, it may cause heavier contention between threads; if this is too large, - * LRU purge algorithm won't work well (entries tend to be purged prematurely). - * The default value should work well for most environments, but this can - * also be configurable at compilation time via the - * DNS_RBTDB_CACHE_NODE_LOCK_COUNT variable. This value must be larger than - * 1 due to the assumption of dns__cacherbt_overmem(). - */ -#ifdef DNS_RBTDB_CACHE_NODE_LOCK_COUNT -#if DNS_RBTDB_CACHE_NODE_LOCK_COUNT <= 1 -#error "DNS_RBTDB_CACHE_NODE_LOCK_COUNT must be larger than 1" -#else /* if DNS_RBTDB_CACHE_NODE_LOCK_COUNT <= 1 */ -#define DEFAULT_CACHE_NODE_LOCK_COUNT DNS_RBTDB_CACHE_NODE_LOCK_COUNT -#endif /* if DNS_RBTDB_CACHE_NODE_LOCK_COUNT <= 1 */ -#else /* ifdef DNS_RBTDB_CACHE_NODE_LOCK_COUNT */ -#define DEFAULT_CACHE_NODE_LOCK_COUNT 17 -#endif /* DNS_RBTDB_CACHE_NODE_LOCK_COUNT */ - -/* - * This defines the number of headers that we try to expire each time the - * expire_ttl_headers() is run. The number should be small enough, so the - * TTL-based header expiration doesn't take too long, but it should be large - * enough, so we expire enough headers if their TTL is clustered. - */ -#define DNS_RBTDB_EXPIRE_TTL_COUNT 10 - -static void -delete_callback(void *data, void *arg); -static void -prune_tree(void *arg); -static void -free_gluetable(struct cds_lfht *glue_table); - -static void -rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp DNS__DB_FLARG); -static isc_result_t -rdatasetiter_first(dns_rdatasetiter_t *iterator DNS__DB_FLARG); -static isc_result_t -rdatasetiter_next(dns_rdatasetiter_t *iterator DNS__DB_FLARG); -static void -rdatasetiter_current(dns_rdatasetiter_t *iterator, - dns_rdataset_t *rdataset DNS__DB_FLARG); - -static dns_rdatasetitermethods_t rdatasetiter_methods = { - rdatasetiter_destroy, rdatasetiter_first, rdatasetiter_next, - rdatasetiter_current -}; - -typedef struct rbtdb_rdatasetiter { - dns_rdatasetiter_t common; - dns_slabheader_t *current; -} rbtdb_rdatasetiter_t; - -/* - * Note that these iterators, unless created with either DNS_DB_NSEC3ONLY or - * DNS_DB_NONSEC3, will transparently move between the last node of the - * "regular" RBT ("chain" field) and the root node of the NSEC3 RBT - * ("nsec3chain" field) of the database in question, as if the latter was a - * successor to the former in lexical order. The "current" field always holds - * the address of either "chain" or "nsec3chain", depending on which RBT is - * being traversed at given time. - */ -static void -dbiterator_destroy(dns_dbiterator_t **iteratorp DNS__DB_FLARG); -static isc_result_t -dbiterator_first(dns_dbiterator_t *iterator DNS__DB_FLARG); -static isc_result_t -dbiterator_last(dns_dbiterator_t *iterator DNS__DB_FLARG); -static isc_result_t -dbiterator_seek(dns_dbiterator_t *iterator, - const dns_name_t *name DNS__DB_FLARG); -static isc_result_t -dbiterator_prev(dns_dbiterator_t *iterator DNS__DB_FLARG); -static isc_result_t -dbiterator_next(dns_dbiterator_t *iterator DNS__DB_FLARG); -static isc_result_t -dbiterator_current(dns_dbiterator_t *iterator, dns_dbnode_t **nodep, - dns_name_t *name DNS__DB_FLARG); -static isc_result_t -dbiterator_pause(dns_dbiterator_t *iterator); -static isc_result_t -dbiterator_origin(dns_dbiterator_t *iterator, dns_name_t *name); - -static dns_dbiteratormethods_t dbiterator_methods = { - dbiterator_destroy, dbiterator_first, dbiterator_last, - dbiterator_seek, dbiterator_prev, dbiterator_next, - dbiterator_current, dbiterator_pause, dbiterator_origin -}; - -/* - * If 'paused' is true, then the tree lock is not being held. - */ -typedef struct rbtdb_dbiterator { - dns_dbiterator_t common; - bool paused; - bool new_origin; - isc_rwlocktype_t tree_locked; - isc_result_t result; - dns_fixedname_t name; - dns_fixedname_t origin; - dns_rbtnodechain_t chain; - dns_rbtnodechain_t nsec3chain; - dns_rbtnodechain_t *current; - dns_rbtnode_t *node; - enum { full, nonsec3, nsec3only } nsec3mode; -} rbtdb_dbiterator_t; - -static void -free_rbtdb(dns_rbtdb_t *rbtdb, bool log); -static void -setnsec3parameters(dns_db_t *db, dns_rbtdb_version_t *version); - -/*% - * 'init_count' is used to initialize 'newheader->count' which inturn - * is used to determine where in the cycle rrset-order cyclic starts. - * We don't lock this as we don't care about simultaneous updates. - */ -static atomic_uint_fast16_t init_count = 0; - -/* - * Locking - * - * If a routine is going to lock more than one lock in this module, then - * the locking must be done in the following order: - * - * Tree Lock - * - * Node Lock (Only one from the set may be locked at one time by - * any caller) - * - * Database Lock - * - * Failure to follow this hierarchy can result in deadlock. - */ - -/* - * Deleting Nodes - * - * For zone databases the node for the origin of the zone MUST NOT be deleted. - */ - -/* - * DB Routines - */ - -static void -update_rrsetstats(dns_stats_t *stats, const dns_typepair_t htype, - const uint_least16_t hattributes, const bool increment) { - dns_rdatastatstype_t statattributes = 0; - dns_rdatastatstype_t base = 0; - dns_rdatastatstype_t type; - dns_slabheader_t *header = &(dns_slabheader_t){ - .type = htype, - .attributes = hattributes, - }; - - if (!EXISTS(header) || !STATCOUNT(header)) { - return; - } - - if (NEGATIVE(header)) { - if (NXDOMAIN(header)) { - statattributes = DNS_RDATASTATSTYPE_ATTR_NXDOMAIN; - } else { - statattributes = DNS_RDATASTATSTYPE_ATTR_NXRRSET; - base = DNS_TYPEPAIR_COVERS(header->type); - } - } else { - base = DNS_TYPEPAIR_TYPE(header->type); - } - - if (STALE(header)) { - statattributes |= DNS_RDATASTATSTYPE_ATTR_STALE; - } - if (ANCIENT(header)) { - statattributes |= DNS_RDATASTATSTYPE_ATTR_ANCIENT; - } - - type = DNS_RDATASTATSTYPE_VALUE(base, statattributes); - if (increment) { - dns_rdatasetstats_increment(stats, type); - } else { - dns_rdatasetstats_decrement(stats, type); - } -} - -void -dns__rbtdb_setttl(dns_slabheader_t *header, dns_ttl_t newttl) { - dns_ttl_t oldttl = header->ttl; - - header->ttl = newttl; - - if (header->db == NULL || !dns_db_iscache(header->db)) { - return; - } - - /* - * This is a cache. Adjust the heaps if necessary. - */ - if (header->heap == NULL || header->heap_index == 0 || newttl == oldttl) - { - return; - } - - if (newttl < oldttl) { - isc_heap_increased(header->heap, header->heap_index); - } else { - isc_heap_decreased(header->heap, header->heap_index); - } - - if (newttl == 0) { - isc_heap_delete(header->heap, header->heap_index); - } -} - -/*% - * These functions allow the heap code to rank the priority of each - * element. It returns true if v1 happens "sooner" than v2. - */ -static bool -ttl_sooner(void *v1, void *v2) { - dns_slabheader_t *h1 = v1; - dns_slabheader_t *h2 = v2; - - return (h1->ttl < h2->ttl); -} - -/*% - * Return which RRset should be resigned sooner. If the RRsets have the - * same signing time, prefer the other RRset over the SOA RRset. - */ -static bool -resign_sooner(void *v1, void *v2) { - dns_slabheader_t *h1 = v1; - dns_slabheader_t *h2 = v2; - - return (h1->resign < h2->resign || - (h1->resign == h2->resign && h1->resign_lsb < h2->resign_lsb) || - (h1->resign == h2->resign && h1->resign_lsb == h2->resign_lsb && - h2->type == DNS_SIGTYPE(dns_rdatatype_soa))); -} - -/*% - * This function sets the heap index into the header. - */ -static void -set_index(void *what, unsigned int idx) { - dns_slabheader_t *h = what; - - h->heap_index = idx; -} - -/*% - * Work out how many nodes can be deleted in the time between two - * requests to the nameserver. Smooth the resulting number and use it - * as a estimate for the number of nodes to be deleted in the next - * iteration. - */ -static unsigned int -adjust_quantum(unsigned int old, isc_time_t *start) { - unsigned int pps = dns_pps; /* packets per second */ - unsigned int interval; - uint64_t usecs; - isc_time_t end; - unsigned int nodes; - - if (pps < 100) { - pps = 100; - } - end = isc_time_now(); - - interval = 1000000 / pps; /* interval in usec */ - if (interval == 0) { - interval = 1; - } - usecs = isc_time_microdiff(&end, start); - if (usecs == 0) { - /* - * We were unable to measure the amount of time taken. - * Double the nodes deleted next time. - */ - old *= 2; - if (old > 1000) { - old = 1000; - } - return (old); - } - nodes = old * interval; - nodes /= (unsigned int)usecs; - if (nodes == 0) { - nodes = 1; - } else if (nodes > 1000) { - nodes = 1000; - } - - /* Smooth */ - nodes = (nodes + old * 3) / 4; - - if (nodes != old) { - isc_log_write(DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, - ISC_LOG_DEBUG(1), - "adjust_quantum: old=%d, new=%d", old, nodes); - } - - return (nodes); -} - -static void -free_rbtdb_callback(void *arg) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)arg; - - free_rbtdb(rbtdb, true); -} - -static void -free_rbtdb(dns_rbtdb_t *rbtdb, bool log) { - unsigned int i; - isc_result_t result; - char buf[DNS_NAME_FORMATSIZE]; - dns_rbt_t **treep = NULL; - isc_time_t start; - - REQUIRE(rbtdb->current_version != NULL || EMPTY(rbtdb->open_versions)); - REQUIRE(rbtdb->future_version == NULL); - - if (rbtdb->current_version != NULL) { - isc_refcount_decrementz(&rbtdb->current_version->references); - - isc_refcount_destroy(&rbtdb->current_version->references); - UNLINK(rbtdb->open_versions, rbtdb->current_version, link); - isc_rwlock_destroy(&rbtdb->current_version->rwlock); - isc_mem_put(rbtdb->common.mctx, rbtdb->current_version, - sizeof(*rbtdb->current_version)); - } - - /* - * We assume the number of remaining dead nodes is reasonably small; - * the overhead of unlinking all nodes here should be negligible. - */ - for (i = 0; i < rbtdb->node_lock_count; i++) { - dns_rbtnode_t *node = NULL; - - node = ISC_LIST_HEAD(rbtdb->deadnodes[i]); - while (node != NULL) { - ISC_LIST_UNLINK(rbtdb->deadnodes[i], node, deadlink); - node = ISC_LIST_HEAD(rbtdb->deadnodes[i]); - } - } - - rbtdb->quantum = (rbtdb->loop != NULL) ? 100 : 0; - - for (;;) { - /* - * pick the next tree to (start to) destroy - */ - treep = &rbtdb->tree; - if (*treep == NULL) { - treep = &rbtdb->nsec; - if (*treep == NULL) { - treep = &rbtdb->nsec3; - /* - * we're finished after clear cutting - */ - if (*treep == NULL) { - break; - } - } - } - - start = isc_time_now(); - result = dns_rbt_destroy(treep, rbtdb->quantum); - if (result == ISC_R_QUOTA) { - INSIST(rbtdb->loop != NULL); - if (rbtdb->quantum != 0) { - rbtdb->quantum = adjust_quantum(rbtdb->quantum, - &start); - } - isc_async_run(rbtdb->loop, free_rbtdb_callback, rbtdb); - return; - } - INSIST(result == ISC_R_SUCCESS && *treep == NULL); - } - - if (log) { - if (dns_name_dynamic(&rbtdb->common.origin)) { - dns_name_format(&rbtdb->common.origin, buf, - sizeof(buf)); - } else { - strlcpy(buf, "", sizeof(buf)); - } - isc_log_write(DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, - ISC_LOG_DEBUG(1), "done free_rbtdb(%s)", buf); - } - if (dns_name_dynamic(&rbtdb->common.origin)) { - dns_name_free(&rbtdb->common.origin, rbtdb->common.mctx); - } - for (i = 0; i < rbtdb->node_lock_count; i++) { - isc_refcount_destroy(&rbtdb->node_locks[i].references); - NODE_DESTROYLOCK(&rbtdb->node_locks[i].lock); - } - - /* - * Clean up LRU / re-signing order lists. - */ - if (rbtdb->lru != NULL) { - for (i = 0; i < rbtdb->node_lock_count; i++) { - INSIST(ISC_LIST_EMPTY(rbtdb->lru[i])); - } - isc_mem_cput(rbtdb->common.mctx, rbtdb->lru, - rbtdb->node_lock_count, - sizeof(dns_slabheaderlist_t)); - } - /* - * Clean up dead node buckets. - */ - if (rbtdb->deadnodes != NULL) { - for (i = 0; i < rbtdb->node_lock_count; i++) { - INSIST(ISC_LIST_EMPTY(rbtdb->deadnodes[i])); - } - isc_mem_cput(rbtdb->common.mctx, rbtdb->deadnodes, - rbtdb->node_lock_count, sizeof(dns_rbtnodelist_t)); - } - /* - * Clean up heap objects. - */ - if (rbtdb->heaps != NULL) { - for (i = 0; i < rbtdb->node_lock_count; i++) { - isc_heap_destroy(&rbtdb->heaps[i]); - } - isc_mem_cput(rbtdb->hmctx, rbtdb->heaps, rbtdb->node_lock_count, - sizeof(isc_heap_t *)); - } - - if (rbtdb->rrsetstats != NULL) { - dns_stats_detach(&rbtdb->rrsetstats); - } - if (rbtdb->cachestats != NULL) { - isc_stats_detach(&rbtdb->cachestats); - } - if (rbtdb->gluecachestats != NULL) { - isc_stats_detach(&rbtdb->gluecachestats); - } - - isc_mem_cput(rbtdb->common.mctx, rbtdb->node_locks, - rbtdb->node_lock_count, sizeof(db_nodelock_t)); - TREE_DESTROYLOCK(&rbtdb->tree_lock); - isc_refcount_destroy(&rbtdb->common.references); - if (rbtdb->loop != NULL) { - isc_loop_detach(&rbtdb->loop); - } - - isc_rwlock_destroy(&rbtdb->lock); - rbtdb->common.magic = 0; - rbtdb->common.impmagic = 0; - isc_mem_detach(&rbtdb->hmctx); - - if (rbtdb->common.update_listeners != NULL) { - INSIST(!cds_lfht_destroy(rbtdb->common.update_listeners, NULL)); - } - - isc_mem_putanddetach(&rbtdb->common.mctx, rbtdb, sizeof(*rbtdb)); -} - -void -dns__rbtdb_destroy(dns_db_t *arg) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)arg; - bool want_free = false; - unsigned int i; - unsigned int inactive = 0; - - /* XXX check for open versions here */ - - if (rbtdb->soanode != NULL) { - dns_db_detachnode((dns_db_t *)rbtdb, &rbtdb->soanode); - } - if (rbtdb->nsnode != NULL) { - dns_db_detachnode((dns_db_t *)rbtdb, &rbtdb->nsnode); - } - - /* - * The current version's glue table needs to be freed early - * so the nodes are dereferenced before we check the active - * node count below. - */ - if (rbtdb->current_version != NULL) { - free_gluetable(rbtdb->current_version->glue_table); - } - - /* - * Even though there are no external direct references, there still - * may be nodes in use. - */ - for (i = 0; i < rbtdb->node_lock_count; i++) { - isc_rwlocktype_t nodelock = isc_rwlocktype_none; - NODE_WRLOCK(&rbtdb->node_locks[i].lock, &nodelock); - rbtdb->node_locks[i].exiting = true; - if (isc_refcount_current(&rbtdb->node_locks[i].references) == 0) - { - inactive++; - } - NODE_UNLOCK(&rbtdb->node_locks[i].lock, &nodelock); - } - - if (inactive != 0) { - RWLOCK(&rbtdb->lock, isc_rwlocktype_write); - rbtdb->active -= inactive; - if (rbtdb->active == 0) { - want_free = true; - } - RWUNLOCK(&rbtdb->lock, isc_rwlocktype_write); - if (want_free) { - char buf[DNS_NAME_FORMATSIZE]; - if (dns_name_dynamic(&rbtdb->common.origin)) { - dns_name_format(&rbtdb->common.origin, buf, - sizeof(buf)); - } else { - strlcpy(buf, "", sizeof(buf)); - } - isc_log_write(DNS_LOGCATEGORY_DATABASE, - DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1), - "calling free_rbtdb(%s)", buf); - free_rbtdb(rbtdb, true); - } - } -} - -void -dns__rbtdb_currentversion(dns_db_t *db, dns_dbversion_t **versionp) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - dns_rbtdb_version_t *version = NULL; - - REQUIRE(VALID_RBTDB(rbtdb)); - - RWLOCK(&rbtdb->lock, isc_rwlocktype_read); - version = rbtdb->current_version; - isc_refcount_increment(&version->references); - RWUNLOCK(&rbtdb->lock, isc_rwlocktype_read); - - *versionp = (dns_dbversion_t *)version; -} - -static dns_rbtdb_version_t * -allocate_version(isc_mem_t *mctx, uint32_t serial, unsigned int references, - bool writer) { - dns_rbtdb_version_t *version = isc_mem_get(mctx, sizeof(*version)); - *version = (dns_rbtdb_version_t){ - .serial = serial, - .writer = writer, - .changed_list = ISC_LIST_INITIALIZER, - .resigned_list = ISC_LIST_INITIALIZER, - .link = ISC_LINK_INITIALIZER, - .glue_table = cds_lfht_new(GLUETABLE_INIT_SIZE, - GLUETABLE_MIN_SIZE, 0, - CDS_LFHT_AUTO_RESIZE, NULL), - }; - - isc_rwlock_init(&version->rwlock); - isc_refcount_init(&version->references, references); - - return (version); -} - -isc_result_t -dns__rbtdb_newversion(dns_db_t *db, dns_dbversion_t **versionp) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - dns_rbtdb_version_t *version = NULL; - - REQUIRE(VALID_RBTDB(rbtdb)); - REQUIRE(versionp != NULL && *versionp == NULL); - REQUIRE(rbtdb->future_version == NULL); - - RWLOCK(&rbtdb->lock, isc_rwlocktype_write); - RUNTIME_CHECK(rbtdb->next_serial != 0); /* XXX Error? */ - version = allocate_version(rbtdb->common.mctx, rbtdb->next_serial, 1, - true); - version->rbtdb = rbtdb; - version->commit_ok = true; - version->secure = rbtdb->current_version->secure; - version->havensec3 = rbtdb->current_version->havensec3; - if (version->havensec3) { - version->flags = rbtdb->current_version->flags; - version->iterations = rbtdb->current_version->iterations; - version->hash = rbtdb->current_version->hash; - version->salt_length = rbtdb->current_version->salt_length; - memmove(version->salt, rbtdb->current_version->salt, - version->salt_length); - } else { - version->flags = 0; - version->iterations = 0; - version->hash = 0; - version->salt_length = 0; - memset(version->salt, 0, sizeof(version->salt)); - } - RWLOCK(&rbtdb->current_version->rwlock, isc_rwlocktype_read); - version->records = rbtdb->current_version->records; - version->xfrsize = rbtdb->current_version->xfrsize; - RWUNLOCK(&rbtdb->current_version->rwlock, isc_rwlocktype_read); - rbtdb->next_serial++; - rbtdb->future_version = version; - RWUNLOCK(&rbtdb->lock, isc_rwlocktype_write); - - *versionp = (dns_dbversion_t *)version; - - return (ISC_R_SUCCESS); -} - -void -dns__rbtdb_attachversion(dns_db_t *db, dns_dbversion_t *source, - dns_dbversion_t **targetp) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - dns_rbtdb_version_t *rbtversion = (dns_rbtdb_version_t *)source; - - REQUIRE(VALID_RBTDB(rbtdb)); - INSIST(rbtversion != NULL && rbtversion->rbtdb == rbtdb); - - isc_refcount_increment(&rbtversion->references); - - *targetp = source; -} - -static rbtdb_changed_t * -add_changed(dns_slabheader_t *header, - dns_rbtdb_version_t *version DNS__DB_FLARG) { - rbtdb_changed_t *changed = NULL; - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)header->db; - - /* - * Caller must be holding the node lock if its reference must be - * protected by the lock. - */ - - changed = isc_mem_get(rbtdb->common.mctx, sizeof(*changed)); - - RWLOCK(&rbtdb->lock, isc_rwlocktype_write); - - REQUIRE(version->writer); - - if (changed != NULL) { - dns_rbtnode_t *node = (dns_rbtnode_t *)header->node; - uint_fast32_t refs = isc_refcount_increment(&node->references); -#if DNS_DB_NODETRACE - fprintf(stderr, - "incr:node:%s:%s:%u:%p->references = %" PRIuFAST32 "\n", - func, file, line, node, refs + 1); -#else - UNUSED(refs); -#endif - changed->node = node; - changed->dirty = false; - ISC_LIST_INITANDAPPEND(version->changed_list, changed, link); - } else { - version->commit_ok = false; - } - - RWUNLOCK(&rbtdb->lock, isc_rwlocktype_write); - - return (changed); -} - -static void -rollback_node(dns_rbtnode_t *node, uint32_t serial) { - dns_slabheader_t *header = NULL, *dcurrent = NULL; - bool make_dirty = false; - - /* - * Caller must hold the node lock. - */ - - /* - * We set the IGNORE attribute on rdatasets with serial number - * 'serial'. When the reference count goes to zero, these rdatasets - * will be cleaned up; until that time, they will be ignored. - */ - for (header = node->data; header != NULL; header = header->next) { - if (header->serial == serial) { - DNS_SLABHEADER_SETATTR(header, - DNS_SLABHEADERATTR_IGNORE); - make_dirty = true; - } - for (dcurrent = header->down; dcurrent != NULL; - dcurrent = dcurrent->down) - { - if (dcurrent->serial == serial) { - DNS_SLABHEADER_SETATTR( - dcurrent, DNS_SLABHEADERATTR_IGNORE); - make_dirty = true; - } - } - } - if (make_dirty) { - node->dirty = 1; - } -} - -void -dns__rbtdb_mark(dns_slabheader_t *header, uint_least16_t flag) { - uint_least16_t attributes = atomic_load_acquire(&header->attributes); - uint_least16_t newattributes = 0; - dns_stats_t *stats = NULL; - - /* - * If we are already ancient there is nothing to do. - */ - do { - if ((attributes & flag) != 0) { - return; - } - newattributes = attributes | flag; - } while (!atomic_compare_exchange_weak_acq_rel( - &header->attributes, &attributes, newattributes)); - - /* - * Decrement and increment the stats counter for the appropriate - * RRtype. - */ - stats = dns_db_getrrsetstats(header->db); - if (stats != NULL) { - update_rrsetstats(stats, header->type, attributes, false); - update_rrsetstats(stats, header->type, newattributes, true); - } -} - -static void -mark_ancient(dns_slabheader_t *header) { - dns__rbtdb_setttl(header, 0); - dns__rbtdb_mark(header, DNS_SLABHEADERATTR_ANCIENT); - RBTDB_HEADERNODE(header)->dirty = 1; -} - -static void -clean_stale_headers(dns_slabheader_t *top) { - dns_slabheader_t *d = NULL, *down_next = NULL; - - for (d = top->down; d != NULL; d = down_next) { - down_next = d->down; - dns_slabheader_destroy(&d); - } - top->down = NULL; -} - -static void -clean_cache_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node) { - dns_slabheader_t *current = NULL, *top_prev = NULL, *top_next = NULL; - - /* - * Caller must be holding the node lock. - */ - - for (current = node->data; current != NULL; current = top_next) { - top_next = current->next; - clean_stale_headers(current); - /* - * If current is nonexistent, ancient, or stale and - * we are not keeping stale, we can clean it up. - */ - if (NONEXISTENT(current) || ANCIENT(current) || - (STALE(current) && !KEEPSTALE(rbtdb))) - { - if (top_prev != NULL) { - top_prev->next = current->next; - } else { - node->data = current->next; - } - dns_slabheader_destroy(¤t); - } else { - top_prev = current; - } - } - node->dirty = 0; -} - -static void -clean_zone_node(dns_rbtnode_t *node, uint32_t least_serial) { - dns_slabheader_t *current = NULL, *dcurrent = NULL; - dns_slabheader_t *down_next = NULL, *dparent = NULL; - dns_slabheader_t *top_prev = NULL, *top_next = NULL; - bool still_dirty = false; - - /* - * Caller must be holding the node lock. - */ - REQUIRE(least_serial != 0); - - for (current = node->data; current != NULL; current = top_next) { - top_next = current->next; - - /* - * First, we clean up any instances of multiple rdatasets - * with the same serial number, or that have the IGNORE - * attribute. - */ - dparent = current; - for (dcurrent = current->down; dcurrent != NULL; - dcurrent = down_next) - { - down_next = dcurrent->down; - INSIST(dcurrent->serial <= dparent->serial); - if (dcurrent->serial == dparent->serial || - IGNORE(dcurrent)) - { - if (down_next != NULL) { - down_next->next = dparent; - } - dparent->down = down_next; - dns_slabheader_destroy(&dcurrent); - } else { - dparent = dcurrent; - } - } - - /* - * We've now eliminated all IGNORE datasets with the possible - * exception of current, which we now check. - */ - if (IGNORE(current)) { - down_next = current->down; - if (down_next == NULL) { - if (top_prev != NULL) { - top_prev->next = current->next; - } else { - node->data = current->next; - } - dns_slabheader_destroy(¤t); - /* - * current no longer exists, so we can - * just continue with the loop. - */ - continue; - } else { - /* - * Pull up current->down, making it the new - * current. - */ - if (top_prev != NULL) { - top_prev->next = down_next; - } else { - node->data = down_next; - } - down_next->next = top_next; - dns_slabheader_destroy(¤t); - current = down_next; - } - } - - /* - * We now try to find the first down node less than the - * least serial. - */ - dparent = current; - for (dcurrent = current->down; dcurrent != NULL; - dcurrent = down_next) - { - down_next = dcurrent->down; - if (dcurrent->serial < least_serial) { - break; - } - dparent = dcurrent; - } - - /* - * If there is a such an rdataset, delete it and any older - * versions. - */ - if (dcurrent != NULL) { - do { - down_next = dcurrent->down; - INSIST(dcurrent->serial <= least_serial); - dns_slabheader_destroy(&dcurrent); - dcurrent = down_next; - } while (dcurrent != NULL); - dparent->down = NULL; - } - - /* - * Note. The serial number of 'current' might be less than - * least_serial too, but we cannot delete it because it is - * the most recent version, unless it is a NONEXISTENT - * rdataset. - */ - if (current->down != NULL) { - still_dirty = true; - top_prev = current; - } else { - /* - * If this is a NONEXISTENT rdataset, we can delete it. - */ - if (NONEXISTENT(current)) { - if (top_prev != NULL) { - top_prev->next = current->next; - } else { - node->data = current->next; - } - dns_slabheader_destroy(¤t); - } else { - top_prev = current; - } - } - } - if (!still_dirty) { - node->dirty = 0; - } -} - -/* - * tree_lock(write) must be held. - */ -static void -delete_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node) { - dns_rbtnode_t *nsecnode = NULL; - dns_fixedname_t fname; - dns_name_t *name = NULL; - isc_result_t result = ISC_R_UNEXPECTED; - - INSIST(!ISC_LINK_LINKED(node, deadlink)); - - if (isc_log_wouldlog(ISC_LOG_DEBUG(1))) { - char printname[DNS_NAME_FORMATSIZE]; - isc_log_write(DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, - ISC_LOG_DEBUG(1), - "delete_node(): %p %s (bucket %d)", node, - dns_rbt_formatnodename(node, printname, - sizeof(printname)), - node->locknum); - } - - switch (node->nsec) { - case DNS_DB_NSEC_NORMAL: - result = dns_rbt_deletenode(rbtdb->tree, node, false); - break; - case DNS_DB_NSEC_HAS_NSEC: - /* - * Though this may be wasteful, it has to be done before - * node is deleted. - */ - name = dns_fixedname_initname(&fname); - dns_rbt_fullnamefromnode(node, name); - /* - * Delete the corresponding node from the auxiliary NSEC - * tree before deleting from the main tree. - */ - nsecnode = NULL; - result = dns_rbt_findnode(rbtdb->nsec, name, NULL, &nsecnode, - NULL, DNS_RBTFIND_EMPTYDATA, NULL, - NULL); - if (result != ISC_R_SUCCESS) { - isc_log_write(DNS_LOGCATEGORY_DATABASE, - DNS_LOGMODULE_CACHE, ISC_LOG_WARNING, - "delete_node: " - "dns_rbt_findnode(nsec): %s", - isc_result_totext(result)); - } else { - result = dns_rbt_deletenode(rbtdb->nsec, nsecnode, - false); - if (result != ISC_R_SUCCESS) { - isc_log_write( - DNS_LOGCATEGORY_DATABASE, - DNS_LOGMODULE_CACHE, ISC_LOG_WARNING, - "delete_node(): " - "dns_rbt_deletenode(nsecnode): %s", - isc_result_totext(result)); - } - } - result = dns_rbt_deletenode(rbtdb->tree, node, false); - break; - case DNS_DB_NSEC_NSEC: - result = dns_rbt_deletenode(rbtdb->nsec, node, false); - break; - case DNS_DB_NSEC_NSEC3: - result = dns_rbt_deletenode(rbtdb->nsec3, node, false); - break; - } - if (result != ISC_R_SUCCESS) { - isc_log_write(DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, - ISC_LOG_WARNING, - "delete_node(): " - "dns_rbt_deletenode: %s", - isc_result_totext(result)); - } -} - -/* - * Caller must be holding the node lock. - */ -void -dns__rbtdb_newref(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, - isc_rwlocktype_t nlocktype DNS__DB_FLARG) { - uint_fast32_t refs; - - if (nlocktype == isc_rwlocktype_write && - ISC_LINK_LINKED(node, deadlink)) - { - ISC_LIST_UNLINK(rbtdb->deadnodes[node->locknum], node, - deadlink); - } - - refs = isc_refcount_increment0(&node->references); -#if DNS_DB_NODETRACE - fprintf(stderr, "incr:node:%s:%s:%u:%p->references = %" PRIuFAST32 "\n", - func, file, line, node, refs + 1); -#else - UNUSED(refs); -#endif - - if (refs == 0) { - /* this is the first reference to the node */ - refs = isc_refcount_increment0( - &rbtdb->node_locks[node->locknum].references); -#if DNS_DB_NODETRACE - fprintf(stderr, - "incr:nodelock:%s:%s:%u:%p:%p->references = " - "%" PRIuFAST32 "\n", - func, file, line, node, - &rbtdb->node_locks[node->locknum], refs + 1); -#else - UNUSED(refs); -#endif - } -} - -/*% - * The tree lock must be held for the result to be valid. - */ -static bool -is_last_node_on_its_level(dns_rbtnode_t *node) { - return (node->parent != NULL && node->parent->down == node && - node->left == NULL && node->right == NULL); -} - -static void -send_to_prune_tree(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, - isc_rwlocktype_t nlocktype DNS__DB_FLARG) { - rbtdb_prune_t *prune = isc_mem_get(rbtdb->common.mctx, sizeof(*prune)); - *prune = (rbtdb_prune_t){ .node = node }; - - dns_db_attach((dns_db_t *)rbtdb, &prune->db); - dns__rbtdb_newref(rbtdb, node, nlocktype DNS__DB_FLARG_PASS); - - isc_async_run(rbtdb->loop, prune_tree, prune); -} - -/*% - * Clean up dead nodes. These are nodes which have no references, and - * have no data. They are dead but we could not or chose not to delete - * them when we deleted all the data at that node because we did not want - * to wait for the tree write lock. - * - * The caller must hold a tree write lock and bucketnum'th node (write) lock. - */ -static void -cleanup_dead_nodes(dns_rbtdb_t *rbtdb, int bucketnum DNS__DB_FLARG) { - dns_rbtnode_t *node = NULL; - int count = 10; /* XXXJT: should be adjustable */ - - node = ISC_LIST_HEAD(rbtdb->deadnodes[bucketnum]); - while (node != NULL && count > 0) { - ISC_LIST_UNLINK(rbtdb->deadnodes[bucketnum], node, deadlink); - - /* - * We might have reactivated this node without a tree write - * lock, so we couldn't remove this node from deadnodes then - * and we have to do it now. - */ - if (isc_refcount_current(&node->references) != 0 || - node->data != NULL) - { - node = ISC_LIST_HEAD(rbtdb->deadnodes[bucketnum]); - count--; - continue; - } - - if (is_last_node_on_its_level(node) && rbtdb->loop != NULL) { - send_to_prune_tree( - rbtdb, node, - isc_rwlocktype_write DNS__DB_FLARG_PASS); - } else if (node->down == NULL && node->data == NULL) { - /* - * Not a interior node and not needing to be - * reactivated. - */ - delete_node(rbtdb, node); - } else if (node->data == NULL) { - /* - * A interior node without data. Leave linked to - * to be cleaned up when node->down becomes NULL. - */ - ISC_LIST_APPEND(rbtdb->deadnodes[bucketnum], node, - deadlink); - } - node = ISC_LIST_HEAD(rbtdb->deadnodes[bucketnum]); - count--; - } -} - -/* - * This function is assumed to be called when a node is newly referenced - * and can be in the deadnode list. In that case the node must be retrieved - * from the list because it is going to be used. In addition, if the caller - * happens to hold a write lock on the tree, it's a good chance to purge dead - * nodes. - * Note: while a new reference is gained in multiple places, there are only very - * few cases where the node can be in the deadnode list (only empty nodes can - * have been added to the list). - */ -static void -reactivate_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, - isc_rwlocktype_t tlocktype DNS__DB_FLARG) { - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - isc_rwlock_t *nodelock = &rbtdb->node_locks[node->locknum].lock; - bool maybe_cleanup = false; - - POST(nlocktype); - - NODE_RDLOCK(nodelock, &nlocktype); - - /* - * Check if we can possibly cleanup the dead node. If so, upgrade - * the node lock below to perform the cleanup. - */ - if (!ISC_LIST_EMPTY(rbtdb->deadnodes[node->locknum]) && - tlocktype == isc_rwlocktype_write) - { - maybe_cleanup = true; - } - - if (ISC_LINK_LINKED(node, deadlink) || maybe_cleanup) { - /* - * Upgrade the lock and test if we still need to unlink. - */ - NODE_FORCEUPGRADE(nodelock, &nlocktype); - POST(nlocktype); - if (ISC_LINK_LINKED(node, deadlink)) { - ISC_LIST_UNLINK(rbtdb->deadnodes[node->locknum], node, - deadlink); - } - if (maybe_cleanup) { - cleanup_dead_nodes(rbtdb, - node->locknum DNS__DB_FILELINE); - } - } - - dns__rbtdb_newref(rbtdb, node, nlocktype DNS__DB_FLARG_PASS); - - NODE_UNLOCK(nodelock, &nlocktype); -} - -/* - * Caller must be holding the node lock; either the read or write lock. - * Note that the lock must be held even when node references are - * atomically modified; in that case the decrement operation itself does not - * have to be protected, but we must avoid a race condition where multiple - * threads are decreasing the reference to zero simultaneously and at least - * one of them is going to free the node. - * - * This function returns true if and only if the node reference decreases - * to zero. - * - * NOTE: Decrementing the reference count of a node to zero does not mean it - * will be immediately freed. - */ -bool -dns__rbtdb_decref(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, - uint32_t least_serial, isc_rwlocktype_t *nlocktypep, - isc_rwlocktype_t *tlocktypep, bool tryupgrade, - bool pruning DNS__DB_FLARG) { - isc_result_t result; - bool locked = *tlocktypep != isc_rwlocktype_none; - bool write_locked = false; - db_nodelock_t *nodelock = NULL; - int bucket = node->locknum; - bool no_reference = true; - uint_fast32_t refs; - - REQUIRE(*nlocktypep != isc_rwlocktype_none); - - nodelock = &rbtdb->node_locks[bucket]; - -#define KEEP_NODE(n, r, l) \ - ((n)->data != NULL || ((l) && (n)->down != NULL) || \ - (n) == (r)->origin_node || (n) == (r)->nsec3_origin_node) - - /* Handle easy and typical case first. */ - if (!node->dirty && KEEP_NODE(node, rbtdb, locked)) { - refs = isc_refcount_decrement(&node->references); -#if DNS_DB_NODETRACE - fprintf(stderr, - "decr:node:%s:%s:%u:%p->references = %" PRIuFAST32 "\n", - func, file, line, node, refs - 1); -#else - UNUSED(refs); -#endif - if (refs == 1) { - refs = isc_refcount_decrement(&nodelock->references); -#if DNS_DB_NODETRACE - fprintf(stderr, - "decr:nodelock:%s:%s:%u:%p:%p->references = " - "%" PRIuFAST32 "\n", - func, file, line, node, nodelock, refs - 1); -#else - UNUSED(refs); -#endif - return (true); - } else { - return (false); - } - } - - /* Upgrade the lock? */ - if (*nlocktypep == isc_rwlocktype_read) { - NODE_FORCEUPGRADE(&nodelock->lock, nlocktypep); - } - - refs = isc_refcount_decrement(&node->references); -#if DNS_DB_NODETRACE - fprintf(stderr, "decr:node:%s:%s:%u:%p->references = %" PRIuFAST32 "\n", - func, file, line, node, refs - 1); -#else - UNUSED(refs); -#endif - if (refs > 1) { - return (false); - } - - if (node->dirty) { - if (IS_CACHE(rbtdb)) { - clean_cache_node(rbtdb, node); - } else { - if (least_serial == 0) { - /* - * Caller doesn't know the least serial. - * Get it. - */ - RWLOCK(&rbtdb->lock, isc_rwlocktype_read); - least_serial = rbtdb->least_serial; - RWUNLOCK(&rbtdb->lock, isc_rwlocktype_read); - } - clean_zone_node(node, least_serial); - } - } - - /* - * Attempt to switch to a write lock on the tree. If this fails, - * we will add this node to a linked list of nodes in this locking - * bucket which we will free later. - * - * Locking hierarchy notwithstanding, we don't need to free - * the node lock before acquiring the tree write lock because - * we only do a trylock. - */ - /* We are allowed to upgrade the tree lock */ - switch (*tlocktypep) { - case isc_rwlocktype_write: - result = ISC_R_SUCCESS; - break; - case isc_rwlocktype_read: - if (tryupgrade) { - result = TREE_TRYUPGRADE(&rbtdb->tree_lock, tlocktypep); - } else { - result = ISC_R_LOCKBUSY; - } - break; - case isc_rwlocktype_none: - result = TREE_TRYWRLOCK(&rbtdb->tree_lock, tlocktypep); - break; - default: - UNREACHABLE(); - } - RUNTIME_CHECK(result == ISC_R_SUCCESS || result == ISC_R_LOCKBUSY); - if (result == ISC_R_SUCCESS) { - write_locked = true; - } - - refs = isc_refcount_decrement(&nodelock->references); -#if DNS_DB_NODETRACE - fprintf(stderr, - "decr:nodelock:%s:%s:%u:%p:%p->references = %" PRIuFAST32 "\n", - func, file, line, node, nodelock, refs - 1); -#else - UNUSED(refs); -#endif - - if (KEEP_NODE(node, rbtdb, (locked || write_locked))) { - goto restore_locks; - } - -#undef KEEP_NODE - - if (write_locked) { - /* - * If this node is the only one left on its RBTDB level, - * attempt pruning the RBTDB (i.e. deleting empty nodes that - * are ancestors of 'node' and are not interior nodes) starting - * from this node (see prune_tree()). The main reason this is - * not done immediately, but asynchronously, is that the - * ancestors of 'node' are almost guaranteed to belong to - * different node buckets and we don't want to do juggle locks - * right now. - * - * Since prune_tree() also calls dns__rbtdb_decref(), check the - * value of the 'pruning' parameter (which is only set to - * 'true' in the dns__rbtdb_decref() call present in - * prune_tree()) to prevent an infinite loop and to allow a - * node sent to prune_tree() to be deleted by the delete_node() - * call in the code branch below. - */ - if (!pruning && is_last_node_on_its_level(node) && - rbtdb->loop != NULL) - { - send_to_prune_tree(rbtdb, node, - *nlocktypep DNS__DB_FLARG_PASS); - no_reference = false; - } else { - /* - * The node can now be deleted. - */ - delete_node(rbtdb, node); - } - } else { - INSIST(node->data == NULL); - if (!ISC_LINK_LINKED(node, deadlink)) { - ISC_LIST_APPEND(rbtdb->deadnodes[bucket], node, - deadlink); - } - } - -restore_locks: - /* - * Relock a read lock, or unlock the write lock if no lock was held. - */ - if (!locked && write_locked) { - TREE_UNLOCK(&rbtdb->tree_lock, tlocktypep); - } - - return (no_reference); -} - -/* - * Prune the RBTDB tree of trees. Start by attempting to delete a node that is - * the only one left on its RBTDB level (see the send_to_prune_tree() call in - * dns__rbtdb_decref()). Then, if the node has a parent (which can either - * exist on the same RBTDB level or on an upper RBTDB level), check whether the - * latter is an interior node (i.e. a node with a non-NULL 'down' pointer). If - * the parent node is not an interior node, attempt deleting the parent node as - * well and then move on to examining the parent node's parent, etc. Continue - * traversing the RBTDB tree until a node is encountered that is still an - * interior node after the previously-processed node gets deleted. - * - * It is acceptable for a node sent to this function to NOT be deleted in the - * process (e.g. if it gets reactivated in the meantime). Furthermore, node - * deletion is not a prerequisite for continuing RBTDB traversal. - * - * This function gets called once for every "starting node" and it continues - * traversing the RBTDB until the stop condition is met. In the worst case, - * the number of nodes processed by a single execution of this function is the - * number of tree levels, which is at most the maximum number of domain name - * labels (127); however, it should be much smaller in practice and deleting - * empty RBTDB nodes is critical to keeping the amount of memory used by the - * cache memory context within the configured limit anyway. - */ -static void -prune_tree(void *arg) { - rbtdb_prune_t *prune = (rbtdb_prune_t *)arg; - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)prune->db; - dns_rbtnode_t *node = prune->node; - dns_rbtnode_t *parent = NULL; - unsigned int locknum = node->locknum; - isc_rwlocktype_t tlocktype = isc_rwlocktype_none; - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - - isc_mem_put(rbtdb->common.mctx, prune, sizeof(*prune)); - - TREE_WRLOCK(&rbtdb->tree_lock, &tlocktype); - NODE_WRLOCK(&rbtdb->node_locks[locknum].lock, &nlocktype); - do { - parent = node->parent; - dns__rbtdb_decref(rbtdb, node, 0, &nlocktype, &tlocktype, true, - true DNS__DB_FILELINE); - - /* - * Check whether the parent is an interior node. Note that it - * might have been one before the dns__rbtdb_decref() call on - * the previous line, but decrementing the reference count for - * 'node' could have caused 'node->parent->down' to become - * NULL. - */ - if (parent != NULL && parent->down == NULL) { - /* - * Keep the node lock if possible; otherwise, release - * the old lock and acquire one for the parent. - */ - if (parent->locknum != locknum) { - NODE_UNLOCK(&rbtdb->node_locks[locknum].lock, - &nlocktype); - locknum = parent->locknum; - NODE_WRLOCK(&rbtdb->node_locks[locknum].lock, - &nlocktype); - } - - /* - * We need to gain a reference to the parent node - * before decrementing it in the next iteration. - */ - dns__rbtdb_newref(rbtdb, parent, - nlocktype DNS__DB_FLARG_PASS); - } else { - parent = NULL; - } - - node = parent; - } while (node != NULL); - NODE_UNLOCK(&rbtdb->node_locks[locknum].lock, &nlocktype); - TREE_UNLOCK(&rbtdb->tree_lock, &tlocktype); - - dns_db_detach((dns_db_t **)&rbtdb); -} - -static void -make_least_version(dns_rbtdb_t *rbtdb, dns_rbtdb_version_t *version, - rbtdb_changedlist_t *cleanup_list) { - /* - * Caller must be holding the database lock. - */ - - rbtdb->least_serial = version->serial; - *cleanup_list = version->changed_list; - ISC_LIST_INIT(version->changed_list); -} - -static void -cleanup_nondirty(dns_rbtdb_version_t *version, - rbtdb_changedlist_t *cleanup_list) { - rbtdb_changed_t *changed = NULL, *next_changed = NULL; - - /* - * If the changed record is dirty, then - * an update created multiple versions of - * a given rdataset. We keep this list - * until we're the least open version, at - * which point it's safe to get rid of any - * older versions. - * - * If the changed record isn't dirty, then - * we don't need it anymore since we're - * committing and not rolling back. - * - * The caller must be holding the database lock. - */ - for (changed = HEAD(version->changed_list); changed != NULL; - changed = next_changed) - { - next_changed = NEXT(changed, link); - if (!changed->dirty) { - UNLINK(version->changed_list, changed, link); - APPEND(*cleanup_list, changed, link); - } - } -} - -void -dns__rbtdb_setsecure(dns_db_t *db, dns_rbtdb_version_t *version, - dns_dbnode_t *origin) { - dns_rdataset_t keyset; - dns_rdataset_t nsecset, signsecset; - bool haszonekey = false; - bool hasnsec = false; - isc_result_t result; - - REQUIRE(version != NULL); - - dns_rdataset_init(&keyset); - result = dns_db_findrdataset(db, origin, (dns_dbversion_t *)version, - dns_rdatatype_dnskey, 0, 0, &keyset, NULL); - if (result == ISC_R_SUCCESS) { - result = dns_rdataset_first(&keyset); - while (result == ISC_R_SUCCESS) { - dns_rdata_t keyrdata = DNS_RDATA_INIT; - dns_rdataset_current(&keyset, &keyrdata); - if (dns_zonekey_iszonekey(&keyrdata)) { - haszonekey = true; - break; - } - result = dns_rdataset_next(&keyset); - } - dns_rdataset_disassociate(&keyset); - } - if (!haszonekey) { - version->secure = false; - version->havensec3 = false; - return; - } - - dns_rdataset_init(&nsecset); - dns_rdataset_init(&signsecset); - result = dns_db_findrdataset(db, origin, (dns_dbversion_t *)version, - dns_rdatatype_nsec, 0, 0, &nsecset, - &signsecset); - if (result == ISC_R_SUCCESS) { - if (dns_rdataset_isassociated(&signsecset)) { - hasnsec = true; - dns_rdataset_disassociate(&signsecset); - } - dns_rdataset_disassociate(&nsecset); - } - - setnsec3parameters(db, version); - - /* - * Do we have a valid NSEC/NSEC3 chain? - */ - if (version->havensec3 || hasnsec) { - version->secure = true; - } else { - version->secure = false; - } -} - -/*%< - * Walk the origin node looking for NSEC3PARAM records. - * Cache the nsec3 parameters. - */ -static void -setnsec3parameters(dns_db_t *db, dns_rbtdb_version_t *version) { - dns_rbtnode_t *node = NULL; - dns_rdata_nsec3param_t nsec3param; - dns_rdata_t rdata = DNS_RDATA_INIT; - isc_region_t region; - isc_result_t result; - dns_slabheader_t *header = NULL, *header_next = NULL; - unsigned char *raw; /* RDATASLAB */ - unsigned int count, length; - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - isc_rwlocktype_t tlocktype = isc_rwlocktype_none; - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - - TREE_RDLOCK(&rbtdb->tree_lock, &tlocktype); - version->havensec3 = false; - node = rbtdb->origin_node; - NODE_RDLOCK(&(rbtdb->node_locks[node->locknum].lock), &nlocktype); - for (header = node->data; header != NULL; header = header_next) { - header_next = header->next; - do { - if (header->serial <= version->serial && - !IGNORE(header)) - { - if (NONEXISTENT(header)) { - header = NULL; - } - break; - } else { - header = header->down; - } - } while (header != NULL); - - if (header != NULL && - (header->type == dns_rdatatype_nsec3param)) - { - /* - * Find A NSEC3PARAM with a supported algorithm. - */ - raw = dns_slabheader_raw(header); - count = raw[0] * 256 + raw[1]; /* count */ - raw += DNS_RDATASET_COUNT + DNS_RDATASET_LENGTH; - while (count-- > 0U) { - length = raw[0] * 256 + raw[1]; - raw += DNS_RDATASET_ORDER + DNS_RDATASET_LENGTH; - region.base = raw; - region.length = length; - raw += length; - dns_rdata_fromregion( - &rdata, rbtdb->common.rdclass, - dns_rdatatype_nsec3param, ®ion); - result = dns_rdata_tostruct(&rdata, &nsec3param, - NULL); - INSIST(result == ISC_R_SUCCESS); - dns_rdata_reset(&rdata); - - if (nsec3param.hash != DNS_NSEC3_UNKNOWNALG && - !dns_nsec3_supportedhash(nsec3param.hash)) - { - continue; - } - - if (nsec3param.flags != 0) { - continue; - } - - memmove(version->salt, nsec3param.salt, - nsec3param.salt_length); - version->hash = nsec3param.hash; - version->salt_length = nsec3param.salt_length; - version->iterations = nsec3param.iterations; - version->flags = nsec3param.flags; - version->havensec3 = true; - /* - * Look for a better algorithm than the - * unknown test algorithm. - */ - if (nsec3param.hash != DNS_NSEC3_UNKNOWNALG) { - goto unlock; - } - } - } - } -unlock: - NODE_UNLOCK(&(rbtdb->node_locks[node->locknum].lock), &nlocktype); - TREE_UNLOCK(&rbtdb->tree_lock, &tlocktype); -} - -static void -cleanup_dead_nodes_callback(void *arg) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)arg; - bool again = false; - unsigned int locknum; - isc_rwlocktype_t tlocktype = isc_rwlocktype_none; - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - - TREE_WRLOCK(&rbtdb->tree_lock, &tlocktype); - for (locknum = 0; locknum < rbtdb->node_lock_count; locknum++) { - NODE_WRLOCK(&rbtdb->node_locks[locknum].lock, &nlocktype); - cleanup_dead_nodes(rbtdb, locknum DNS__DB_FILELINE); - if (ISC_LIST_HEAD(rbtdb->deadnodes[locknum]) != NULL) { - again = true; - } - NODE_UNLOCK(&rbtdb->node_locks[locknum].lock, &nlocktype); - } - TREE_UNLOCK(&rbtdb->tree_lock, &tlocktype); - if (again) { - isc_async_run(rbtdb->loop, cleanup_dead_nodes_callback, rbtdb); - } else { - dns_db_detach((dns_db_t **)&rbtdb); - } -} - -void -dns__rbtdb_closeversion(dns_db_t *db, dns_dbversion_t **versionp, - bool commit DNS__DB_FLARG) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - dns_rbtdb_version_t *version = NULL, *cleanup_version = NULL; - dns_rbtdb_version_t *least_greater = NULL; - bool rollback = false; - rbtdb_changedlist_t cleanup_list; - dns_slabheaderlist_t resigned_list; - rbtdb_changed_t *changed = NULL, *next_changed = NULL; - uint32_t serial, least_serial; - dns_rbtnode_t *rbtnode = NULL; - dns_slabheader_t *header = NULL; - - REQUIRE(VALID_RBTDB(rbtdb)); - version = (dns_rbtdb_version_t *)*versionp; - INSIST(version->rbtdb == rbtdb); - - ISC_LIST_INIT(cleanup_list); - ISC_LIST_INIT(resigned_list); - - if (isc_refcount_decrement(&version->references) > 1) { - /* typical and easy case first */ - if (commit) { - RWLOCK(&rbtdb->lock, isc_rwlocktype_read); - INSIST(!version->writer); - RWUNLOCK(&rbtdb->lock, isc_rwlocktype_read); - } - goto end; - } - - /* - * Update the zone's secure status in version before making - * it the current version. - */ - if (version->writer && commit && !IS_CACHE(rbtdb)) { - dns__rbtdb_setsecure(db, version, - (dns_dbnode_t *)rbtdb->origin_node); - } - - RWLOCK(&rbtdb->lock, isc_rwlocktype_write); - serial = version->serial; - if (version->writer) { - if (commit) { - unsigned int cur_ref; - dns_rbtdb_version_t *cur_version = NULL; - - INSIST(version->commit_ok); - INSIST(version == rbtdb->future_version); - /* - * The current version is going to be replaced. - * Release the (likely last) reference to it from the - * DB itself and unlink it from the open list. - */ - cur_version = rbtdb->current_version; - cur_ref = isc_refcount_decrement( - &cur_version->references); - if (cur_ref == 1) { - (void)isc_refcount_current( - &cur_version->references); - if (cur_version->serial == rbtdb->least_serial) - { - INSIST(EMPTY( - cur_version->changed_list)); - } - UNLINK(rbtdb->open_versions, cur_version, link); - } - if (EMPTY(rbtdb->open_versions)) { - /* - * We're going to become the least open - * version. - */ - make_least_version(rbtdb, version, - &cleanup_list); - } else { - /* - * Some other open version is the - * least version. We can't cleanup - * records that were changed in this - * version because the older versions - * may still be in use by an open - * version. - * - * We can, however, discard the - * changed records for things that - * we've added that didn't exist in - * prior versions. - */ - cleanup_nondirty(version, &cleanup_list); - } - /* - * If the (soon to be former) current version - * isn't being used by anyone, we can clean - * it up. - */ - if (cur_ref == 1) { - cleanup_version = cur_version; - APPENDLIST(version->changed_list, - cleanup_version->changed_list, link); - } - /* - * Become the current version. - */ - version->writer = false; - rbtdb->current_version = version; - rbtdb->current_serial = version->serial; - rbtdb->future_version = NULL; - - /* - * Keep the current version in the open list, and - * gain a reference for the DB itself (see the DB - * creation function below). This must be the only - * case where we need to increment the counter from - * zero and need to use isc_refcount_increment0(). - */ - INSIST(isc_refcount_increment0(&version->references) == - 0); - PREPEND(rbtdb->open_versions, rbtdb->current_version, - link); - resigned_list = version->resigned_list; - ISC_LIST_INIT(version->resigned_list); - } else { - /* - * We're rolling back this transaction. - */ - cleanup_list = version->changed_list; - ISC_LIST_INIT(version->changed_list); - resigned_list = version->resigned_list; - ISC_LIST_INIT(version->resigned_list); - rollback = true; - cleanup_version = version; - rbtdb->future_version = NULL; - } - } else { - if (version != rbtdb->current_version) { - /* - * There are no external or internal references - * to this version and it can be cleaned up. - */ - cleanup_version = version; - - /* - * Find the version with the least serial - * number greater than ours. - */ - least_greater = PREV(version, link); - if (least_greater == NULL) { - least_greater = rbtdb->current_version; - } - - INSIST(version->serial < least_greater->serial); - /* - * Is this the least open version? - */ - if (version->serial == rbtdb->least_serial) { - /* - * Yes. Install the new least open - * version. - */ - make_least_version(rbtdb, least_greater, - &cleanup_list); - } else { - /* - * Add any unexecuted cleanups to - * those of the least greater version. - */ - APPENDLIST(least_greater->changed_list, - version->changed_list, link); - } - } else if (version->serial == rbtdb->least_serial) { - INSIST(EMPTY(version->changed_list)); - } - UNLINK(rbtdb->open_versions, version, link); - } - least_serial = rbtdb->least_serial; - RWUNLOCK(&rbtdb->lock, isc_rwlocktype_write); - - if (cleanup_version != NULL) { - isc_refcount_destroy(&cleanup_version->references); - INSIST(EMPTY(cleanup_version->changed_list)); - free_gluetable(cleanup_version->glue_table); - isc_rwlock_destroy(&cleanup_version->rwlock); - isc_mem_put(rbtdb->common.mctx, cleanup_version, - sizeof(*cleanup_version)); - } - - /* - * Commit/rollback re-signed headers. - */ - for (header = HEAD(resigned_list); header != NULL; - header = HEAD(resigned_list)) - { - isc_rwlock_t *lock = NULL; - isc_rwlocktype_t tlocktype = isc_rwlocktype_none; - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - - ISC_LIST_UNLINK(resigned_list, header, link); - - lock = &rbtdb->node_locks[RBTDB_HEADERNODE(header)->locknum] - .lock; - NODE_WRLOCK(lock, &nlocktype); - if (rollback && !IGNORE(header)) { - dns__zonerbt_resigninsert( - rbtdb, RBTDB_HEADERNODE(header)->locknum, - header); - } - dns__rbtdb_decref(rbtdb, RBTDB_HEADERNODE(header), least_serial, - &nlocktype, &tlocktype, true, - false DNS__DB_FLARG_PASS); - NODE_UNLOCK(lock, &nlocktype); - INSIST(tlocktype == isc_rwlocktype_none); - } - - if (!EMPTY(cleanup_list)) { - isc_rwlocktype_t tlocktype = isc_rwlocktype_none; - - if (rbtdb->loop == NULL) { - /* - * We acquire a tree write lock here in order to make - * sure that stale nodes will be removed in - * dns__rbtdb_decref(). If we didn't have the lock, - * those nodes could miss the chance to be removed - * until the server stops. The write lock is - * expensive, but this should be rare enough - * to justify the cost. - */ - TREE_WRLOCK(&rbtdb->tree_lock, &tlocktype); - } - - for (changed = HEAD(cleanup_list); changed != NULL; - changed = next_changed) - { - isc_rwlock_t *lock = NULL; - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - - next_changed = NEXT(changed, link); - rbtnode = changed->node; - lock = &rbtdb->node_locks[rbtnode->locknum].lock; - - NODE_WRLOCK(lock, &nlocktype); - /* - * This is a good opportunity to purge any dead nodes, - * so use it. - */ - if (rbtdb->loop == NULL) { - cleanup_dead_nodes( - rbtdb, - rbtnode->locknum DNS__DB_FLARG_PASS); - } - - if (rollback) { - rollback_node(rbtnode, serial); - } - dns__rbtdb_decref(rbtdb, rbtnode, least_serial, - &nlocktype, &tlocktype, true, - false DNS__DB_FILELINE); - - NODE_UNLOCK(lock, &nlocktype); - - isc_mem_put(rbtdb->common.mctx, changed, - sizeof(*changed)); - } - if (rbtdb->loop != NULL) { - isc_refcount_increment(&rbtdb->common.references); - isc_async_run(rbtdb->loop, cleanup_dead_nodes_callback, - rbtdb); - } else { - TREE_UNLOCK(&rbtdb->tree_lock, &tlocktype); - } - - INSIST(tlocktype == isc_rwlocktype_none); - } - -end: - *versionp = NULL; -} - -isc_result_t -dns__rbtdb_findnodeintree(dns_rbtdb_t *rbtdb, dns_rbt_t *tree, - const dns_name_t *name, bool create, - dns_dbnode_t **nodep DNS__DB_FLARG) { - dns_rbtnode_t *node = NULL; - dns_name_t nodename; - isc_result_t result; - isc_rwlocktype_t tlocktype = isc_rwlocktype_none; - - INSIST(tree == rbtdb->tree || tree == rbtdb->nsec3); - - dns_name_init(&nodename, NULL); - TREE_RDLOCK(&rbtdb->tree_lock, &tlocktype); - result = dns_rbt_findnode(tree, name, NULL, &node, NULL, - DNS_RBTFIND_EMPTYDATA, NULL, NULL); - if (result != ISC_R_SUCCESS) { - if (!create) { - if (result == DNS_R_PARTIALMATCH) { - result = ISC_R_NOTFOUND; - } - goto unlock; - } - /* - * Try to upgrade the lock and if that fails unlock then relock. - */ - TREE_FORCEUPGRADE(&rbtdb->tree_lock, &tlocktype); - node = NULL; - result = dns_rbt_addnode(tree, name, &node); - if (result == ISC_R_SUCCESS) { - dns_rbt_namefromnode(node, &nodename); - node->locknum = node->hashval % rbtdb->node_lock_count; - if (tree == rbtdb->tree) { - dns__zonerbt_addwildcards(rbtdb, name, true); - - if (dns_name_iswildcard(name)) { - result = dns__zonerbt_wildcardmagic( - rbtdb, name, true); - if (result != ISC_R_SUCCESS) { - goto unlock; - } - } - } - if (tree == rbtdb->nsec3) { - node->nsec = DNS_DB_NSEC_NSEC3; - } - } else if (result == ISC_R_EXISTS) { - result = ISC_R_SUCCESS; - } else { - goto unlock; - } - } - - if (tree == rbtdb->nsec3) { - INSIST(node->nsec == DNS_DB_NSEC_NSEC3); - } - - reactivate_node(rbtdb, node, tlocktype DNS__DB_FLARG_PASS); - - *nodep = (dns_dbnode_t *)node; -unlock: - TREE_UNLOCK(&rbtdb->tree_lock, &tlocktype); - - return (result); -} - -isc_result_t -dns__rbtdb_findnode(dns_db_t *db, const dns_name_t *name, bool create, - dns_dbnode_t **nodep DNS__DB_FLARG) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - - REQUIRE(VALID_RBTDB(rbtdb)); - - return (dns__rbtdb_findnodeintree(rbtdb, rbtdb->tree, name, create, - nodep DNS__DB_FLARG_PASS)); -} - -void -dns__rbtdb_bindrdataset(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, - dns_slabheader_t *header, isc_stdtime_t now, - isc_rwlocktype_t locktype, - dns_rdataset_t *rdataset DNS__DB_FLARG) { - bool stale = STALE(header); - bool ancient = ANCIENT(header); - - /* - * Caller must be holding the node reader lock. - * XXXJT: technically, we need a writer lock, since we'll increment - * the header count below. However, since the actual counter value - * doesn't matter, we prioritize performance here. (We may want to - * use atomic increment when available). - */ - - if (rdataset == NULL) { - return; - } - - dns__rbtdb_newref(rbtdb, node, locktype DNS__DB_FLARG_PASS); - - INSIST(rdataset->methods == NULL); /* We must be disassociated. */ - - /* - * Mark header stale or ancient if the RRset is no longer active. - */ - if (!ACTIVE(header, now)) { - dns_ttl_t stale_ttl = header->ttl + STALE_TTL(header, rbtdb); - /* - * If this data is in the stale window keep it and if - * DNS_DBFIND_STALEOK is not set we tell the caller to - * skip this record. We skip the records with ZEROTTL - * (these records should not be cached anyway). - */ - - if (KEEPSTALE(rbtdb) && stale_ttl > now) { - stale = true; - } else { - /* - * We are not keeping stale, or it is outside the - * stale window. Mark ancient, i.e. ready for cleanup. - */ - ancient = true; - } - } - - rdataset->methods = &dns_rdataslab_rdatasetmethods; - rdataset->rdclass = rbtdb->common.rdclass; - rdataset->type = DNS_TYPEPAIR_TYPE(header->type); - rdataset->covers = DNS_TYPEPAIR_COVERS(header->type); - rdataset->ttl = header->ttl - now; - rdataset->trust = header->trust; - - if (NEGATIVE(header)) { - rdataset->attributes |= DNS_RDATASETATTR_NEGATIVE; - } - if (NXDOMAIN(header)) { - rdataset->attributes |= DNS_RDATASETATTR_NXDOMAIN; - } - if (OPTOUT(header)) { - rdataset->attributes |= DNS_RDATASETATTR_OPTOUT; - } - if (PREFETCH(header)) { - rdataset->attributes |= DNS_RDATASETATTR_PREFETCH; - } - - if (stale && !ancient) { - dns_ttl_t stale_ttl = header->ttl + STALE_TTL(header, rbtdb); - if (stale_ttl > now) { - rdataset->ttl = stale_ttl - now; - } else { - rdataset->ttl = 0; - } - if (STALE_WINDOW(header)) { - rdataset->attributes |= DNS_RDATASETATTR_STALE_WINDOW; - } - rdataset->attributes |= DNS_RDATASETATTR_STALE; - } else if (IS_CACHE(rbtdb) && !ACTIVE(header, now)) { - rdataset->attributes |= DNS_RDATASETATTR_ANCIENT; - rdataset->ttl = header->ttl; - } - - rdataset->count = atomic_fetch_add_relaxed(&header->count, 1); - - rdataset->slab.db = (dns_db_t *)rbtdb; - rdataset->slab.node = (dns_dbnode_t *)node; - rdataset->slab.raw = dns_slabheader_raw(header); - rdataset->slab.iter_pos = NULL; - rdataset->slab.iter_count = 0; - - /* - * Add noqname proof. - */ - rdataset->slab.noqname = header->noqname; - if (header->noqname != NULL) { - rdataset->attributes |= DNS_RDATASETATTR_NOQNAME; - } - rdataset->slab.closest = header->closest; - if (header->closest != NULL) { - rdataset->attributes |= DNS_RDATASETATTR_CLOSEST; - } - - /* - * Copy out re-signing information. - */ - if (RESIGN(header)) { - rdataset->attributes |= DNS_RDATASETATTR_RESIGN; - rdataset->resign = (header->resign << 1) | header->resign_lsb; - } else { - rdataset->resign = 0; - } -} - -void -dns__rbtdb_attachnode(dns_db_t *db, dns_dbnode_t *source, - dns_dbnode_t **targetp DNS__DB_FLARG) { - REQUIRE(VALID_RBTDB((dns_rbtdb_t *)db)); - REQUIRE(targetp != NULL && *targetp == NULL); - - dns_rbtnode_t *node = (dns_rbtnode_t *)source; - uint_fast32_t refs = isc_refcount_increment(&node->references); - -#if DNS_DB_NODETRACE - fprintf(stderr, "incr:node:%s:%s:%u:%p->references = %" PRIuFAST32 "\n", - func, file, line, node, refs + 1); -#else - UNUSED(refs); -#endif - - *targetp = source; -} - -void -dns__rbtdb_detachnode(dns_db_t *db, dns_dbnode_t **targetp DNS__DB_FLARG) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - dns_rbtnode_t *node = NULL; - bool want_free = false; - bool inactive = false; - db_nodelock_t *nodelock = NULL; - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - isc_rwlocktype_t tlocktype = isc_rwlocktype_none; - - REQUIRE(VALID_RBTDB(rbtdb)); - REQUIRE(targetp != NULL && *targetp != NULL); - - node = (dns_rbtnode_t *)(*targetp); - nodelock = &rbtdb->node_locks[node->locknum]; - - NODE_RDLOCK(&nodelock->lock, &nlocktype); - - if (dns__rbtdb_decref(rbtdb, node, 0, &nlocktype, &tlocktype, true, - false DNS__DB_FLARG_PASS)) - { - if (isc_refcount_current(&nodelock->references) == 0 && - nodelock->exiting) - { - inactive = true; - } - } - - NODE_UNLOCK(&nodelock->lock, &nlocktype); - INSIST(tlocktype == isc_rwlocktype_none); - - *targetp = NULL; - - if (inactive) { - RWLOCK(&rbtdb->lock, isc_rwlocktype_write); - rbtdb->active--; - if (rbtdb->active == 0) { - want_free = true; - } - RWUNLOCK(&rbtdb->lock, isc_rwlocktype_write); - if (want_free) { - char buf[DNS_NAME_FORMATSIZE]; - if (dns_name_dynamic(&rbtdb->common.origin)) { - dns_name_format(&rbtdb->common.origin, buf, - sizeof(buf)); - } else { - strlcpy(buf, "", sizeof(buf)); - } - isc_log_write(DNS_LOGCATEGORY_DATABASE, - DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1), - "calling free_rbtdb(%s)", buf); - free_rbtdb(rbtdb, true); - } - } -} - -isc_result_t -dns__rbtdb_createiterator(dns_db_t *db, unsigned int options, - dns_dbiterator_t **iteratorp) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - rbtdb_dbiterator_t *rbtdbiter = NULL; - - REQUIRE(VALID_RBTDB(rbtdb)); - REQUIRE((options & (DNS_DB_NSEC3ONLY | DNS_DB_NONSEC3)) != - (DNS_DB_NSEC3ONLY | DNS_DB_NONSEC3)); - - rbtdbiter = isc_mem_get(rbtdb->common.mctx, sizeof(*rbtdbiter)); - - rbtdbiter->common.methods = &dbiterator_methods; - rbtdbiter->common.db = NULL; - dns_db_attach(db, &rbtdbiter->common.db); - rbtdbiter->common.relative_names = ((options & DNS_DB_RELATIVENAMES) != - 0); - rbtdbiter->common.magic = DNS_DBITERATOR_MAGIC; - rbtdbiter->paused = true; - rbtdbiter->tree_locked = isc_rwlocktype_none; - rbtdbiter->result = ISC_R_SUCCESS; - dns_fixedname_init(&rbtdbiter->name); - dns_fixedname_init(&rbtdbiter->origin); - rbtdbiter->node = NULL; - if ((options & DNS_DB_NSEC3ONLY) != 0) { - rbtdbiter->nsec3mode = nsec3only; - } else if ((options & DNS_DB_NONSEC3) != 0) { - rbtdbiter->nsec3mode = nonsec3; - } else { - rbtdbiter->nsec3mode = full; - } - dns_rbtnodechain_init(&rbtdbiter->chain); - dns_rbtnodechain_init(&rbtdbiter->nsec3chain); - if (rbtdbiter->nsec3mode == nsec3only) { - rbtdbiter->current = &rbtdbiter->nsec3chain; - } else { - rbtdbiter->current = &rbtdbiter->chain; - } - - *iteratorp = (dns_dbiterator_t *)rbtdbiter; - - return (ISC_R_SUCCESS); -} - -isc_result_t -dns__rbtdb_allrdatasets(dns_db_t *db, dns_dbnode_t *node, - dns_dbversion_t *version, unsigned int options, - isc_stdtime_t now, - dns_rdatasetiter_t **iteratorp DNS__DB_FLARG) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node; - dns_rbtdb_version_t *rbtversion = (dns_rbtdb_version_t *)version; - rbtdb_rdatasetiter_t *iterator = NULL; - uint_fast32_t refs; - - REQUIRE(VALID_RBTDB(rbtdb)); - - iterator = isc_mem_get(rbtdb->common.mctx, sizeof(*iterator)); - - if ((db->attributes & DNS_DBATTR_CACHE) == 0) { - now = 0; - if (rbtversion == NULL) { - dns__rbtdb_currentversion( - db, (dns_dbversion_t **)(void *)(&rbtversion)); - } else { - INSIST(rbtversion->rbtdb == rbtdb); - - (void)isc_refcount_increment(&rbtversion->references); - } - } else { - if (now == 0) { - now = isc_stdtime_now(); - } - rbtversion = NULL; - } - - iterator->common.magic = DNS_RDATASETITER_MAGIC; - iterator->common.methods = &rdatasetiter_methods; - iterator->common.db = db; - iterator->common.node = node; - iterator->common.version = (dns_dbversion_t *)rbtversion; - iterator->common.options = options; - iterator->common.now = now; - - refs = isc_refcount_increment(&rbtnode->references); -#if DNS_DB_NODETRACE - fprintf(stderr, "incr:node:%s:%s:%u:%p->references = %" PRIuFAST32 "\n", - func, file, line, node, refs + 1); -#else - UNUSED(refs); -#endif - - iterator->current = NULL; - - *iteratorp = (dns_rdatasetiter_t *)iterator; - - return (ISC_R_SUCCESS); -} - -static bool -cname_and_other_data(dns_rbtnode_t *node, uint32_t serial) { - dns_slabheader_t *header = NULL, *header_next = NULL; - bool cname = false, other_data = false; - dns_rdatatype_t rdtype; - - /* - * The caller must hold the node lock. - */ - - /* - * Look for CNAME and "other data" rdatasets active in our version. - */ - for (header = node->data; header != NULL; header = header_next) { - header_next = header->next; - if (!prio_type(header->type)) { - /* - * CNAME is in the priority list, so if we are done - * with the priority list, we know there will not be - * CNAME, so we are safe to skip the rest of the types. - */ - return (false); - } - if (header->type == dns_rdatatype_cname) { - /* - * Look for an active extant CNAME. - */ - do { - if (header->serial <= serial && !IGNORE(header)) - { - /* - * Is this a "this rdataset doesn't - * exist" record? - */ - if (NONEXISTENT(header)) { - header = NULL; - } - break; - } else { - header = header->down; - } - } while (header != NULL); - if (header != NULL) { - cname = true; - } - } else { - /* - * Look for active extant "other data". - * - * "Other data" is any rdataset whose type is not - * KEY, NSEC, SIG or RRSIG. - */ - rdtype = DNS_TYPEPAIR_TYPE(header->type); - if (rdtype != dns_rdatatype_key && - rdtype != dns_rdatatype_sig && - rdtype != dns_rdatatype_nsec && - rdtype != dns_rdatatype_rrsig) - { - /* - * Is it active and extant? - */ - do { - if (header->serial <= serial && - !IGNORE(header)) - { - /* - * Is this a "this rdataset - * doesn't exist" record? - */ - if (NONEXISTENT(header)) { - header = NULL; - } - break; - } else { - header = header->down; - } - } while (header != NULL); - if (header != NULL) { - other_data = true; - } - } - } - if (cname && other_data) { - return (true); - } - } - - return (false); -} - -static uint64_t -recordsize(dns_slabheader_t *header, unsigned int namelen) { - return (dns_rdataslab_rdatasize((unsigned char *)header, - sizeof(*header)) + - sizeof(dns_ttl_t) + sizeof(dns_rdatatype_t) + - sizeof(dns_rdataclass_t) + namelen); -} - -static void -update_recordsandxfrsize(bool add, dns_rbtdb_version_t *rbtversion, - dns_slabheader_t *header, unsigned int namelen) { - unsigned char *hdr = (unsigned char *)header; - size_t hdrsize = sizeof(*header); - - RWLOCK(&rbtversion->rwlock, isc_rwlocktype_write); - if (add) { - rbtversion->records += dns_rdataslab_count(hdr, hdrsize); - rbtversion->xfrsize += recordsize(header, namelen); - } else { - rbtversion->records -= dns_rdataslab_count(hdr, hdrsize); - rbtversion->xfrsize -= recordsize(header, namelen); - } - RWUNLOCK(&rbtversion->rwlock, isc_rwlocktype_write); -} - -static bool -overmaxtype(dns_rbtdb_t *rbtdb, uint32_t ntypes) { - if (rbtdb->maxtypepername == 0) { - return (false); - } - - return (ntypes >= rbtdb->maxtypepername); -} - -static bool -prio_header(dns_slabheader_t *header) { - if (NEGATIVE(header) && prio_type(DNS_TYPEPAIR_COVERS(header->type))) { - return (true); - } - - return (prio_type(header->type)); -} - -isc_result_t -dns__rbtdb_add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, - const dns_name_t *nodename, dns_rbtdb_version_t *rbtversion, - dns_slabheader_t *newheader, unsigned int options, bool loading, - dns_rdataset_t *addedrdataset, isc_stdtime_t now DNS__DB_FLARG) { - rbtdb_changed_t *changed = NULL; - dns_slabheader_t *topheader = NULL, *topheader_prev = NULL; - dns_slabheader_t *header = NULL, *sigheader = NULL; - dns_slabheader_t *prioheader = NULL, *expireheader = NULL; - unsigned char *merged = NULL; - isc_result_t result; - bool header_nx; - bool newheader_nx; - bool merge; - dns_rdatatype_t rdtype, covers; - dns_typepair_t negtype = 0, sigtype; - dns_trust_t trust; - int idx; - uint32_t ntypes = 0; - - if ((options & DNS_DBADD_MERGE) != 0) { - REQUIRE(rbtversion != NULL); - merge = true; - } else { - merge = false; - } - - if ((options & DNS_DBADD_FORCE) != 0) { - trust = dns_trust_ultimate; - } else { - trust = newheader->trust; - } - - if (rbtversion != NULL && !loading) { - /* - * We always add a changed record, even if no changes end up - * being made to this node, because it's harmless and - * simplifies the code. - */ - changed = add_changed(newheader, rbtversion DNS__DB_FLARG_PASS); - if (changed == NULL) { - dns_slabheader_destroy(&newheader); - return (ISC_R_NOMEMORY); - } - } - - newheader_nx = NONEXISTENT(newheader) ? true : false; - if (rbtversion == NULL && !newheader_nx) { - rdtype = DNS_TYPEPAIR_TYPE(newheader->type); - covers = DNS_TYPEPAIR_COVERS(newheader->type); - sigtype = DNS_SIGTYPE(covers); - if (NEGATIVE(newheader)) { - /* - * We're adding a negative cache entry. - */ - if (covers == dns_rdatatype_any) { - /* - * If we're adding an negative cache entry - * which covers all types (NXDOMAIN, - * NODATA(QTYPE=ANY)), - * - * We make all other data ancient so that the - * only rdataset that can be found at this - * node is the negative cache entry. - */ - for (topheader = rbtnode->data; - topheader != NULL; - topheader = topheader->next) - { - mark_ancient(topheader); - } - goto find_header; - } - /* - * Otherwise look for any RRSIGs of the given - * type so they can be marked ancient later. - */ - for (topheader = rbtnode->data; topheader != NULL; - topheader = topheader->next) - { - if (topheader->type == sigtype) { - sigheader = topheader; - break; - } - } - negtype = DNS_TYPEPAIR_VALUE(covers, 0); - } else { - /* - * We're adding something that isn't a - * negative cache entry. Look for an extant - * non-ancient NXDOMAIN/NODATA(QTYPE=ANY) negative - * cache entry. If we're adding an RRSIG, also - * check for an extant non-ancient NODATA ncache - * entry which covers the same type as the RRSIG. - */ - for (topheader = rbtnode->data; topheader != NULL; - topheader = topheader->next) - { - if ((topheader->type == RDATATYPE_NCACHEANY) || - (newheader->type == sigtype && - topheader->type == - DNS_TYPEPAIR_VALUE(0, covers))) - { - break; - } - } - if (topheader != NULL && EXISTS(topheader) && - ACTIVE(topheader, now)) - { - /* - * Found one. - */ - if (trust < topheader->trust) { - /* - * The NXDOMAIN/NODATA(QTYPE=ANY) - * is more trusted. - */ - dns_slabheader_destroy(&newheader); - if (addedrdataset != NULL) { - dns__rbtdb_bindrdataset( - rbtdb, rbtnode, - topheader, now, - isc_rwlocktype_write, - addedrdataset - DNS__DB_FLARG_PASS); - } - return (DNS_R_UNCHANGED); - } - /* - * The new rdataset is better. Expire the - * ncache entry. - */ - mark_ancient(topheader); - topheader = NULL; - goto find_header; - } - negtype = DNS_TYPEPAIR_VALUE(0, rdtype); - } - } - - for (topheader = rbtnode->data; topheader != NULL; - topheader = topheader->next) - { - if (IS_CACHE(rbtdb) && ACTIVE(topheader, now)) { - ++ntypes; - expireheader = topheader; - } else if (!IS_CACHE(rbtdb)) { - ++ntypes; - } - if (prio_header(topheader)) { - prioheader = topheader; - } - if (topheader->type == newheader->type || - topheader->type == negtype) - { - break; - } - topheader_prev = topheader; - } - -find_header: - /* - * If header isn't NULL, we've found the right type. There may be - * IGNORE rdatasets between the top of the chain and the first real - * data. We skip over them. - */ - header = topheader; - while (header != NULL && IGNORE(header)) { - header = header->down; - } - if (header != NULL) { - header_nx = NONEXISTENT(header) ? true : false; - - /* - * Deleting an already non-existent rdataset has no effect. - */ - if (header_nx && newheader_nx) { - dns_slabheader_destroy(&newheader); - return (DNS_R_UNCHANGED); - } - - /* - * Trying to add an rdataset with lower trust to a cache - * DB has no effect, provided that the cache data isn't - * stale. If the cache data is stale, new lower trust - * data will supersede it below. Unclear what the best - * policy is here. - */ - if (rbtversion == NULL && trust < header->trust && - (ACTIVE(header, now) || header_nx)) - { - dns_slabheader_destroy(&newheader); - if (addedrdataset != NULL) { - dns__rbtdb_bindrdataset( - rbtdb, rbtnode, header, now, - isc_rwlocktype_write, - addedrdataset DNS__DB_FLARG_PASS); - } - return (DNS_R_UNCHANGED); - } - - /* - * Don't merge if a nonexistent rdataset is involved. - */ - if (merge && (header_nx || newheader_nx)) { - merge = false; - } - - /* - * If 'merge' is true, we'll try to create a new rdataset - * that is the union of 'newheader' and 'header'. - */ - if (merge) { - unsigned int flags = 0; - INSIST(rbtversion->serial >= header->serial); - merged = NULL; - result = ISC_R_SUCCESS; - - if ((options & DNS_DBADD_EXACT) != 0) { - flags |= DNS_RDATASLAB_EXACT; - } - /* - * TTL use here is irrelevant to the cache; - * merge is only done with zonedbs. - */ - if ((options & DNS_DBADD_EXACTTTL) != 0 && - newheader->ttl != header->ttl) - { - result = DNS_R_NOTEXACT; - } else if (newheader->ttl != header->ttl) { - flags |= DNS_RDATASLAB_FORCE; - } - if (result == ISC_R_SUCCESS) { - result = dns_rdataslab_merge( - (unsigned char *)header, - (unsigned char *)newheader, - (unsigned int)(sizeof(*newheader)), - rbtdb->common.mctx, - rbtdb->common.rdclass, - (dns_rdatatype_t)header->type, flags, - rbtdb->maxrrperset, &merged); - } - if (result == ISC_R_SUCCESS) { - /* - * If 'header' has the same serial number as - * we do, we could clean it up now if we knew - * that our caller had no references to it. - * We don't know this, however, so we leave it - * alone. It will get cleaned up when - * clean_zone_node() runs. - */ - dns_slabheader_destroy(&newheader); - newheader = (dns_slabheader_t *)merged; - dns_slabheader_reset(newheader, - (dns_db_t *)rbtdb, - (dns_dbnode_t *)rbtnode); - dns_slabheader_copycase(newheader, header); - if (loading && RESIGN(newheader) && - RESIGN(header) && - resign_sooner(header, newheader)) - { - newheader->resign = header->resign; - newheader->resign_lsb = - header->resign_lsb; - } - } else { - dns_slabheader_destroy(&newheader); - return (result); - } - } - /* - * Don't replace existing NS, A and AAAA RRsets in the - * cache if they are already exist. This prevents named - * being locked to old servers. Don't lower trust of - * existing record if the update is forced. Nothing - * special to be done w.r.t stale data; it gets replaced - * normally further down. - */ - if (IS_CACHE(rbtdb) && ACTIVE(header, now) && - header->type == dns_rdatatype_ns && !header_nx && - !newheader_nx && header->trust >= newheader->trust && - dns_rdataslab_equalx((unsigned char *)header, - (unsigned char *)newheader, - (unsigned int)(sizeof(*newheader)), - rbtdb->common.rdclass, - (dns_rdatatype_t)header->type)) - { - /* - * Honour the new ttl if it is less than the - * older one. - */ - if (header->ttl > newheader->ttl) { - dns__rbtdb_setttl(header, newheader->ttl); - } - if (header->last_used != now) { - ISC_LIST_UNLINK( - rbtdb->lru[RBTDB_HEADERNODE(header) - ->locknum], - header, link); - header->last_used = now; - ISC_LIST_PREPEND( - rbtdb->lru[RBTDB_HEADERNODE(header) - ->locknum], - header, link); - } - if (header->noqname == NULL && - newheader->noqname != NULL) - { - header->noqname = newheader->noqname; - newheader->noqname = NULL; - } - if (header->closest == NULL && - newheader->closest != NULL) - { - header->closest = newheader->closest; - newheader->closest = NULL; - } - dns_slabheader_destroy(&newheader); - if (addedrdataset != NULL) { - dns__rbtdb_bindrdataset( - rbtdb, rbtnode, header, now, - isc_rwlocktype_write, - addedrdataset DNS__DB_FLARG_PASS); - } - return (ISC_R_SUCCESS); - } - - /* - * If we have will be replacing a NS RRset force its TTL - * to be no more than the current NS RRset's TTL. This - * ensures the delegations that are withdrawn are honoured. - */ - if (IS_CACHE(rbtdb) && ACTIVE(header, now) && - header->type == dns_rdatatype_ns && !header_nx && - !newheader_nx && header->trust <= newheader->trust) - { - if (newheader->ttl > header->ttl) { - newheader->ttl = header->ttl; - } - } - if (IS_CACHE(rbtdb) && ACTIVE(header, now) && - (options & DNS_DBADD_PREFETCH) == 0 && - (header->type == dns_rdatatype_a || - header->type == dns_rdatatype_aaaa || - header->type == dns_rdatatype_ds || - header->type == DNS_SIGTYPE(dns_rdatatype_ds)) && - !header_nx && !newheader_nx && - header->trust >= newheader->trust && - dns_rdataslab_equal((unsigned char *)header, - (unsigned char *)newheader, - (unsigned int)(sizeof(*newheader)))) - { - /* - * Honour the new ttl if it is less than the - * older one. - */ - if (header->ttl > newheader->ttl) { - dns__rbtdb_setttl(header, newheader->ttl); - } - if (header->last_used != now) { - ISC_LIST_UNLINK( - rbtdb->lru[RBTDB_HEADERNODE(header) - ->locknum], - header, link); - header->last_used = now; - ISC_LIST_PREPEND( - rbtdb->lru[RBTDB_HEADERNODE(header) - ->locknum], - header, link); - } - if (header->noqname == NULL && - newheader->noqname != NULL) - { - header->noqname = newheader->noqname; - newheader->noqname = NULL; - } - if (header->closest == NULL && - newheader->closest != NULL) - { - header->closest = newheader->closest; - newheader->closest = NULL; - } - dns_slabheader_destroy(&newheader); - if (addedrdataset != NULL) { - dns__rbtdb_bindrdataset( - rbtdb, rbtnode, header, now, - isc_rwlocktype_write, - addedrdataset DNS__DB_FLARG_PASS); - } - return (ISC_R_SUCCESS); - } - INSIST(rbtversion == NULL || - rbtversion->serial >= topheader->serial); - if (loading) { - newheader->down = NULL; - idx = RBTDB_HEADERNODE(newheader)->locknum; - if (IS_CACHE(rbtdb)) { - if (ZEROTTL(newheader)) { - newheader->last_used = - rbtdb->last_used + 1; - ISC_LIST_APPEND(rbtdb->lru[idx], - newheader, link); - } else { - ISC_LIST_PREPEND(rbtdb->lru[idx], - newheader, link); - } - INSIST(rbtdb->heaps != NULL); - isc_heap_insert(rbtdb->heaps[idx], newheader); - newheader->heap = rbtdb->heaps[idx]; - } else if (RESIGN(newheader)) { - dns__zonerbt_resigninsert(rbtdb, idx, - newheader); - /* - * Don't call resigndelete, we don't need - * to reverse the delete. The free_slabheader - * call below will clean up the heap entry. - */ - } - - /* - * There are no other references to 'header' when - * loading, so we MAY clean up 'header' now. - * Since we don't generate changed records when - * loading, we MUST clean up 'header' now. - */ - if (topheader_prev != NULL) { - topheader_prev->next = newheader; - } else { - rbtnode->data = newheader; - } - newheader->next = topheader->next; - if (rbtversion != NULL && !header_nx) { - update_recordsandxfrsize(false, rbtversion, - header, - nodename->length); - } - dns_slabheader_destroy(&header); - } else { - idx = RBTDB_HEADERNODE(newheader)->locknum; - if (IS_CACHE(rbtdb)) { - INSIST(rbtdb->heaps != NULL); - isc_heap_insert(rbtdb->heaps[idx], newheader); - newheader->heap = rbtdb->heaps[idx]; - if (ZEROTTL(newheader)) { - newheader->last_used = - rbtdb->last_used + 1; - ISC_LIST_APPEND(rbtdb->lru[idx], - newheader, link); - } else { - ISC_LIST_PREPEND(rbtdb->lru[idx], - newheader, link); - } - } else if (RESIGN(newheader)) { - dns__zonerbt_resigninsert(rbtdb, idx, - newheader); - dns__zonerbt_resigndelete( - rbtdb, rbtversion, - header DNS__DB_FLARG_PASS); - } - if (topheader_prev != NULL) { - topheader_prev->next = newheader; - } else { - rbtnode->data = newheader; - } - newheader->next = topheader->next; - newheader->down = topheader; - topheader->next = newheader; - rbtnode->dirty = 1; - if (changed != NULL) { - changed->dirty = true; - } - if (rbtversion == NULL) { - mark_ancient(header); - if (sigheader != NULL) { - mark_ancient(sigheader); - } - } - if (rbtversion != NULL && !header_nx) { - update_recordsandxfrsize(false, rbtversion, - header, - nodename->length); - } - } - } else { - /* - * No non-IGNORED rdatasets of the given type exist at - * this node. - */ - - /* - * If we're trying to delete the type, don't bother. - */ - if (newheader_nx) { - dns_slabheader_destroy(&newheader); - return (DNS_R_UNCHANGED); - } - - idx = RBTDB_HEADERNODE(newheader)->locknum; - if (IS_CACHE(rbtdb)) { - isc_heap_insert(rbtdb->heaps[idx], newheader); - newheader->heap = rbtdb->heaps[idx]; - if (ZEROTTL(newheader)) { - ISC_LIST_APPEND(rbtdb->lru[idx], newheader, - link); - } else { - ISC_LIST_PREPEND(rbtdb->lru[idx], newheader, - link); - } - } else if (RESIGN(newheader)) { - dns__zonerbt_resigninsert(rbtdb, idx, newheader); - dns__zonerbt_resigndelete(rbtdb, rbtversion, - header DNS__DB_FLARG_PASS); - } - - if (topheader != NULL) { - /* - * We have an list of rdatasets of the given type, - * but they're all marked IGNORE. We simply insert - * the new rdataset at the head of the list. - * - * Ignored rdatasets cannot occur during loading, so - * we INSIST on it. - */ - INSIST(!loading); - INSIST(rbtversion == NULL || - rbtversion->serial >= topheader->serial); - if (topheader_prev != NULL) { - topheader_prev->next = newheader; - } else { - rbtnode->data = newheader; - } - newheader->next = topheader->next; - newheader->down = topheader; - topheader->next = newheader; - rbtnode->dirty = 1; - if (changed != NULL) { - changed->dirty = true; - } - } else { - /* - * No rdatasets of the given type exist at the node. - */ - INSIST(newheader->down == NULL); - - if (!IS_CACHE(rbtdb) && overmaxtype(rbtdb, ntypes)) { - dns_slabheader_destroy(&newheader); - return (DNS_R_TOOMANYRECORDS); - } - - if (prio_header(newheader)) { - /* This is a priority type, prepend it */ - newheader->next = rbtnode->data; - rbtnode->data = newheader; - } else if (prioheader != NULL) { - /* Append after the priority headers */ - newheader->next = prioheader->next; - prioheader->next = newheader; - } else { - /* There were no priority headers */ - newheader->next = rbtnode->data; - rbtnode->data = newheader; - } - - if (IS_CACHE(rbtdb) && overmaxtype(rbtdb, ntypes)) { - if (expireheader == NULL) { - expireheader = newheader; - } - if (NEGATIVE(newheader) && - !prio_header(newheader)) - { - /* - * Add the new non-priority negative - * header to the database only - * temporarily. - */ - expireheader = newheader; - } - - mark_ancient(expireheader); - /* - * FIXME: In theory, we should mark the RRSIG - * and the header at the same time, but there is - * no direct link between those two header, so - * we would have to check the whole list again. - */ - } - } - } - - if (rbtversion != NULL && !newheader_nx) { - update_recordsandxfrsize(true, rbtversion, newheader, - nodename->length); - } - - /* - * Check if the node now contains CNAME and other data. - */ - if (rbtversion != NULL && - cname_and_other_data(rbtnode, rbtversion->serial)) - { - return (DNS_R_CNAMEANDOTHER); - } - - if (addedrdataset != NULL) { - dns__rbtdb_bindrdataset(rbtdb, rbtnode, newheader, now, - isc_rwlocktype_write, - addedrdataset DNS__DB_FLARG_PASS); - } - - return (ISC_R_SUCCESS); -} - -static bool -delegating_type(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, dns_typepair_t type) { - if (IS_CACHE(rbtdb)) { - if (type == dns_rdatatype_dname) { - return (true); - } else { - return (false); - } - } else if (type == dns_rdatatype_dname || - (type == dns_rdatatype_ns && - (node != rbtdb->origin_node || IS_STUB(rbtdb)))) - { - return (true); - } - return (false); -} - -static isc_result_t -addnoqname(isc_mem_t *mctx, dns_slabheader_t *newheader, uint32_t maxrrperset, - dns_rdataset_t *rdataset) { - isc_result_t result; - dns_slabheader_proof_t *noqname = NULL; - dns_name_t name = DNS_NAME_INITEMPTY; - dns_rdataset_t neg = DNS_RDATASET_INIT, negsig = DNS_RDATASET_INIT; - isc_region_t r1, r2; - - result = dns_rdataset_getnoqname(rdataset, &name, &neg, &negsig); - RUNTIME_CHECK(result == ISC_R_SUCCESS); - - result = dns_rdataslab_fromrdataset(&neg, mctx, &r1, 0, maxrrperset); - if (result != ISC_R_SUCCESS) { - goto cleanup; - } - - result = dns_rdataslab_fromrdataset(&negsig, mctx, &r2, 0, maxrrperset); - if (result != ISC_R_SUCCESS) { - goto cleanup; - } - - noqname = isc_mem_get(mctx, sizeof(*noqname)); - *noqname = (dns_slabheader_proof_t){ - .neg = r1.base, - .negsig = r2.base, - .type = neg.type, - .name = DNS_NAME_INITEMPTY, - }; - dns_name_dup(&name, mctx, &noqname->name); - newheader->noqname = noqname; - -cleanup: - dns_rdataset_disassociate(&neg); - dns_rdataset_disassociate(&negsig); - - return (result); -} - -static isc_result_t -addclosest(isc_mem_t *mctx, dns_slabheader_t *newheader, uint32_t maxrrperset, - dns_rdataset_t *rdataset) { - isc_result_t result; - dns_slabheader_proof_t *closest = NULL; - dns_name_t name = DNS_NAME_INITEMPTY; - dns_rdataset_t neg = DNS_RDATASET_INIT, negsig = DNS_RDATASET_INIT; - isc_region_t r1, r2; - - result = dns_rdataset_getclosest(rdataset, &name, &neg, &negsig); - RUNTIME_CHECK(result == ISC_R_SUCCESS); - - result = dns_rdataslab_fromrdataset(&neg, mctx, &r1, 0, maxrrperset); - if (result != ISC_R_SUCCESS) { - goto cleanup; - } - - result = dns_rdataslab_fromrdataset(&negsig, mctx, &r2, 0, maxrrperset); - if (result != ISC_R_SUCCESS) { - goto cleanup; - } - - closest = isc_mem_get(mctx, sizeof(*closest)); - *closest = (dns_slabheader_proof_t){ - .neg = r1.base, - .negsig = r2.base, - .name = DNS_NAME_INITEMPTY, - .type = neg.type, - }; - dns_name_dup(&name, mctx, &closest->name); - newheader->closest = closest; - -cleanup: - dns_rdataset_disassociate(&neg); - dns_rdataset_disassociate(&negsig); - return (result); -} - -static void -expire_ttl_headers(dns_rbtdb_t *rbtdb, unsigned int locknum, - isc_rwlocktype_t *tlocktypep, isc_stdtime_t now, - bool cache_is_overmem DNS__DB_FLARG); - -isc_result_t -dns__rbtdb_addrdataset(dns_db_t *db, dns_dbnode_t *node, - dns_dbversion_t *version, isc_stdtime_t now, - dns_rdataset_t *rdataset, unsigned int options, - dns_rdataset_t *addedrdataset DNS__DB_FLARG) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node; - dns_rbtdb_version_t *rbtversion = (dns_rbtdb_version_t *)version; - isc_region_t region; - dns_slabheader_t *newheader = NULL; - isc_result_t result; - bool delegating; - bool newnsec; - isc_rwlocktype_t tlocktype = isc_rwlocktype_none; - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - bool cache_is_overmem = false; - dns_fixedname_t fixed; - dns_name_t *name = NULL; - - REQUIRE(VALID_RBTDB(rbtdb)); - INSIST(rbtversion == NULL || rbtversion->rbtdb == rbtdb); - - if (!IS_CACHE(rbtdb)) { - /* - * SOA records are only allowed at top of zone. - */ - if (rdataset->type == dns_rdatatype_soa && - node != (dns_dbnode_t *)rbtdb->origin_node) - { - return (DNS_R_NOTZONETOP); - } - TREE_RDLOCK(&rbtdb->tree_lock, &tlocktype); - REQUIRE(((rbtnode->nsec == DNS_DB_NSEC_NSEC3 && - (rdataset->type == dns_rdatatype_nsec3 || - rdataset->covers == dns_rdatatype_nsec3)) || - (rbtnode->nsec != DNS_DB_NSEC_NSEC3 && - rdataset->type != dns_rdatatype_nsec3 && - rdataset->covers != dns_rdatatype_nsec3))); - TREE_UNLOCK(&rbtdb->tree_lock, &tlocktype); - } - - if (rbtversion == NULL) { - if (now == 0) { - now = isc_stdtime_now(); - } - } else { - now = 0; - } - - result = dns_rdataslab_fromrdataset(rdataset, rbtdb->common.mctx, - ®ion, sizeof(dns_slabheader_t), - rbtdb->maxrrperset); - if (result != ISC_R_SUCCESS) { - return (result); - } - - name = dns_fixedname_initname(&fixed); - dns__rbtdb_nodefullname(db, node, name); - dns_rdataset_getownercase(rdataset, name); - - newheader = (dns_slabheader_t *)region.base; - *newheader = (dns_slabheader_t){ - .type = DNS_TYPEPAIR_VALUE(rdataset->type, rdataset->covers), - .trust = rdataset->trust, - .last_used = now, - .node = (dns_dbnode_t *)rbtnode, - }; - - dns_slabheader_reset(newheader, db, node); - dns__rbtdb_setttl(newheader, rdataset->ttl + now); - if (rdataset->ttl == 0U) { - DNS_SLABHEADER_SETATTR(newheader, DNS_SLABHEADERATTR_ZEROTTL); - } - atomic_init(&newheader->count, - atomic_fetch_add_relaxed(&init_count, 1)); - if (rbtversion != NULL) { - newheader->serial = rbtversion->serial; - now = 0; - - if ((rdataset->attributes & DNS_RDATASETATTR_RESIGN) != 0) { - DNS_SLABHEADER_SETATTR(newheader, - DNS_SLABHEADERATTR_RESIGN); - newheader->resign = - (isc_stdtime_t)(dns_time64_from32( - rdataset->resign) >> - 1); - newheader->resign_lsb = rdataset->resign & 0x1; - } - } else { - newheader->serial = 1; - if ((rdataset->attributes & DNS_RDATASETATTR_PREFETCH) != 0) { - DNS_SLABHEADER_SETATTR(newheader, - DNS_SLABHEADERATTR_PREFETCH); - } - if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) { - DNS_SLABHEADER_SETATTR(newheader, - DNS_SLABHEADERATTR_NEGATIVE); - } - if ((rdataset->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0) { - DNS_SLABHEADER_SETATTR(newheader, - DNS_SLABHEADERATTR_NXDOMAIN); - } - if ((rdataset->attributes & DNS_RDATASETATTR_OPTOUT) != 0) { - DNS_SLABHEADER_SETATTR(newheader, - DNS_SLABHEADERATTR_OPTOUT); - } - if ((rdataset->attributes & DNS_RDATASETATTR_NOQNAME) != 0) { - result = addnoqname(rbtdb->common.mctx, newheader, - rbtdb->maxrrperset, rdataset); - if (result != ISC_R_SUCCESS) { - dns_slabheader_destroy(&newheader); - return (result); - } - } - if ((rdataset->attributes & DNS_RDATASETATTR_CLOSEST) != 0) { - result = addclosest(rbtdb->common.mctx, newheader, - rbtdb->maxrrperset, rdataset); - if (result != ISC_R_SUCCESS) { - dns_slabheader_destroy(&newheader); - return (result); - } - } - } - - /* - * If we're adding a delegation type (e.g. NS or DNAME for a zone, - * just DNAME for the cache), then we need to set the callback bit - * on the node. - */ - if (delegating_type(rbtdb, rbtnode, rdataset->type)) { - delegating = true; - } else { - delegating = false; - } - - /* - * Add to the auxiliary NSEC tree if we're adding an NSEC record. - */ - TREE_RDLOCK(&rbtdb->tree_lock, &tlocktype); - if (rbtnode->nsec != DNS_DB_NSEC_HAS_NSEC && - rdataset->type == dns_rdatatype_nsec) - { - newnsec = true; - } else { - newnsec = false; - } - TREE_UNLOCK(&rbtdb->tree_lock, &tlocktype); - - /* - * If we're adding a delegation type, adding to the auxiliary NSEC - * tree, or the DB is a cache in an overmem state, hold an - * exclusive lock on the tree. In the latter case the lock does - * not necessarily have to be acquired but it will help purge - * ancient entries more effectively. - */ - if (IS_CACHE(rbtdb) && isc_mem_isovermem(rbtdb->common.mctx)) { - cache_is_overmem = true; - } - if (delegating || newnsec || cache_is_overmem) { - TREE_WRLOCK(&rbtdb->tree_lock, &tlocktype); - } - - if (cache_is_overmem) { - dns__cacherbt_overmem(rbtdb, newheader, - &tlocktype DNS__DB_FLARG_PASS); - } - - NODE_WRLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype); - - if (rbtdb->rrsetstats != NULL) { - DNS_SLABHEADER_SETATTR(newheader, DNS_SLABHEADERATTR_STATCOUNT); - update_rrsetstats(rbtdb->rrsetstats, newheader->type, - atomic_load_acquire(&newheader->attributes), - true); - } - - if (IS_CACHE(rbtdb)) { - if (tlocktype == isc_rwlocktype_write) { - cleanup_dead_nodes(rbtdb, - rbtnode->locknum DNS__DB_FLARG_PASS); - } - - expire_ttl_headers(rbtdb, rbtnode->locknum, &tlocktype, now, - cache_is_overmem DNS__DB_FLARG_PASS); - - /* - * If we've been holding a write lock on the tree just for - * cleaning, we can release it now. However, we still need the - * node lock. - */ - if (tlocktype == isc_rwlocktype_write && !delegating && - !newnsec) - { - TREE_UNLOCK(&rbtdb->tree_lock, &tlocktype); - } - } - - result = ISC_R_SUCCESS; - if (newnsec) { - dns_rbtnode_t *nsecnode = NULL; - - result = dns_rbt_addnode(rbtdb->nsec, name, &nsecnode); - if (result == ISC_R_SUCCESS) { - nsecnode->nsec = DNS_DB_NSEC_NSEC; - rbtnode->nsec = DNS_DB_NSEC_HAS_NSEC; - } else if (result == ISC_R_EXISTS) { - rbtnode->nsec = DNS_DB_NSEC_HAS_NSEC; - result = ISC_R_SUCCESS; - } - } - - if (result == ISC_R_SUCCESS) { - result = dns__rbtdb_add(rbtdb, rbtnode, name, rbtversion, - newheader, options, false, - addedrdataset, now DNS__DB_FLARG_PASS); - } - if (result == ISC_R_SUCCESS && delegating) { - rbtnode->find_callback = 1; - } - - NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype); - - if (tlocktype != isc_rwlocktype_none) { - TREE_UNLOCK(&rbtdb->tree_lock, &tlocktype); - } - INSIST(tlocktype == isc_rwlocktype_none); - - return (result); -} - -isc_result_t -dns__rbtdb_subtractrdataset(dns_db_t *db, dns_dbnode_t *node, - dns_dbversion_t *version, dns_rdataset_t *rdataset, - unsigned int options, - dns_rdataset_t *newrdataset DNS__DB_FLARG) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node; - dns_rbtdb_version_t *rbtversion = (dns_rbtdb_version_t *)version; - dns_fixedname_t fname; - dns_name_t *nodename = dns_fixedname_initname(&fname); - dns_slabheader_t *topheader = NULL, *topheader_prev = NULL; - dns_slabheader_t *header = NULL, *newheader = NULL; - unsigned char *subresult = NULL; - isc_region_t region; - isc_result_t result; - rbtdb_changed_t *changed = NULL; - isc_rwlocktype_t tlocktype = isc_rwlocktype_none; - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - - REQUIRE(VALID_RBTDB(rbtdb)); - REQUIRE(rbtversion != NULL && rbtversion->rbtdb == rbtdb); - - if (!IS_CACHE(rbtdb)) { - TREE_RDLOCK(&rbtdb->tree_lock, &tlocktype); - REQUIRE(((rbtnode->nsec == DNS_DB_NSEC_NSEC3 && - (rdataset->type == dns_rdatatype_nsec3 || - rdataset->covers == dns_rdatatype_nsec3)) || - (rbtnode->nsec != DNS_DB_NSEC_NSEC3 && - rdataset->type != dns_rdatatype_nsec3 && - rdataset->covers != dns_rdatatype_nsec3))); - TREE_UNLOCK(&rbtdb->tree_lock, &tlocktype); - } - - dns__rbtdb_nodefullname(db, node, nodename); - - result = dns_rdataslab_fromrdataset(rdataset, rbtdb->common.mctx, - ®ion, sizeof(dns_slabheader_t), - 0); - if (result != ISC_R_SUCCESS) { - return (result); - } - - newheader = (dns_slabheader_t *)region.base; - dns_slabheader_reset(newheader, db, node); - dns__rbtdb_setttl(newheader, rdataset->ttl); - newheader->type = DNS_TYPEPAIR_VALUE(rdataset->type, rdataset->covers); - atomic_init(&newheader->attributes, 0); - newheader->serial = rbtversion->serial; - newheader->trust = 0; - newheader->noqname = NULL; - newheader->closest = NULL; - atomic_init(&newheader->count, - atomic_fetch_add_relaxed(&init_count, 1)); - newheader->last_used = 0; - newheader->node = (dns_dbnode_t *)rbtnode; - newheader->db = (dns_db_t *)rbtdb; - if ((rdataset->attributes & DNS_RDATASETATTR_RESIGN) != 0) { - DNS_SLABHEADER_SETATTR(newheader, DNS_SLABHEADERATTR_RESIGN); - newheader->resign = - (isc_stdtime_t)(dns_time64_from32(rdataset->resign) >> - 1); - newheader->resign_lsb = rdataset->resign & 0x1; - } else { - newheader->resign = 0; - newheader->resign_lsb = 0; - } - - NODE_WRLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype); - - changed = add_changed(newheader, rbtversion DNS__DB_FLARG_PASS); - if (changed == NULL) { - dns_slabheader_destroy(&newheader); - NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, - &nlocktype); - return (ISC_R_NOMEMORY); - } - - for (topheader = rbtnode->data; topheader != NULL; - topheader = topheader->next) - { - if (topheader->type == newheader->type) { - break; - } - topheader_prev = topheader; - } - /* - * If header isn't NULL, we've found the right type. There may be - * IGNORE rdatasets between the top of the chain and the first real - * data. We skip over them. - */ - header = topheader; - while (header != NULL && IGNORE(header)) { - header = header->down; - } - if (header != NULL && EXISTS(header)) { - unsigned int flags = 0; - subresult = NULL; - result = ISC_R_SUCCESS; - if ((options & DNS_DBSUB_EXACT) != 0) { - flags |= DNS_RDATASLAB_EXACT; - if (newheader->ttl != header->ttl) { - result = DNS_R_NOTEXACT; - } - } - if (result == ISC_R_SUCCESS) { - result = dns_rdataslab_subtract( - (unsigned char *)header, - (unsigned char *)newheader, - (unsigned int)(sizeof(*newheader)), - rbtdb->common.mctx, rbtdb->common.rdclass, - (dns_rdatatype_t)header->type, flags, - &subresult); - } - if (result == ISC_R_SUCCESS) { - dns_slabheader_destroy(&newheader); - newheader = (dns_slabheader_t *)subresult; - dns_slabheader_reset(newheader, db, node); - dns_slabheader_copycase(newheader, header); - if (RESIGN(header)) { - DNS_SLABHEADER_SETATTR( - newheader, DNS_SLABHEADERATTR_RESIGN); - newheader->resign = header->resign; - newheader->resign_lsb = header->resign_lsb; - dns__zonerbt_resigninsert( - rbtdb, rbtnode->locknum, newheader); - } - /* - * We have to set the serial since the rdataslab - * subtraction routine copies the reserved portion of - * header, not newheader. - */ - newheader->serial = rbtversion->serial; - /* - * XXXJT: dns_rdataslab_subtract() copied the pointers - * to additional info. We need to clear these fields - * to avoid having duplicated references. - */ - update_recordsandxfrsize(true, rbtversion, newheader, - nodename->length); - } else if (result == DNS_R_NXRRSET) { - /* - * This subtraction would remove all of the rdata; - * add a nonexistent header instead. - */ - dns_slabheader_destroy(&newheader); - newheader = dns_slabheader_new((dns_db_t *)rbtdb, - (dns_dbnode_t *)rbtnode); - dns__rbtdb_setttl(newheader, 0); - newheader->type = topheader->type; - atomic_init(&newheader->attributes, - DNS_SLABHEADERATTR_NONEXISTENT); - newheader->serial = rbtversion->serial; - } else { - dns_slabheader_destroy(&newheader); - goto unlock; - } - - /* - * If we're here, we want to link newheader in front of - * topheader. - */ - INSIST(rbtversion->serial >= topheader->serial); - update_recordsandxfrsize(false, rbtversion, header, - nodename->length); - if (topheader_prev != NULL) { - topheader_prev->next = newheader; - } else { - rbtnode->data = newheader; - } - newheader->next = topheader->next; - newheader->down = topheader; - topheader->next = newheader; - rbtnode->dirty = 1; - changed->dirty = true; - dns__zonerbt_resigndelete(rbtdb, rbtversion, - header DNS__DB_FLARG_PASS); - } else { - /* - * The rdataset doesn't exist, so we don't need to do anything - * to satisfy the deletion request. - */ - dns_slabheader_destroy(&newheader); - if ((options & DNS_DBSUB_EXACT) != 0) { - result = DNS_R_NOTEXACT; - } else { - result = DNS_R_UNCHANGED; - } - } - - if (result == ISC_R_SUCCESS && newrdataset != NULL) { - dns__rbtdb_bindrdataset(rbtdb, rbtnode, newheader, 0, - isc_rwlocktype_write, - newrdataset DNS__DB_FLARG_PASS); - } - - if (result == DNS_R_NXRRSET && newrdataset != NULL && - (options & DNS_DBSUB_WANTOLD) != 0) - { - dns__rbtdb_bindrdataset(rbtdb, rbtnode, header, 0, - isc_rwlocktype_write, - newrdataset DNS__DB_FLARG_PASS); - } - -unlock: - NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype); - - return (result); -} - -isc_result_t -dns__rbtdb_deleterdataset(dns_db_t *db, dns_dbnode_t *node, - dns_dbversion_t *version, dns_rdatatype_t type, - dns_rdatatype_t covers DNS__DB_FLARG) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node; - dns_rbtdb_version_t *rbtversion = (dns_rbtdb_version_t *)version; - dns_fixedname_t fname; - dns_name_t *nodename = dns_fixedname_initname(&fname); - isc_result_t result; - dns_slabheader_t *newheader = NULL; - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - - REQUIRE(VALID_RBTDB(rbtdb)); - INSIST(rbtversion == NULL || rbtversion->rbtdb == rbtdb); - - if (type == dns_rdatatype_any) { - return (ISC_R_NOTIMPLEMENTED); - } - if (type == dns_rdatatype_rrsig && covers == 0) { - return (ISC_R_NOTIMPLEMENTED); - } - - newheader = dns_slabheader_new(db, node); - newheader->type = DNS_TYPEPAIR_VALUE(type, covers); - dns__rbtdb_setttl(newheader, 0); - atomic_init(&newheader->attributes, DNS_SLABHEADERATTR_NONEXISTENT); - if (rbtversion != NULL) { - newheader->serial = rbtversion->serial; - } - - dns__rbtdb_nodefullname(db, node, nodename); - - NODE_WRLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype); - result = dns__rbtdb_add(rbtdb, rbtnode, nodename, rbtversion, newheader, - DNS_DBADD_FORCE, false, NULL, - 0 DNS__DB_FLARG_PASS); - NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype); - - /* - * Update the zone's secure status. If version is non-NULL - * this is deferred until dns__rbtdb_closeversion() is called. - */ - if (result == ISC_R_SUCCESS && rbtversion == NULL && !IS_CACHE(rbtdb)) { - RWLOCK(&rbtdb->lock, isc_rwlocktype_read); - rbtversion = rbtdb->current_version; - RWUNLOCK(&rbtdb->lock, isc_rwlocktype_read); - dns__rbtdb_setsecure(db, rbtversion, - (dns_dbnode_t *)rbtdb->origin_node); - } - - return (result); -} - -static void -delete_callback(void *data, void *arg) { - dns_rbtdb_t *rbtdb = arg; - dns_slabheader_t *current = NULL, *next = NULL; - unsigned int locknum; - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - - current = data; - locknum = RBTDB_HEADERNODE(current)->locknum; - NODE_WRLOCK(&rbtdb->node_locks[locknum].lock, &nlocktype); - while (current != NULL) { - next = current->next; - dns_slabheader_destroy(¤t); - current = next; - } - NODE_UNLOCK(&rbtdb->node_locks[locknum].lock, &nlocktype); -} - -unsigned int -dns__rbtdb_nodecount(dns_db_t *db, dns_dbtree_t tree) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - unsigned int count; - isc_rwlocktype_t tlocktype = isc_rwlocktype_none; - - REQUIRE(VALID_RBTDB(rbtdb)); - - TREE_RDLOCK(&rbtdb->tree_lock, &tlocktype); - switch (tree) { - case dns_dbtree_main: - count = dns_rbt_nodecount(rbtdb->tree); - break; - case dns_dbtree_nsec: - count = dns_rbt_nodecount(rbtdb->nsec); - break; - case dns_dbtree_nsec3: - count = dns_rbt_nodecount(rbtdb->nsec3); - break; - default: - UNREACHABLE(); - } - TREE_UNLOCK(&rbtdb->tree_lock, &tlocktype); - - return (count); -} - -void -dns__rbtdb_setloop(dns_db_t *db, isc_loop_t *loop) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - - REQUIRE(VALID_RBTDB(rbtdb)); - - RWLOCK(&rbtdb->lock, isc_rwlocktype_write); - if (rbtdb->loop != NULL) { - isc_loop_detach(&rbtdb->loop); - } - if (loop != NULL) { - isc_loop_attach(loop, &rbtdb->loop); - } - RWUNLOCK(&rbtdb->lock, isc_rwlocktype_write); -} - -isc_result_t -dns__rbtdb_getoriginnode(dns_db_t *db, dns_dbnode_t **nodep DNS__DB_FLARG) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - dns_rbtnode_t *onode = NULL; - isc_result_t result = ISC_R_SUCCESS; - - REQUIRE(VALID_RBTDB(rbtdb)); - REQUIRE(nodep != NULL && *nodep == NULL); - - /* Note that the access to origin_node doesn't require a DB lock */ - onode = (dns_rbtnode_t *)rbtdb->origin_node; - if (onode != NULL) { - dns__rbtdb_newref(rbtdb, onode, - isc_rwlocktype_none DNS__DB_FLARG_PASS); - *nodep = (dns_dbnode_t *)rbtdb->origin_node; - } else { - INSIST(IS_CACHE(rbtdb)); - result = ISC_R_NOTFOUND; - } - - return (result); -} - -void -dns__rbtdb_locknode(dns_db_t *db, dns_dbnode_t *node, isc_rwlocktype_t type) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node; - - RWLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, type); -} - -void -dns__rbtdb_unlocknode(dns_db_t *db, dns_dbnode_t *node, isc_rwlocktype_t type) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node; - - RWUNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, type); -} - -isc_result_t -dns__rbtdb_nodefullname(dns_db_t *db, dns_dbnode_t *node, dns_name_t *name) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node; - isc_result_t result; - isc_rwlocktype_t tlocktype = isc_rwlocktype_none; - - REQUIRE(VALID_RBTDB(rbtdb)); - REQUIRE(node != NULL); - REQUIRE(name != NULL); - - TREE_RDLOCK(&rbtdb->tree_lock, &tlocktype); - result = dns_rbt_fullnamefromnode(rbtnode, name); - TREE_UNLOCK(&rbtdb->tree_lock, &tlocktype); - - return (result); -} - -isc_result_t -dns__rbtdb_create(isc_mem_t *mctx, const dns_name_t *origin, dns_dbtype_t type, - dns_rdataclass_t rdclass, unsigned int argc, char *argv[], - void *driverarg ISC_ATTR_UNUSED, dns_db_t **dbp) { - dns_rbtdb_t *rbtdb = NULL; - isc_result_t result; - int i; - dns_name_t name; - isc_mem_t *hmctx = mctx; - - rbtdb = isc_mem_get(mctx, sizeof(*rbtdb)); - *rbtdb = (dns_rbtdb_t){ - .common.origin = DNS_NAME_INITEMPTY, - .common.rdclass = rdclass, - .current_serial = 1, - .least_serial = 1, - .next_serial = 2, - .open_versions = ISC_LIST_INITIALIZER, - }; - - isc_refcount_init(&rbtdb->common.references, 1); - - /* - * If argv[0] exists, it points to a memory context to use for heap - */ - if (argc != 0) { - hmctx = (isc_mem_t *)argv[0]; - } - - if (type == dns_dbtype_cache) { - rbtdb->common.methods = &dns__rbtdb_cachemethods; - rbtdb->common.attributes |= DNS_DBATTR_CACHE; - } else if (type == dns_dbtype_stub) { - rbtdb->common.methods = &dns__rbtdb_zonemethods; - rbtdb->common.attributes |= DNS_DBATTR_STUB; - } else { - rbtdb->common.methods = &dns__rbtdb_zonemethods; - } - - isc_rwlock_init(&rbtdb->lock); - TREE_INITLOCK(&rbtdb->tree_lock); - - /* - * Initialize node_lock_count in a generic way to support future - * extension which allows the user to specify this value on creation. - * Note that when specified for a cache DB it must be larger than 1 - * as commented with the definition of DEFAULT_CACHE_NODE_LOCK_COUNT. - */ - if (rbtdb->node_lock_count == 0) { - if (IS_CACHE(rbtdb)) { - rbtdb->node_lock_count = DEFAULT_CACHE_NODE_LOCK_COUNT; - } else { - rbtdb->node_lock_count = DEFAULT_NODE_LOCK_COUNT; - } - } else if (rbtdb->node_lock_count < 2 && IS_CACHE(rbtdb)) { - result = ISC_R_RANGE; - goto cleanup_tree_lock; - } - INSIST(rbtdb->node_lock_count < (1 << DNS_RBT_LOCKLENGTH)); - rbtdb->node_locks = isc_mem_get(mctx, rbtdb->node_lock_count * - sizeof(db_nodelock_t)); - - rbtdb->common.update_listeners = cds_lfht_new(16, 16, 0, 0, NULL); - - if (IS_CACHE(rbtdb)) { - dns_rdatasetstats_create(mctx, &rbtdb->rrsetstats); - rbtdb->lru = isc_mem_get(mctx, - rbtdb->node_lock_count * - sizeof(dns_slabheaderlist_t)); - for (i = 0; i < (int)rbtdb->node_lock_count; i++) { - ISC_LIST_INIT(rbtdb->lru[i]); - } - } - - /* - * Create the heaps. - */ - rbtdb->heaps = isc_mem_get(hmctx, rbtdb->node_lock_count * - sizeof(isc_heap_t *)); - for (i = 0; i < (int)rbtdb->node_lock_count; i++) { - rbtdb->heaps[i] = NULL; - } - - rbtdb->sooner = IS_CACHE(rbtdb) ? ttl_sooner : resign_sooner; - for (i = 0; i < (int)rbtdb->node_lock_count; i++) { - isc_heap_create(hmctx, rbtdb->sooner, set_index, 0, - &rbtdb->heaps[i]); - } - - /* - * Create deadnode lists. - */ - rbtdb->deadnodes = isc_mem_get(mctx, rbtdb->node_lock_count * - sizeof(dns_rbtnodelist_t)); - for (i = 0; i < (int)rbtdb->node_lock_count; i++) { - ISC_LIST_INIT(rbtdb->deadnodes[i]); - } - - rbtdb->active = rbtdb->node_lock_count; - - for (i = 0; i < (int)(rbtdb->node_lock_count); i++) { - NODE_INITLOCK(&rbtdb->node_locks[i].lock); - isc_refcount_init(&rbtdb->node_locks[i].references, 0); - rbtdb->node_locks[i].exiting = false; - } - - /* - * Attach to the mctx. The database will persist so long as there - * are references to it, and attaching to the mctx ensures that our - * mctx won't disappear out from under us. - */ - isc_mem_attach(mctx, &rbtdb->common.mctx); - isc_mem_attach(hmctx, &rbtdb->hmctx); - - /* - * Make a copy of the origin name. - */ - dns_name_dupwithoffsets(origin, mctx, &rbtdb->common.origin); - - /* - * Make the Red-Black Trees. - */ - result = dns_rbt_create(mctx, delete_callback, rbtdb, &rbtdb->tree); - if (result != ISC_R_SUCCESS) { - free_rbtdb(rbtdb, false); - return (result); - } - - result = dns_rbt_create(mctx, delete_callback, rbtdb, &rbtdb->nsec); - if (result != ISC_R_SUCCESS) { - free_rbtdb(rbtdb, false); - return (result); - } - - result = dns_rbt_create(mctx, delete_callback, rbtdb, &rbtdb->nsec3); - if (result != ISC_R_SUCCESS) { - free_rbtdb(rbtdb, false); - return (result); - } - - /* - * In order to set the node callback bit correctly in zone databases, - * we need to know if the node has the origin name of the zone. - * In loading_addrdataset() we could simply compare the new name - * to the origin name, but this is expensive. Also, we don't know the - * node name in dns__rbtdb_addrdataset(), so we need another way of - * knowing the zone's top. - * - * We now explicitly create a node for the zone's origin, and then - * we simply remember the node's address. This is safe, because - * the top-of-zone node can never be deleted, nor can its address - * change. - */ - if (!IS_CACHE(rbtdb)) { - result = dns_rbt_addnode(rbtdb->tree, &rbtdb->common.origin, - &rbtdb->origin_node); - if (result != ISC_R_SUCCESS) { - INSIST(result != ISC_R_EXISTS); - free_rbtdb(rbtdb, false); - return (result); - } - INSIST(rbtdb->origin_node != NULL); - rbtdb->origin_node->nsec = DNS_DB_NSEC_NORMAL; - /* - * We need to give the origin node the right locknum. - */ - dns_name_init(&name, NULL); - dns_rbt_namefromnode(rbtdb->origin_node, &name); - rbtdb->origin_node->locknum = rbtdb->origin_node->hashval % - rbtdb->node_lock_count; - /* - * Add an apex node to the NSEC3 tree so that NSEC3 searches - * return partial matches when there is only a single NSEC3 - * record in the tree. - */ - result = dns_rbt_addnode(rbtdb->nsec3, &rbtdb->common.origin, - &rbtdb->nsec3_origin_node); - if (result != ISC_R_SUCCESS) { - INSIST(result != ISC_R_EXISTS); - free_rbtdb(rbtdb, false); - return (result); - } - rbtdb->nsec3_origin_node->nsec = DNS_DB_NSEC_NSEC3; - /* - * We need to give the nsec3 origin node the right locknum. - */ - dns_name_init(&name, NULL); - dns_rbt_namefromnode(rbtdb->nsec3_origin_node, &name); - rbtdb->nsec3_origin_node->locknum = - rbtdb->nsec3_origin_node->hashval % - rbtdb->node_lock_count; - } - - /* - * Version Initialization. - */ - rbtdb->current_version = allocate_version(mctx, 1, 1, false); - rbtdb->current_version->rbtdb = rbtdb; - - /* - * Keep the current version in the open list so that list operation - * won't happen in normal lookup operations. - */ - PREPEND(rbtdb->open_versions, rbtdb->current_version, link); - - rbtdb->common.magic = DNS_DB_MAGIC; - rbtdb->common.impmagic = RBTDB_MAGIC; - - *dbp = (dns_db_t *)rbtdb; - - return (ISC_R_SUCCESS); - -cleanup_tree_lock: - TREE_DESTROYLOCK(&rbtdb->tree_lock); - isc_rwlock_destroy(&rbtdb->lock); - isc_mem_put(mctx, rbtdb, sizeof(*rbtdb)); - return (result); -} - -/* - * Rdataset Iterator Methods - */ - -static void -rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp DNS__DB_FLARG) { - rbtdb_rdatasetiter_t *rbtiterator = NULL; - - rbtiterator = (rbtdb_rdatasetiter_t *)(*iteratorp); - - if (rbtiterator->common.version != NULL) { - dns__rbtdb_closeversion(rbtiterator->common.db, - &rbtiterator->common.version, - false DNS__DB_FLARG_PASS); - } - dns__db_detachnode(rbtiterator->common.db, - &rbtiterator->common.node DNS__DB_FLARG_PASS); - isc_mem_put(rbtiterator->common.db->mctx, rbtiterator, - sizeof(*rbtiterator)); - - *iteratorp = NULL; -} - -static bool -iterator_active(dns_rbtdb_t *rbtdb, rbtdb_rdatasetiter_t *rbtiterator, - dns_slabheader_t *header) { - dns_ttl_t stale_ttl = header->ttl + STALE_TTL(header, rbtdb); - - /* - * Is this a "this rdataset doesn't exist" record? - */ - if (NONEXISTENT(header)) { - return (false); - } - - /* - * If this is a zone or this header still active then return it. - */ - if (!IS_CACHE(rbtdb) || ACTIVE(header, rbtiterator->common.now)) { - return (true); - } - - /* - * If we are not returning stale records or the rdataset is - * too old don't return it. - */ - if (!STALEOK(rbtiterator) || (rbtiterator->common.now > stale_ttl)) { - return (false); - } - return (true); -} - -static isc_result_t -rdatasetiter_first(dns_rdatasetiter_t *iterator DNS__DB_FLARG) { - rbtdb_rdatasetiter_t *rbtiterator = (rbtdb_rdatasetiter_t *)iterator; - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)(rbtiterator->common.db); - dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)rbtiterator->common.node; - dns_rbtdb_version_t *rbtversion = - (dns_rbtdb_version_t *)rbtiterator->common.version; - dns_slabheader_t *header = NULL, *top_next = NULL; - uint32_t serial = IS_CACHE(rbtdb) ? 1 : rbtversion->serial; - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - - NODE_RDLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype); - - for (header = rbtnode->data; header != NULL; header = top_next) { - top_next = header->next; - do { - if (EXPIREDOK(rbtiterator)) { - if (!NONEXISTENT(header)) { - break; - } - header = header->down; - } else if (header->serial <= serial && !IGNORE(header)) - { - if (!iterator_active(rbtdb, rbtiterator, - header)) - { - header = NULL; - } - break; - } else { - header = header->down; - } - } while (header != NULL); - if (header != NULL) { - break; - } - } - - NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype); - - rbtiterator->current = header; - - if (header == NULL) { - return (ISC_R_NOMORE); - } - - return (ISC_R_SUCCESS); -} - -static isc_result_t -rdatasetiter_next(dns_rdatasetiter_t *iterator DNS__DB_FLARG) { - rbtdb_rdatasetiter_t *rbtiterator = (rbtdb_rdatasetiter_t *)iterator; - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)(rbtiterator->common.db); - dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)rbtiterator->common.node; - dns_rbtdb_version_t *rbtversion = - (dns_rbtdb_version_t *)rbtiterator->common.version; - dns_slabheader_t *header = NULL, *top_next = NULL; - uint32_t serial = IS_CACHE(rbtdb) ? 1 : rbtversion->serial; - dns_typepair_t type, negtype; - dns_rdatatype_t rdtype, covers; - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - bool expiredok = EXPIREDOK(rbtiterator); - - header = rbtiterator->current; - if (header == NULL) { - return (ISC_R_NOMORE); - } - - NODE_RDLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype); - - type = header->type; - rdtype = DNS_TYPEPAIR_TYPE(header->type); - if (NEGATIVE(header)) { - covers = DNS_TYPEPAIR_COVERS(header->type); - negtype = DNS_TYPEPAIR_VALUE(covers, 0); - } else { - negtype = DNS_TYPEPAIR_VALUE(0, rdtype); - } - - /* - * Find the start of the header chain for the next type - * by walking back up the list. - */ - top_next = header->next; - while (top_next != NULL && - (top_next->type == type || top_next->type == negtype)) - { - top_next = top_next->next; - } - if (expiredok) { - /* - * Keep walking down the list if possible or - * start the next type. - */ - header = header->down != NULL ? header->down : top_next; - } else { - header = top_next; - } - for (; header != NULL; header = top_next) { - top_next = header->next; - do { - if (expiredok) { - if (!NONEXISTENT(header)) { - break; - } - header = header->down; - } else if (header->serial <= serial && !IGNORE(header)) - { - if (!iterator_active(rbtdb, rbtiterator, - header)) - { - header = NULL; - } - break; - } else { - header = header->down; - } - } while (header != NULL); - if (header != NULL) { - break; - } - /* - * Find the start of the header chain for the next type - * by walking back up the list. - */ - while (top_next != NULL && - (top_next->type == type || top_next->type == negtype)) - { - top_next = top_next->next; - } - } - - NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype); - - rbtiterator->current = header; - - if (header == NULL) { - return (ISC_R_NOMORE); - } - - return (ISC_R_SUCCESS); -} - -static void -rdatasetiter_current(dns_rdatasetiter_t *iterator, - dns_rdataset_t *rdataset DNS__DB_FLARG) { - rbtdb_rdatasetiter_t *rbtiterator = (rbtdb_rdatasetiter_t *)iterator; - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)(rbtiterator->common.db); - dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)rbtiterator->common.node; - dns_slabheader_t *header = NULL; - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - - header = rbtiterator->current; - REQUIRE(header != NULL); - - NODE_RDLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype); - - dns__rbtdb_bindrdataset(rbtdb, rbtnode, header, rbtiterator->common.now, - isc_rwlocktype_read, - rdataset DNS__DB_FLARG_PASS); - - NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock, &nlocktype); -} - -/* - * Database Iterator Methods - */ - -static void -reference_iter_node(rbtdb_dbiterator_t *rbtdbiter DNS__DB_FLARG) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)rbtdbiter->common.db; - dns_rbtnode_t *node = rbtdbiter->node; - - if (node == NULL) { - return; - } - - INSIST(rbtdbiter->tree_locked != isc_rwlocktype_none); - reactivate_node(rbtdb, node, rbtdbiter->tree_locked DNS__DB_FLARG_PASS); -} - -static void -dereference_iter_node(rbtdb_dbiterator_t *rbtdbiter DNS__DB_FLARG) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)rbtdbiter->common.db; - dns_rbtnode_t *node = rbtdbiter->node; - isc_rwlock_t *lock = NULL; - isc_rwlocktype_t nlocktype = isc_rwlocktype_none; - isc_rwlocktype_t tlocktype = rbtdbiter->tree_locked; - - if (node == NULL) { - return; - } - - REQUIRE(tlocktype != isc_rwlocktype_write); - - lock = &rbtdb->node_locks[node->locknum].lock; - NODE_RDLOCK(lock, &nlocktype); - dns__rbtdb_decref(rbtdb, node, 0, &nlocktype, &rbtdbiter->tree_locked, - false, false DNS__DB_FLARG_PASS); - NODE_UNLOCK(lock, &nlocktype); - - INSIST(rbtdbiter->tree_locked == tlocktype); - - rbtdbiter->node = NULL; -} - -static void -resume_iteration(rbtdb_dbiterator_t *rbtdbiter) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)rbtdbiter->common.db; - - REQUIRE(rbtdbiter->paused); - REQUIRE(rbtdbiter->tree_locked == isc_rwlocktype_none); - - TREE_RDLOCK(&rbtdb->tree_lock, &rbtdbiter->tree_locked); - - rbtdbiter->paused = false; -} - -static void -dbiterator_destroy(dns_dbiterator_t **iteratorp DNS__DB_FLARG) { - rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)(*iteratorp); - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)rbtdbiter->common.db; - dns_db_t *db = NULL; - - if (rbtdbiter->tree_locked == isc_rwlocktype_read) { - TREE_UNLOCK(&rbtdb->tree_lock, &rbtdbiter->tree_locked); - } - INSIST(rbtdbiter->tree_locked == isc_rwlocktype_none); - - dereference_iter_node(rbtdbiter DNS__DB_FLARG_PASS); - - dns_db_attach(rbtdbiter->common.db, &db); - dns_db_detach(&rbtdbiter->common.db); - - dns_rbtnodechain_reset(&rbtdbiter->chain); - dns_rbtnodechain_reset(&rbtdbiter->nsec3chain); - isc_mem_put(db->mctx, rbtdbiter, sizeof(*rbtdbiter)); - dns_db_detach(&db); - - *iteratorp = NULL; -} - -static isc_result_t -dbiterator_first(dns_dbiterator_t *iterator DNS__DB_FLARG) { - isc_result_t result; - rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator; - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db; - dns_name_t *name = NULL, *origin = NULL; - - if (rbtdbiter->result != ISC_R_SUCCESS && - rbtdbiter->result != ISC_R_NOTFOUND && - rbtdbiter->result != DNS_R_PARTIALMATCH && - rbtdbiter->result != ISC_R_NOMORE) - { - return (rbtdbiter->result); - } - - if (rbtdbiter->paused) { - resume_iteration(rbtdbiter); - } - - dereference_iter_node(rbtdbiter DNS__DB_FLARG_PASS); - - name = dns_fixedname_name(&rbtdbiter->name); - origin = dns_fixedname_name(&rbtdbiter->origin); - dns_rbtnodechain_reset(&rbtdbiter->chain); - dns_rbtnodechain_reset(&rbtdbiter->nsec3chain); - - switch (rbtdbiter->nsec3mode) { - case nsec3only: - rbtdbiter->current = &rbtdbiter->nsec3chain; - result = dns_rbtnodechain_first(rbtdbiter->current, - rbtdb->nsec3, name, origin); - break; - case nonsec3: - rbtdbiter->current = &rbtdbiter->chain; - result = dns_rbtnodechain_first(rbtdbiter->current, rbtdb->tree, - name, origin); - break; - case full: - rbtdbiter->current = &rbtdbiter->chain; - result = dns_rbtnodechain_first(rbtdbiter->current, rbtdb->tree, - name, origin); - if (result == ISC_R_NOTFOUND) { - rbtdbiter->current = &rbtdbiter->nsec3chain; - result = dns_rbtnodechain_first( - rbtdbiter->current, rbtdb->nsec3, name, origin); - } - break; - default: - UNREACHABLE(); - } - - if (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) { - result = dns_rbtnodechain_current(rbtdbiter->current, NULL, - NULL, &rbtdbiter->node); - - /* If we're in the NSEC3 tree, skip the origin */ - if (RBTDBITER_NSEC3_ORIGIN_NODE(rbtdb, rbtdbiter)) { - rbtdbiter->node = NULL; - result = dns_rbtnodechain_next(rbtdbiter->current, name, - origin); - if (result == ISC_R_SUCCESS || - result == DNS_R_NEWORIGIN) - { - result = dns_rbtnodechain_current( - rbtdbiter->current, NULL, NULL, - &rbtdbiter->node); - } - } - if (result == ISC_R_SUCCESS) { - rbtdbiter->new_origin = true; - reference_iter_node(rbtdbiter DNS__DB_FLARG_PASS); - } - } else { - INSIST(result == ISC_R_NOTFOUND); - result = ISC_R_NOMORE; /* The tree is empty. */ - } - - rbtdbiter->result = result; - - if (result != ISC_R_SUCCESS) { - ENSURE(!rbtdbiter->paused); - } - - return (result); -} - -static isc_result_t -dbiterator_last(dns_dbiterator_t *iterator DNS__DB_FLARG) { - isc_result_t result; - rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator; - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db; - dns_name_t *name = NULL, *origin = NULL; - - if (rbtdbiter->result != ISC_R_SUCCESS && - rbtdbiter->result != ISC_R_NOTFOUND && - rbtdbiter->result != DNS_R_PARTIALMATCH && - rbtdbiter->result != ISC_R_NOMORE) - { - return (rbtdbiter->result); - } - - if (rbtdbiter->paused) { - resume_iteration(rbtdbiter); - } - - dereference_iter_node(rbtdbiter DNS__DB_FLARG_PASS); - - name = dns_fixedname_name(&rbtdbiter->name); - origin = dns_fixedname_name(&rbtdbiter->origin); - dns_rbtnodechain_reset(&rbtdbiter->chain); - dns_rbtnodechain_reset(&rbtdbiter->nsec3chain); - - switch (rbtdbiter->nsec3mode) { - case nsec3only: - rbtdbiter->current = &rbtdbiter->nsec3chain; - result = dns_rbtnodechain_last(rbtdbiter->current, rbtdb->nsec3, - name, origin); - break; - case nonsec3: - rbtdbiter->current = &rbtdbiter->chain; - result = dns_rbtnodechain_last(rbtdbiter->current, rbtdb->tree, - name, origin); - break; - case full: - rbtdbiter->current = &rbtdbiter->nsec3chain; - result = dns_rbtnodechain_last(rbtdbiter->current, rbtdb->nsec3, - name, origin); - if (result == ISC_R_NOTFOUND) { - rbtdbiter->current = &rbtdbiter->chain; - result = dns_rbtnodechain_last( - rbtdbiter->current, rbtdb->tree, name, origin); - } - break; - default: - UNREACHABLE(); - } - - if (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) { - result = dns_rbtnodechain_current(rbtdbiter->current, NULL, - NULL, &rbtdbiter->node); - if (RBTDBITER_NSEC3_ORIGIN_NODE(rbtdb, rbtdbiter)) { - /* - * NSEC3 tree only has an origin node. - */ - rbtdbiter->node = NULL; - switch (rbtdbiter->nsec3mode) { - case nsec3only: - result = ISC_R_NOMORE; - break; - case nonsec3: - case full: - rbtdbiter->current = &rbtdbiter->chain; - result = dns_rbtnodechain_last( - rbtdbiter->current, rbtdb->tree, name, - origin); - if (result == ISC_R_SUCCESS || - result == DNS_R_NEWORIGIN) - { - result = dns_rbtnodechain_current( - rbtdbiter->current, NULL, NULL, - &rbtdbiter->node); - } - break; - default: - UNREACHABLE(); - } - } - if (result == ISC_R_SUCCESS) { - rbtdbiter->new_origin = true; - reference_iter_node(rbtdbiter DNS__DB_FLARG_PASS); - } - } else { - INSIST(result == ISC_R_NOTFOUND); - result = ISC_R_NOMORE; /* The tree is empty. */ - } - - rbtdbiter->result = result; - - return (result); -} - -static isc_result_t -dbiterator_seek(dns_dbiterator_t *iterator, - const dns_name_t *name DNS__DB_FLARG) { - isc_result_t result, tresult; - rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator; - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db; - dns_name_t *iname = NULL, *origin = NULL; - - if (rbtdbiter->result != ISC_R_SUCCESS && - rbtdbiter->result != ISC_R_NOTFOUND && - rbtdbiter->result != DNS_R_PARTIALMATCH && - rbtdbiter->result != ISC_R_NOMORE) - { - return (rbtdbiter->result); - } - - if (rbtdbiter->paused) { - resume_iteration(rbtdbiter); - } - - dereference_iter_node(rbtdbiter DNS__DB_FLARG_PASS); - - iname = dns_fixedname_name(&rbtdbiter->name); - origin = dns_fixedname_name(&rbtdbiter->origin); - dns_rbtnodechain_reset(&rbtdbiter->chain); - dns_rbtnodechain_reset(&rbtdbiter->nsec3chain); - - switch (rbtdbiter->nsec3mode) { - case nsec3only: - rbtdbiter->current = &rbtdbiter->nsec3chain; - result = dns_rbt_findnode(rbtdb->nsec3, name, NULL, - &rbtdbiter->node, rbtdbiter->current, - DNS_RBTFIND_EMPTYDATA, NULL, NULL); - break; - case nonsec3: - rbtdbiter->current = &rbtdbiter->chain; - result = dns_rbt_findnode(rbtdb->tree, name, NULL, - &rbtdbiter->node, rbtdbiter->current, - DNS_RBTFIND_EMPTYDATA, NULL, NULL); - break; - case full: - /* - * Stay on main chain if not found on either chain. - */ - rbtdbiter->current = &rbtdbiter->chain; - result = dns_rbt_findnode(rbtdb->tree, name, NULL, - &rbtdbiter->node, rbtdbiter->current, - DNS_RBTFIND_EMPTYDATA, NULL, NULL); - if (result == DNS_R_PARTIALMATCH) { - dns_rbtnode_t *node = NULL; - tresult = dns_rbt_findnode( - rbtdb->nsec3, name, NULL, &node, - &rbtdbiter->nsec3chain, DNS_RBTFIND_EMPTYDATA, - NULL, NULL); - if (tresult == ISC_R_SUCCESS) { - rbtdbiter->node = node; - rbtdbiter->current = &rbtdbiter->nsec3chain; - result = tresult; - } - } - break; - default: - UNREACHABLE(); - } - - if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) { - tresult = dns_rbtnodechain_current(rbtdbiter->current, iname, - origin, NULL); - if (tresult == ISC_R_SUCCESS) { - rbtdbiter->new_origin = true; - reference_iter_node(rbtdbiter DNS__DB_FLARG_PASS); - } else { - result = tresult; - rbtdbiter->node = NULL; - } - } else { - rbtdbiter->node = NULL; - } - - rbtdbiter->result = (result == DNS_R_PARTIALMATCH) ? ISC_R_SUCCESS - : result; - - return (result); -} - -static isc_result_t -dbiterator_prev(dns_dbiterator_t *iterator DNS__DB_FLARG) { - isc_result_t result; - rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator; - dns_name_t *name = NULL, *origin = NULL; - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db; - - REQUIRE(rbtdbiter->node != NULL); - - if (rbtdbiter->result != ISC_R_SUCCESS) { - return (rbtdbiter->result); - } - - if (rbtdbiter->paused) { - resume_iteration(rbtdbiter); - } - - dereference_iter_node(rbtdbiter DNS__DB_FLARG_PASS); - - name = dns_fixedname_name(&rbtdbiter->name); - origin = dns_fixedname_name(&rbtdbiter->origin); - result = dns_rbtnodechain_prev(rbtdbiter->current, name, origin); - if (rbtdbiter->current == &rbtdbiter->nsec3chain && - (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN)) - { - /* - * If we're in the NSEC3 tree, it's empty or we've - * reached the origin, then we're done with it. - */ - result = dns_rbtnodechain_current(rbtdbiter->current, NULL, - NULL, &rbtdbiter->node); - if (result == ISC_R_NOTFOUND || - RBTDBITER_NSEC3_ORIGIN_NODE(rbtdb, rbtdbiter)) - { - rbtdbiter->node = NULL; - result = ISC_R_NOMORE; - } - } - if (result == ISC_R_NOMORE && rbtdbiter->nsec3mode != nsec3only && - &rbtdbiter->nsec3chain == rbtdbiter->current) - { - rbtdbiter->current = &rbtdbiter->chain; - dns_rbtnodechain_reset(rbtdbiter->current); - result = dns_rbtnodechain_last(rbtdbiter->current, rbtdb->tree, - name, origin); - if (result == ISC_R_NOTFOUND) { - result = ISC_R_NOMORE; - } - } - - if (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) { - rbtdbiter->new_origin = (result == DNS_R_NEWORIGIN); - result = dns_rbtnodechain_current(rbtdbiter->current, NULL, - NULL, &rbtdbiter->node); - } - - if (result == ISC_R_SUCCESS) { - reference_iter_node(rbtdbiter DNS__DB_FLARG_PASS); - } - - rbtdbiter->result = result; - - return (result); -} - -static isc_result_t -dbiterator_next(dns_dbiterator_t *iterator DNS__DB_FLARG) { - isc_result_t result; - rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator; - dns_name_t *name = NULL, *origin = NULL; - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db; - - REQUIRE(rbtdbiter->node != NULL); - - if (rbtdbiter->result != ISC_R_SUCCESS) { - return (rbtdbiter->result); - } - - if (rbtdbiter->paused) { - resume_iteration(rbtdbiter); - } - - name = dns_fixedname_name(&rbtdbiter->name); - origin = dns_fixedname_name(&rbtdbiter->origin); - result = dns_rbtnodechain_next(rbtdbiter->current, name, origin); - if (result == ISC_R_NOMORE && rbtdbiter->nsec3mode != nonsec3 && - &rbtdbiter->chain == rbtdbiter->current) - { - rbtdbiter->current = &rbtdbiter->nsec3chain; - dns_rbtnodechain_reset(rbtdbiter->current); - result = dns_rbtnodechain_first(rbtdbiter->current, - rbtdb->nsec3, name, origin); - if (result == ISC_R_NOTFOUND) { - result = ISC_R_NOMORE; - } - } - - dereference_iter_node(rbtdbiter DNS__DB_FLARG_PASS); - - if (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) { - /* - * If we've just started the NSEC3 tree, - * skip over the origin. - */ - rbtdbiter->new_origin = (result == DNS_R_NEWORIGIN); - result = dns_rbtnodechain_current(rbtdbiter->current, NULL, - NULL, &rbtdbiter->node); - if (RBTDBITER_NSEC3_ORIGIN_NODE(rbtdb, rbtdbiter)) { - rbtdbiter->node = NULL; - result = dns_rbtnodechain_next(rbtdbiter->current, name, - origin); - if (result == ISC_R_SUCCESS || - result == DNS_R_NEWORIGIN) - { - result = dns_rbtnodechain_current( - rbtdbiter->current, NULL, NULL, - &rbtdbiter->node); - } - } - } - if (result == ISC_R_SUCCESS) { - reference_iter_node(rbtdbiter DNS__DB_FLARG_PASS); - } - - rbtdbiter->result = result; - - return (result); -} - -static isc_result_t -dbiterator_current(dns_dbiterator_t *iterator, dns_dbnode_t **nodep, - dns_name_t *name DNS__DB_FLARG) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db; - rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator; - dns_rbtnode_t *node = rbtdbiter->node; - isc_result_t result; - dns_name_t *nodename = dns_fixedname_name(&rbtdbiter->name); - dns_name_t *origin = dns_fixedname_name(&rbtdbiter->origin); - - REQUIRE(rbtdbiter->result == ISC_R_SUCCESS); - REQUIRE(rbtdbiter->node != NULL); - - if (rbtdbiter->paused) { - resume_iteration(rbtdbiter); - } - - if (name != NULL) { - if (rbtdbiter->common.relative_names) { - origin = NULL; - } - result = dns_name_concatenate(nodename, origin, name, NULL); - if (result != ISC_R_SUCCESS) { - return (result); - } - if (rbtdbiter->common.relative_names && rbtdbiter->new_origin) { - result = DNS_R_NEWORIGIN; - } - } else { - result = ISC_R_SUCCESS; - } - - dns__rbtdb_newref(rbtdb, node, isc_rwlocktype_none DNS__DB_FLARG_PASS); - - *nodep = (dns_dbnode_t *)rbtdbiter->node; - - return (result); -} - -static isc_result_t -dbiterator_pause(dns_dbiterator_t *iterator) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db; - rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator; - - if (rbtdbiter->result != ISC_R_SUCCESS && - rbtdbiter->result != ISC_R_NOTFOUND && - rbtdbiter->result != DNS_R_PARTIALMATCH && - rbtdbiter->result != ISC_R_NOMORE) - { - return (rbtdbiter->result); - } - - if (rbtdbiter->paused) { - return (ISC_R_SUCCESS); - } - - rbtdbiter->paused = true; - - if (rbtdbiter->tree_locked == isc_rwlocktype_read) { - TREE_UNLOCK(&rbtdb->tree_lock, &rbtdbiter->tree_locked); - } - INSIST(rbtdbiter->tree_locked == isc_rwlocktype_none); - - return (ISC_R_SUCCESS); -} - -static isc_result_t -dbiterator_origin(dns_dbiterator_t *iterator, dns_name_t *name) { - rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator; - dns_name_t *origin = dns_fixedname_name(&rbtdbiter->origin); - - if (rbtdbiter->result != ISC_R_SUCCESS) { - return (rbtdbiter->result); - } - - dns_name_copy(origin, name); - return (ISC_R_SUCCESS); -} - -static void -freeglue(isc_mem_t *mctx, dns_glue_t *glue) { - while (glue != NULL) { - dns_glue_t *next = glue->next; - - if (dns_rdataset_isassociated(&glue->rdataset_a)) { - dns_rdataset_disassociate(&glue->rdataset_a); - } - if (dns_rdataset_isassociated(&glue->sigrdataset_a)) { - dns_rdataset_disassociate(&glue->sigrdataset_a); - } - - if (dns_rdataset_isassociated(&glue->rdataset_aaaa)) { - dns_rdataset_disassociate(&glue->rdataset_aaaa); - } - if (dns_rdataset_isassociated(&glue->sigrdataset_aaaa)) { - dns_rdataset_disassociate(&glue->sigrdataset_aaaa); - } - - dns_rdataset_invalidate(&glue->rdataset_a); - dns_rdataset_invalidate(&glue->sigrdataset_a); - dns_rdataset_invalidate(&glue->rdataset_aaaa); - dns_rdataset_invalidate(&glue->sigrdataset_aaaa); - - isc_mem_put(mctx, glue, sizeof(*glue)); - - glue = next; - } -} - -void -dns__rbtdb_free_gluenode_rcu(struct rcu_head *rcu_head) { - dns_gluenode_t *gluenode = caa_container_of(rcu_head, dns_gluenode_t, - rcu_head); - - freeglue(gluenode->mctx, gluenode->glue); - - dns_db_detachnode(gluenode->db, (dns_dbnode_t **)&gluenode->node); - - isc_mem_putanddetach(&gluenode->mctx, gluenode, sizeof(*gluenode)); -} - -void -dns__rbtdb_free_gluenode(dns_gluenode_t *gluenode) { - call_rcu(&gluenode->rcu_head, dns__rbtdb_free_gluenode_rcu); -} - -static void -free_gluetable(struct cds_lfht *glue_table) { - struct cds_lfht_iter iter; - dns_gluenode_t *gluenode = NULL; - - rcu_read_lock(); - cds_lfht_for_each_entry(glue_table, &iter, gluenode, ht_node) { - INSIST(!cds_lfht_del(glue_table, &gluenode->ht_node)); - dns__rbtdb_free_gluenode(gluenode); - } - rcu_read_unlock(); - - cds_lfht_destroy(glue_table, NULL); -} - -void -dns__rbtdb_deletedata(dns_db_t *db ISC_ATTR_UNUSED, - dns_dbnode_t *node ISC_ATTR_UNUSED, void *data) { - dns_slabheader_t *header = data; - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)header->db; - - if (header->heap != NULL && header->heap_index != 0) { - isc_heap_delete(header->heap, header->heap_index); - } - - if (IS_CACHE(rbtdb)) { - update_rrsetstats(rbtdb->rrsetstats, header->type, - atomic_load_acquire(&header->attributes), - false); - - if (ISC_LINK_LINKED(header, link)) { - int idx = RBTDB_HEADERNODE(header)->locknum; - INSIST(IS_CACHE(rbtdb)); - ISC_LIST_UNLINK(rbtdb->lru[idx], header, link); - } - - if (header->noqname != NULL) { - dns_slabheader_freeproof(db->mctx, &header->noqname); - } - if (header->closest != NULL) { - dns_slabheader_freeproof(db->mctx, &header->closest); - } - } -} - -/* - * Caller must be holding the node write lock. - */ -static void -expire_ttl_headers(dns_rbtdb_t *rbtdb, unsigned int locknum, - isc_rwlocktype_t *tlocktypep, isc_stdtime_t now, - bool cache_is_overmem DNS__DB_FLARG) { - isc_heap_t *heap = rbtdb->heaps[locknum]; - - for (size_t i = 0; i < DNS_RBTDB_EXPIRE_TTL_COUNT; i++) { - dns_slabheader_t *header = isc_heap_element(heap, 1); - - if (header == NULL) { - /* No headers left on this TTL heap; exit cleaning */ - return; - } - - dns_ttl_t ttl = header->ttl; - - if (!cache_is_overmem) { - /* Only account for stale TTL if cache is not overmem */ - ttl += STALE_TTL(header, rbtdb); - } - - if (ttl >= now - RBTDB_VIRTUAL) { - /* - * The header at the top of this TTL heap is not yet - * eligible for expiry, so none of the other headers on - * the same heap can be eligible for expiry, either; - * exit cleaning. - */ - return; - } - - dns__cacherbt_expireheader(header, tlocktypep, - dns_expire_ttl DNS__DB_FLARG_PASS); - } -} - -void -dns__rbtdb_setmaxrrperset(dns_db_t *db, uint32_t value) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - - REQUIRE(VALID_RBTDB(rbtdb)); - - rbtdb->maxrrperset = value; -} - -void -dns__rbtdb_setmaxtypepername(dns_db_t *db, uint32_t maxtypepername) { - dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; - - REQUIRE(VALID_RBTDB(rbtdb)); - - rbtdb->maxtypepername = maxtypepername; -} diff --git a/lib/dns/rbtdb_p.h b/lib/dns/rbtdb_p.h deleted file mode 100644 index 6552ee9143..0000000000 --- a/lib/dns/rbtdb_p.h +++ /dev/null @@ -1,511 +0,0 @@ -/* - * Copyright (C) Internet Systems Consortium, Inc. ("ISC") - * - * SPDX-License-Identifier: MPL-2.0 - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, you can obtain one at https://mozilla.org/MPL/2.0/. - * - * See the COPYRIGHT file distributed with this work for additional - * information regarding copyright ownership. - */ - -#pragma once - -#include -#include -#include -#include - -#include -#include -#include - -#include "db_p.h" /* for db_nodelock_t */ - -/*% - * Note that "impmagic" is not the first four bytes of the struct, so - * ISC_MAGIC_VALID cannot be used. - */ -#define RBTDB_MAGIC ISC_MAGIC('R', 'B', 'D', '4') -#define VALID_RBTDB(rbtdb) \ - ((rbtdb) != NULL && (rbtdb)->common.impmagic == RBTDB_MAGIC) - -#define RBTDB_HEADERNODE(h) ((dns_rbtnode_t *)((h)->node)) - -/* - * Allow clients with a virtual time of up to 5 minutes in the past to see - * records that would have otherwise have expired. - */ -#define RBTDB_VIRTUAL 300 - -/***** -***** Module Info -*****/ - -/*! \file - * \brief - * DNS Red-Black Tree DB Implementation - */ - -ISC_LANG_BEGINDECLS - -typedef struct rbtdb_changed { - dns_rbtnode_t *node; - bool dirty; - ISC_LINK(struct rbtdb_changed) link; -} rbtdb_changed_t; - -typedef ISC_LIST(rbtdb_changed_t) rbtdb_changedlist_t; - -struct dns_rbtdb_version { - /* Not locked */ - uint32_t serial; - dns_rbtdb_t *rbtdb; - /* - * Protected in the refcount routines. - * XXXJT: should we change the lock policy based on the refcount - * performance? - */ - isc_refcount_t references; - /* Locked by database lock. */ - bool writer; - bool commit_ok; - rbtdb_changedlist_t changed_list; - dns_slabheaderlist_t resigned_list; - ISC_LINK(dns_rbtdb_version_t) link; - bool secure; - bool havensec3; - /* NSEC3 parameters */ - dns_hash_t hash; - uint8_t flags; - uint16_t iterations; - uint8_t salt_length; - unsigned char salt[DNS_NSEC3_SALTSIZE]; - - /* - * records and xfrsize are covered by rwlock. - */ - isc_rwlock_t rwlock; - uint64_t records; - uint64_t xfrsize; - - struct cds_lfht *glue_table; -}; - -typedef ISC_LIST(dns_rbtdb_version_t) rbtdb_versionlist_t; - -struct dns_rbtdb { - /* Unlocked. */ - dns_db_t common; - /* Locks the data in this struct */ - isc_rwlock_t lock; - /* Locks the tree structure (prevents nodes appearing/disappearing) */ - isc_rwlock_t tree_lock; - /* Locks for individual tree nodes */ - unsigned int node_lock_count; - db_nodelock_t *node_locks; - dns_rbtnode_t *origin_node; - dns_rbtnode_t *nsec3_origin_node; - dns_stats_t *rrsetstats; /* cache DB only */ - isc_stats_t *cachestats; /* cache DB only */ - isc_stats_t *gluecachestats; /* zone DB only */ - /* Locked by lock. */ - unsigned int active; - unsigned int attributes; - uint32_t current_serial; - uint32_t least_serial; - uint32_t next_serial; - uint32_t maxrrperset; - uint32_t maxtypepername; - dns_rbtdb_version_t *current_version; - dns_rbtdb_version_t *future_version; - rbtdb_versionlist_t open_versions; - isc_loop_t *loop; - dns_dbnode_t *soanode; - dns_dbnode_t *nsnode; - - /* - * The time after a failed lookup, where stale answers from cache - * may be used directly in a DNS response without attempting a - * new iterative lookup. - */ - uint32_t serve_stale_refresh; - - /* - * This is an array of linked lists used to implement the LRU cache. - * There will be node_lock_count linked lists here. Nodes in bucket 1 - * will be placed on the linked list lru[1]. - */ - dns_slabheaderlist_t *lru; - - /* - * Start point % node_lock_count for next LRU cleanup. - */ - atomic_uint lru_sweep; - - /* - * When performing LRU cleaning limit cleaning to headers that were - * last used at or before this. - */ - _Atomic(isc_stdtime_t) last_used; - - /*% - * Temporary storage for stale cache nodes and dynamically deleted - * nodes that await being cleaned up. - */ - dns_rbtnodelist_t *deadnodes; - - /* - * Heaps. These are used for TTL based expiry in a cache, - * or for zone resigning in a zone DB. hmctx is the memory - * context to use for the heap (which differs from the main - * database memory context in the case of a cache). - */ - isc_mem_t *hmctx; - isc_heap_t **heaps; - isc_heapcompare_t sooner; - - /* Locked by tree_lock. */ - dns_rbt_t *tree; - dns_rbt_t *nsec; - dns_rbt_t *nsec3; - - /* Unlocked */ - unsigned int quantum; -}; - -/*% - * Search Context - */ -typedef struct { - dns_rbtdb_t *rbtdb; - dns_rbtdb_version_t *rbtversion; - uint32_t serial; - unsigned int options; - dns_rbtnodechain_t chain; - bool copy_name; - bool need_cleanup; - bool wild; - dns_rbtnode_t *zonecut; - dns_slabheader_t *zonecut_header; - dns_slabheader_t *zonecut_sigheader; - dns_fixedname_t zonecut_name; - isc_stdtime_t now; -} rbtdb_search_t; - -/*% - * Load Context - */ -typedef struct { - dns_db_t *db; - isc_stdtime_t now; -} rbtdb_load_t; - -/*% - * Prune context - */ -typedef struct { - dns_db_t *db; - dns_rbtnode_t *node; -} rbtdb_prune_t; - -extern dns_dbmethods_t dns__rbtdb_zonemethods; -extern dns_dbmethods_t dns__rbtdb_cachemethods; - -typedef struct dns_gluenode_t { - isc_mem_t *mctx; - - struct dns_glue *glue; - - dns_db_t *db; - dns_rbtnode_t *node; - - struct cds_lfht_node ht_node; - struct rcu_head rcu_head; -} dns_gluenode_t; - -/* - * Common DB implementation methods shared by both cache and zone RBT - * databases: - */ - -isc_result_t -dns__rbtdb_create(isc_mem_t *mctx, const dns_name_t *base, dns_dbtype_t type, - dns_rdataclass_t rdclass, unsigned int argc, char *argv[], - void *driverarg, dns_db_t **dbp); -/*%< - * Create a new database of type "rbt". Called via dns_db_create(); - * see documentation for that function for more details. - * - * If argv[0] is set, it points to a valid memory context to be used for - * allocation of heap memory. Generally this is used for cache databases - * only. - * - * Requires: - * - * \li argc == 0 or argv[0] is a valid memory context. - */ - -void -dns__rbtdb_destroy(dns_db_t *arg); -/*%< - * Implement dns_db_destroy() for RBT databases, see documentation - * for that function for more details. - */ - -void -dns__rbtdb_currentversion(dns_db_t *db, dns_dbversion_t **versionp); -isc_result_t -dns__rbtdb_newversion(dns_db_t *db, dns_dbversion_t **versionp); -void -dns__rbtdb_attachversion(dns_db_t *db, dns_dbversion_t *source, - dns_dbversion_t **targetp); -void -dns__rbtdb_closeversion(dns_db_t *db, dns_dbversion_t **versionp, - bool commit DNS__DB_FLARG); -/*%< - * Implement the dns_db_currentversion(), _newversion(), - * _attachversion() and _closeversion() methods for RBT databases; - * see documentation of those functions for more details. - */ - -isc_result_t -dns__rbtdb_findnode(dns_db_t *db, const dns_name_t *name, bool create, - dns_dbnode_t **nodep DNS__DB_FLARG); -isc_result_t -dns__rbtdb_findnodeintree(dns_rbtdb_t *rbtdb, dns_rbt_t *tree, - const dns_name_t *name, bool create, - dns_dbnode_t **nodep DNS__DB_FLARG); -/*%< - * Implement the dns_db_findnode() and _findnodeintree() methods for - * RBT databases; see documentation of those functions for more details. - */ - -void -dns__rbtdb_attachnode(dns_db_t *db, dns_dbnode_t *source, - dns_dbnode_t **targetp DNS__DB_FLARG); -void -dns__rbtdb_detachnode(dns_db_t *db, dns_dbnode_t **targetp DNS__DB_FLARG); -/*%< - * Implement the dns_db_attachnode() and _detachnode() methods for - * RBT databases; see documentation of those functions for more details. - */ - -isc_result_t -dns__rbtdb_createiterator(dns_db_t *db, unsigned int options, - dns_dbiterator_t **iteratorp); -/*%< - * Implement dns_db_createiterator() for RBT databases; see documentation of - * that function for more details. - */ - -isc_result_t -dns__rbtdb_allrdatasets(dns_db_t *db, dns_dbnode_t *node, - dns_dbversion_t *version, unsigned int options, - isc_stdtime_t now, - dns_rdatasetiter_t **iteratorp DNS__DB_FLARG); -/*%< - * Implement dns_db_allrdatasets() for RBT databases; see documentation of - * that function for more details. - */ -isc_result_t -dns__rbtdb_addrdataset(dns_db_t *db, dns_dbnode_t *node, - dns_dbversion_t *version, isc_stdtime_t now, - dns_rdataset_t *rdataset, unsigned int options, - dns_rdataset_t *addedrdataset DNS__DB_FLARG); -isc_result_t -dns__rbtdb_subtractrdataset(dns_db_t *db, dns_dbnode_t *node, - dns_dbversion_t *version, dns_rdataset_t *rdataset, - unsigned int options, - dns_rdataset_t *newrdataset DNS__DB_FLARG); -isc_result_t -dns__rbtdb_deleterdataset(dns_db_t *db, dns_dbnode_t *node, - dns_dbversion_t *version, dns_rdatatype_t type, - dns_rdatatype_t covers DNS__DB_FLARG); -/*%< - * Implement the dns_db_addrdataset(), _subtractrdataset() and - * _deleterdataset() methods for RBT databases; see documentation of - * those functions for more details. - */ - -unsigned int -dns__rbtdb_nodecount(dns_db_t *db, dns_dbtree_t tree); -/*%< - * Implement dns_db_nodecount() for RBT databases; see documentation of - * that function for more details. - */ - -void -dns__rbtdb_setloop(dns_db_t *db, isc_loop_t *loop); -/*%< - * Implement dns_db_setloop() for RBT databases; see documentation of - * that function for more details. - */ - -isc_result_t -dns__rbtdb_getoriginnode(dns_db_t *db, dns_dbnode_t **nodep DNS__DB_FLARG); -/*%< - * Implement dns_db_getoriginnode() for RBT databases; see documentation of - * that function for more details. - */ - -void -dns__rbtdb_deletedata(dns_db_t *db ISC_ATTR_UNUSED, - dns_dbnode_t *node ISC_ATTR_UNUSED, void *data); -/*%< - * Implement dns_db_deletedata() for RBT databases; see documentation of - * that function for more details. - */ - -void -dns__rbtdb_locknode(dns_db_t *db, dns_dbnode_t *node, isc_rwlocktype_t type); -void -dns__rbtdb_unlocknode(dns_db_t *db, dns_dbnode_t *node, isc_rwlocktype_t type); -/*%< - * Implement the dns_db_locknode() and _unlocknode() methods for - * RBT databases; see documentation of those functions for more details. - */ - -/*% - * Functions used for the RBT implementation which are defined and - * used in rbtdb.c but may also be called from rbt-zonedb.c or - * rbt-cachedb.c: - */ -void -dns__rbtdb_bindrdataset(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, - dns_slabheader_t *header, isc_stdtime_t now, - isc_rwlocktype_t locktype, - dns_rdataset_t *rdataset DNS__DB_FLARG); - -isc_result_t -dns__rbtdb_nodefullname(dns_db_t *db, dns_dbnode_t *node, dns_name_t *name); - -void -dns__rbtdb_free_gluenode_rcu(struct rcu_head *rcu_head); -void -dns__rbtdb_free_gluenode(dns_gluenode_t *gluenode); - -void -dns__rbtdb_newref(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, - isc_rwlocktype_t locktype DNS__DB_FLARG); -/*%< - * Increment the reference counter to a node in an RBT database. - * If the caller holds a node lock then its lock type is specified - * as 'locktype'. If the node is write-locked, then the node can - * be removed from the dead nodes list. If not, the list can be - * cleaned up later. - */ - -bool -dns__rbtdb_decref(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, - uint32_t least_serial, isc_rwlocktype_t *nlocktypep, - isc_rwlocktype_t *tlocktypep, bool tryupgrade, - bool pruning DNS__DB_FLARG); -/*%< - * Decrement the reference counter to a node in an RBT database. - * 'nlocktypep' and 'tlocktypep' are pointers to the current status - * of the node lock and tree lock. - * - * If references go to 0, the node will be cleaned up, which may - * necessitate upgrading the locks. - */ - -isc_result_t -dns__rbtdb_add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, - const dns_name_t *nodename, dns_rbtdb_version_t *rbtversion, - dns_slabheader_t *newheader, unsigned int options, bool loading, - dns_rdataset_t *addedrdataset, isc_stdtime_t now DNS__DB_FLARG); -/*%< - * Add a slab header 'newheader' to a node in an RBT database. - * The caller must have the node write-locked. - */ - -void -dns__rbtdb_setsecure(dns_db_t *db, dns_rbtdb_version_t *version, - dns_dbnode_t *origin); -/*%< - * Update the secure status for an RBT database version 'version'. - * The version will be marked secure if it is fully signed and - * and contains a complete NSEC/NSEC3 chain. - */ - -void -dns__rbtdb_mark(dns_slabheader_t *header, uint_least16_t flag); -/*%< - * Set attribute 'flag' in a slab header 'header' - for example, - * DNS_SLABHEADERATTR_STALE or DNS_SLABHEADERATTR_ANCIENT - and, - * in a cache database, update the rrset stats accordingly. - */ - -void -dns__rbtdb_setttl(dns_slabheader_t *header, dns_ttl_t newttl); -/*%< - * Set the TTL in a slab header 'header'. In a cache database, - * also update the TTL heap accordingly. - */ - -void -dns__rbtdb_setmaxrrperset(dns_db_t *db, uint32_t maxrrperset); -/*%< - * Set the max RRs per RRset limit. - */ - -void -dns__rbtdb_setmaxtypepername(dns_db_t *db, uint32_t maxtypepername); -/*%< - * Set the max RRs per RRset limit. - */ - -/* - * Functions specific to zone databases that are also called from rbtdb.c. - */ -void -dns__zonerbt_resigninsert(dns_rbtdb_t *rbtdb, int idx, - dns_slabheader_t *newheader); -void -dns__zonerbt_resigndelete(dns_rbtdb_t *rbtdb, dns_rbtdb_version_t *version, - dns_slabheader_t *header DNS__DB_FLARG); -/*%< - * Insert/delete a node from the zone database's resigning heap. - */ - -isc_result_t -dns__zonerbt_wildcardmagic(dns_rbtdb_t *rbtdb, const dns_name_t *name, - bool lock); -/*%< - * Add the necessary magic for the wildcard name 'name' - * to be found in 'rbtdb'. - * - * In order for wildcard matching to work correctly in - * zone_find(), we must ensure that a node for the wildcarding - * level exists in the database, and has its 'find_callback' - * and 'wild' bits set. - * - * E.g. if the wildcard name is "*.sub.example." then we - * must ensure that "sub.example." exists and is marked as - * a wildcard level. - * - * The tree must be write-locked. - */ -isc_result_t -dns__zonerbt_addwildcards(dns_rbtdb_t *rbtdb, const dns_name_t *name, - bool lock); -/*%< - * If 'name' is or contains a wildcard name, create a node for it in the - * database. The tree must be write-locked. - */ - -/* - * Cache-specific functions that are called from rbtdb.c - */ -void -dns__cacherbt_expireheader(dns_slabheader_t *header, - isc_rwlocktype_t *tlocktypep, - dns_expire_t reason DNS__DB_FLARG); -void -dns__cacherbt_overmem(dns_rbtdb_t *rbtdb, dns_slabheader_t *newheader, - isc_rwlocktype_t *tlocktypep DNS__DB_FLARG); - -ISC_LANG_ENDDECLS diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index 9a4443c92e..2189efcbba 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -58,7 +58,6 @@ #include #include #include -#include #include #include #include diff --git a/lib/dns/view.c b/lib/dns/view.c index 2be324156c..a7538bbdbe 100644 --- a/lib/dns/view.c +++ b/lib/dns/view.c @@ -51,7 +51,6 @@ #include #include #include -#include #include #include #include diff --git a/lib/isccfg/check.c b/lib/isccfg/check.c index 432cdb3947..4271801290 100644 --- a/lib/isccfg/check.c +++ b/lib/isccfg/check.c @@ -50,7 +50,6 @@ #include #include #include -#include #include #include #include diff --git a/lib/ns/query.c b/lib/ns/query.c index 6dc2048fc0..b49a853ad5 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -47,7 +47,6 @@ #include #include #include -#include #include #include #include diff --git a/tests/bench/load-names.c b/tests/bench/load-names.c index 4a11375a98..11fd6272d0 100644 --- a/tests/bench/load-names.c +++ b/tests/bench/load-names.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -26,7 +27,6 @@ #include #include -#include #include #include "qp_p.h" @@ -298,78 +298,6 @@ thread_ht(void *arg0) { return (NULL); } -/* - * rbt - */ - -static void * -new_rbt(isc_mem_t *mem) { - dns_rbt_t *rbt = NULL; - (void)dns_rbt_create(mem, NULL, NULL, &rbt); - return (rbt); -} - -static isc_result_t -add_rbt(void *rbt, size_t count) { - isc_result_t result; - dns_rbtnode_t *node = NULL; - - result = dns_rbt_addnode(rbt, &item[count].fixed.name, &node); - if (result == ISC_R_SUCCESS || - (result == ISC_R_EXISTS && node->data == NULL)) - { - node->data = &item[count]; - result = ISC_R_SUCCESS; - } - - return (result); -} - -static isc_result_t -get_rbt(void *rbt, size_t count, void **pval) { - isc_result_t result; - dns_rbtnode_t *node = NULL; - - result = dns_rbt_findnode(rbt, &item[count].fixed.name, NULL, &node, - NULL, 0, NULL, NULL); - if (result == ISC_R_SUCCESS) { - *pval = node->data; - } - return (result); -} - -static void * -thread_rbt(void *arg0) { - struct thread_s *arg = arg0; - - isc_barrier_wait(&barrier); - - isc_time_t t0 = isc_time_now_hires(); - WRLOCK(&rwl); - for (size_t n = arg->start; n < arg->end; n++) { - isc_result_t result = add_rbt(arg->map, n); - CHECK(n, result); - } - WRUNLOCK(&rwl); - - isc_time_t t1 = isc_time_now_hires(); - RDLOCK(&rwl); - for (size_t n = arg->start; n < arg->end; n++) { - void *pval = NULL; - isc_result_t result = get_rbt(arg->map, n, &pval); - CHECK(n, result); - assert(pval == &item[n]); - } - RDUNLOCK(&rwl); - - isc_time_t t2 = isc_time_now_hires(); - - arg->d0 = isc_time_microdiff(&t1, &t0); - arg->d1 = isc_time_microdiff(&t2, &t1); - - return (NULL); -} - /* * qp */ @@ -463,7 +391,6 @@ static struct fun fun_list[] = { { "lfht", new_lfht, thread_lfht }, { "ht", new_ht, thread_ht }, { "hashmap", new_hashmap, thread_hashmap }, - { "rbt", new_rbt, thread_rbt }, { "qp", new_qp, thread_qp }, { "qp+nosqz", new_qp, thread_qp_nosqz }, { "qp+barrier", new_qp, thread_qp_brr }, diff --git a/tests/bench/qp-dump.c b/tests/bench/qp-dump.c index 0142eb3d9b..e0896fb9ff 100644 --- a/tests/bench/qp-dump.c +++ b/tests/bench/qp-dump.c @@ -22,7 +22,6 @@ #include #include -#include #include #include diff --git a/tests/bench/qplookups.c b/tests/bench/qplookups.c index 12a54f2ad4..aa97d7cec1 100644 --- a/tests/bench/qplookups.c +++ b/tests/bench/qplookups.c @@ -23,7 +23,6 @@ #include #include -#include #include #include diff --git a/tests/dns/Makefile.am b/tests/dns/Makefile.am index 70104151cc..579e034135 100644 --- a/tests/dns/Makefile.am +++ b/tests/dns/Makefile.am @@ -37,7 +37,6 @@ check_PROGRAMS = \ qpmulti_test \ qpdb_test \ qpzone_test \ - rbt_test \ rdata_test \ rdataset_test \ rdatasetstats_test \ diff --git a/tests/dns/qpdb_test.c b/tests/dns/qpdb_test.c index de11982c80..6adac9faca 100644 --- a/tests/dns/qpdb_test.c +++ b/tests/dns/qpdb_test.c @@ -25,7 +25,6 @@ #include -#include #include #include #include diff --git a/tests/dns/rbt_test.c b/tests/dns/rbt_test.c deleted file mode 100644 index 9e1604230e..0000000000 --- a/tests/dns/rbt_test.c +++ /dev/null @@ -1,1222 +0,0 @@ -/* - * Copyright (C) Internet Systems Consortium, Inc. ("ISC") - * - * SPDX-License-Identifier: MPL-2.0 - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, you can obtain one at https://mozilla.org/MPL/2.0/. - * - * See the COPYRIGHT file distributed with this work for additional - * information regarding copyright ownership. - */ - -#include -#include -#include -#include /* IWYU pragma: keep */ -#include -#include -#include -#include -#include -#include -#include -#include - -#define UNIT_TESTING -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include - -typedef struct { - dns_rbt_t *rbt; - dns_rbt_t *rbt_distances; -} test_context_t; - -/* The initial structure of domain tree will be as follows: - * - * . - * | - * b - * / \ - * a d.e.f - * / | \ - * c | g.h - * | | - * w.y i - * / | \ \ - * x | z k - * | | - * p j - * / \ - * o q - */ - -/* The full absolute names of the nodes in the tree (the tree also - * contains "." which is not included in this list). - */ -static const char *const domain_names[] = { - "c.", "b.", "a.", "x.d.e.f.", - "z.d.e.f.", "g.h.", "i.g.h.", "o.w.y.d.e.f.", - "j.z.d.e.f.", "p.w.y.d.e.f.", "q.w.y.d.e.f.", "k.g.h." -}; - -static const size_t domain_names_count = - (sizeof(domain_names) / sizeof(domain_names[0])); - -/* These are set as the node data for the tree used in distances check - * (for the names in domain_names[] above). - */ -static const int node_distances[] = { 3, 1, 2, 2, 2, 3, 1, 2, 1, 1, 2, 2 }; - -/* - * The domain order should be: - * ., a, b, c, d.e.f, x.d.e.f, w.y.d.e.f, o.w.y.d.e.f, p.w.y.d.e.f, - * q.w.y.d.e.f, z.d.e.f, j.z.d.e.f, g.h, i.g.h, k.g.h - * . (no data, can't be found) - * | - * b - * / \ - * a d.e.f - * / | \ - * c | g.h - * | | - * w.y i - * / | \ \ - * x | z k - * | | - * p j - * / \ - * o q - */ - -static const char *const ordered_names[] = { - "a.", "b.", "c.", "d.e.f.", - "x.d.e.f.", "w.y.d.e.f.", "o.w.y.d.e.f.", "p.w.y.d.e.f.", - "q.w.y.d.e.f.", "z.d.e.f.", "j.z.d.e.f.", "g.h.", - "i.g.h.", "k.g.h." -}; - -static const size_t ordered_names_count = - (sizeof(ordered_names) / sizeof(*ordered_names)); - -static void -delete_data(void *data, void *arg) { - UNUSED(arg); - - isc_mem_put(mctx, data, sizeof(size_t)); -} - -static test_context_t * -test_context_setup(void) { - test_context_t *ctx; - isc_result_t result; - size_t i; - - ctx = isc_mem_get(mctx, sizeof(*ctx)); - assert_non_null(ctx); - - ctx->rbt = NULL; - result = dns_rbt_create(mctx, delete_data, NULL, &ctx->rbt); - assert_int_equal(result, ISC_R_SUCCESS); - - ctx->rbt_distances = NULL; - result = dns_rbt_create(mctx, delete_data, NULL, &ctx->rbt_distances); - assert_int_equal(result, ISC_R_SUCCESS); - - for (i = 0; i < domain_names_count; i++) { - size_t *n; - dns_fixedname_t fname; - dns_name_t *name = NULL; - dns_rbtnode_t *node = NULL; - - dns_test_namefromstring(domain_names[i], &fname); - - name = dns_fixedname_name(&fname); - - n = isc_mem_get(mctx, sizeof(size_t)); - assert_non_null(n); - *n = i + 1; - result = dns_rbt_addnode(ctx->rbt, name, &node); - assert_int_equal(result, ISC_R_SUCCESS); - node->data = n; - - node = NULL; - n = isc_mem_get(mctx, sizeof(size_t)); - assert_non_null(n); - *n = node_distances[i]; - result = dns_rbt_addnode(ctx->rbt_distances, name, &node); - node->data = n; - assert_int_equal(result, ISC_R_SUCCESS); - } - - return (ctx); -} - -static void -test_context_teardown(test_context_t *ctx) { - dns_rbt_destroy(&ctx->rbt, 0); - dns_rbt_destroy(&ctx->rbt_distances, 0); - - isc_mem_put(mctx, ctx, sizeof(*ctx)); -} - -/* - * Walk the tree and ensure that all the test nodes are present. - */ -static void -check_test_data(dns_rbt_t *rbt) { - dns_fixedname_t fixed; - isc_result_t result; - dns_name_t *foundname; - size_t i; - - foundname = dns_fixedname_initname(&fixed); - - for (i = 0; i < domain_names_count; i++) { - dns_fixedname_t fname; - dns_rbtnode_t *node = NULL; - dns_name_t *name = NULL; - size_t *n = NULL; - - dns_test_namefromstring(domain_names[i], &fname); - - name = dns_fixedname_name(&fname); - n = NULL; - result = dns_rbt_findnode(rbt, name, foundname, &node, NULL, 0, - NULL, NULL); - assert_int_equal(result, ISC_R_SUCCESS); - n = node->data; - assert_int_equal(*n, i + 1); - } -} - -/* Test the creation of an rbt */ -ISC_RUN_TEST_IMPL(rbt_create) { - test_context_t *ctx; - bool tree_ok; - - isc_mem_debugging = ISC_MEM_DEBUGRECORD; - - ctx = test_context_setup(); - - check_test_data(ctx->rbt); - - tree_ok = dns__rbt_checkproperties(ctx->rbt); - assert_true(tree_ok); - - test_context_teardown(ctx); -} - -/* Test dns_rbt_nodecount() on a tree */ -ISC_RUN_TEST_IMPL(rbt_nodecount) { - test_context_t *ctx; - - isc_mem_debugging = ISC_MEM_DEBUGRECORD; - - ctx = test_context_setup(); - - assert_int_equal(15, dns_rbt_nodecount(ctx->rbt)); - - test_context_teardown(ctx); -} - -/* Test dns_rbtnode_get_distance() on a tree */ -ISC_RUN_TEST_IMPL(rbtnode_get_distance) { - isc_result_t result; - test_context_t *ctx; - const char *name_str = "a."; - dns_fixedname_t fname; - dns_name_t *name; - dns_rbtnode_t *node = NULL; - dns_rbtnodechain_t chain; - - isc_mem_debugging = ISC_MEM_DEBUGRECORD; - - ctx = test_context_setup(); - - dns_test_namefromstring(name_str, &fname); - name = dns_fixedname_name(&fname); - - dns_rbtnodechain_init(&chain); - - result = dns_rbt_findnode(ctx->rbt_distances, name, NULL, &node, &chain, - 0, NULL, NULL); - assert_int_equal(result, ISC_R_SUCCESS); - - while (node != NULL) { - const size_t *distance = (const size_t *)node->data; - if (distance != NULL) { - assert_int_equal(*distance, - dns__rbtnode_getdistance(node)); - } - result = dns_rbtnodechain_next(&chain, NULL, NULL); - if (result == ISC_R_NOMORE) { - break; - } - dns_rbtnodechain_current(&chain, NULL, NULL, &node); - } - - assert_int_equal(result, ISC_R_NOMORE); - - dns_rbtnodechain_invalidate(&chain); - - test_context_teardown(ctx); -} - -/* - * Test tree balance, inserting names in random order. - * - * This test checks an important performance-related property of - * the red-black tree, which is important for us: the longest - * path from a sub-tree's root to a node is no more than - * 2log(n). This check verifies that the tree is balanced. - */ -ISC_RUN_TEST_IMPL(rbt_check_distance_random) { - dns_rbt_t *mytree = NULL; - const unsigned int log_num_nodes = 16; - isc_result_t result; - bool tree_ok; - int i; - - isc_mem_debugging = ISC_MEM_DEBUGRECORD; - - result = dns_rbt_create(mctx, delete_data, NULL, &mytree); - assert_int_equal(result, ISC_R_SUCCESS); - - /* Names are inserted in random order. */ - - /* Make a large 65536 node top-level domain tree, i.e., the - * following code inserts names such as: - * - * savoucnsrkrqzpkqypbygwoiliawpbmz. - * wkadamcbbpjtundbxcmuayuycposvngx. - * wzbpznemtooxdpjecdxynsfztvnuyfao. - * yueojmhyffslpvfmgyfwioxegfhepnqq. - */ - for (i = 0; i < (1 << log_num_nodes); i++) { - size_t *n = NULL; - char namebuf[34]; - - n = isc_mem_get(mctx, sizeof(size_t)); - assert_non_null(n); - *n = i + 1; - - while (1) { - int j; - dns_fixedname_t fname; - dns_rbtnode_t *node = NULL; - dns_name_t *name = NULL; - - for (j = 0; j < 32; j++) { - uint32_t v = isc_random_uniform(26); - namebuf[j] = 'a' + v; - } - namebuf[32] = '.'; - namebuf[33] = 0; - - dns_test_namefromstring(namebuf, &fname); - name = dns_fixedname_name(&fname); - - result = dns_rbt_addnode(mytree, name, &node); - node->data = n; - if (result == ISC_R_SUCCESS) { - break; - } - } - } - - /* 1 (root . node) + (1 << log_num_nodes) */ - assert_int_equal(1U + (1U << log_num_nodes), dns_rbt_nodecount(mytree)); - - /* The distance from each node to its sub-tree root must be less - * than 2 * log(n). - */ - assert_true((2U * log_num_nodes) >= dns__rbt_getheight(mytree)); - - /* Also check RB tree properties */ - tree_ok = dns__rbt_checkproperties(mytree); - assert_true(tree_ok); - - dns_rbt_destroy(&mytree, 0); -} - -/* - * Test tree balance, inserting names in sorted order. - * - * This test checks an important performance-related property of - * the red-black tree, which is important for us: the longest - * path from a sub-tree's root to a node is no more than - * 2log(n). This check verifies that the tree is balanced. - */ -ISC_RUN_TEST_IMPL(rbt_check_distance_ordered) { - dns_rbt_t *mytree = NULL; - const unsigned int log_num_nodes = 16; - isc_result_t result; - bool tree_ok; - int i; - - isc_mem_debugging = ISC_MEM_DEBUGRECORD; - - result = dns_rbt_create(mctx, delete_data, NULL, &mytree); - assert_int_equal(result, ISC_R_SUCCESS); - - /* Names are inserted in sorted order. */ - - /* Make a large 65536 node top-level domain tree, i.e., the - * following code inserts names such as: - * - * name00000000. - * name00000001. - * name00000002. - * name00000003. - */ - for (i = 0; i < (1 << log_num_nodes); i++) { - size_t *n; - char namebuf[14]; - dns_fixedname_t fname; - dns_name_t *name = NULL; - dns_rbtnode_t *node = NULL; - - n = isc_mem_get(mctx, sizeof(size_t)); - assert_non_null(n); - *n = i + 1; - - snprintf(namebuf, sizeof(namebuf), "name%08x.", i); - dns_test_namefromstring(namebuf, &fname); - name = dns_fixedname_name(&fname); - - result = dns_rbt_addnode(mytree, name, &node); - assert_int_equal(result, ISC_R_SUCCESS); - node->data = n; - } - - /* 1 (root . node) + (1 << log_num_nodes) */ - assert_int_equal(1U + (1U << log_num_nodes), dns_rbt_nodecount(mytree)); - - /* The distance from each node to its sub-tree root must be less - * than 2 * log(n). - */ - assert_true((2U * log_num_nodes) >= dns__rbt_getheight(mytree)); - - /* Also check RB tree properties */ - tree_ok = dns__rbt_checkproperties(mytree); - assert_true(tree_ok); - - dns_rbt_destroy(&mytree, 0); -} - -static isc_result_t -insert_helper(dns_rbt_t *rbt, const char *namestr, dns_rbtnode_t **node) { - dns_fixedname_t fname; - dns_name_t *name; - - dns_test_namefromstring(namestr, &fname); - name = dns_fixedname_name(&fname); - - return (dns_rbt_addnode(rbt, name, node)); -} - -static bool -compare_labelsequences(dns_rbtnode_t *node, const char *labelstr) { - dns_name_t name; - isc_result_t result; - char *nodestr = NULL; - bool is_equal; - - dns_name_init(&name, NULL); - dns_rbt_namefromnode(node, &name); - - result = dns_name_tostring(&name, &nodestr, mctx); - assert_int_equal(result, ISC_R_SUCCESS); - - is_equal = strcmp(labelstr, nodestr) == 0 ? true : false; - - isc_mem_free(mctx, nodestr); - - return (is_equal); -} - -/* Test insertion into a tree */ -ISC_RUN_TEST_IMPL(rbt_insert) { - isc_result_t result; - test_context_t *ctx; - dns_rbtnode_t *node; - - isc_mem_debugging = ISC_MEM_DEBUGRECORD; - - ctx = test_context_setup(); - - /* Check node count before beginning. */ - assert_int_equal(15, dns_rbt_nodecount(ctx->rbt)); - - /* Try to insert a node that already exists. */ - node = NULL; - result = insert_helper(ctx->rbt, "d.e.f.", &node); - assert_int_equal(result, ISC_R_EXISTS); - - /* Node count must not have changed. */ - assert_int_equal(15, dns_rbt_nodecount(ctx->rbt)); - - /* Try to insert a node that doesn't exist. */ - node = NULL; - result = insert_helper(ctx->rbt, "0.", &node); - assert_int_equal(result, ISC_R_SUCCESS); - assert_true(compare_labelsequences(node, "0")); - - /* Node count must have increased. */ - assert_int_equal(16, dns_rbt_nodecount(ctx->rbt)); - - /* Another. */ - node = NULL; - result = insert_helper(ctx->rbt, "example.com.", &node); - assert_int_equal(result, ISC_R_SUCCESS); - assert_non_null(node); - assert_null(node->data); - - /* Node count must have increased. */ - assert_int_equal(17, dns_rbt_nodecount(ctx->rbt)); - - /* Re-adding it should return EXISTS */ - node = NULL; - result = insert_helper(ctx->rbt, "example.com.", &node); - assert_int_equal(result, ISC_R_EXISTS); - - /* Node count must not have changed. */ - assert_int_equal(17, dns_rbt_nodecount(ctx->rbt)); - - /* Fission the node d.e.f */ - node = NULL; - result = insert_helper(ctx->rbt, "k.e.f.", &node); - assert_int_equal(result, ISC_R_SUCCESS); - assert_true(compare_labelsequences(node, "k")); - - /* Node count must have incremented twice ("d.e.f" fissioned to - * "d" and "e.f", and the newly added "k"). - */ - assert_int_equal(19, dns_rbt_nodecount(ctx->rbt)); - - /* Fission the node "g.h" */ - node = NULL; - result = insert_helper(ctx->rbt, "h.", &node); - assert_int_equal(result, ISC_R_SUCCESS); - assert_true(compare_labelsequences(node, "h")); - - /* Node count must have incremented ("g.h" fissioned to "g" and - * "h"). - */ - assert_int_equal(20, dns_rbt_nodecount(ctx->rbt)); - - /* Add child domains */ - - node = NULL; - result = insert_helper(ctx->rbt, "m.p.w.y.d.e.f.", &node); - assert_int_equal(result, ISC_R_SUCCESS); - assert_true(compare_labelsequences(node, "m")); - assert_int_equal(21, dns_rbt_nodecount(ctx->rbt)); - - node = NULL; - result = insert_helper(ctx->rbt, "n.p.w.y.d.e.f.", &node); - assert_int_equal(result, ISC_R_SUCCESS); - assert_true(compare_labelsequences(node, "n")); - assert_int_equal(22, dns_rbt_nodecount(ctx->rbt)); - - node = NULL; - result = insert_helper(ctx->rbt, "l.a.", &node); - assert_int_equal(result, ISC_R_SUCCESS); - assert_true(compare_labelsequences(node, "l")); - assert_int_equal(23, dns_rbt_nodecount(ctx->rbt)); - - node = NULL; - result = insert_helper(ctx->rbt, "r.d.e.f.", &node); - assert_int_equal(result, ISC_R_SUCCESS); - node = NULL; - result = insert_helper(ctx->rbt, "s.d.e.f.", &node); - assert_int_equal(result, ISC_R_SUCCESS); - assert_int_equal(25, dns_rbt_nodecount(ctx->rbt)); - - node = NULL; - result = insert_helper(ctx->rbt, "h.w.y.d.e.f.", &node); - assert_int_equal(result, ISC_R_SUCCESS); - - /* Add more nodes one by one to cover left and right rotation - * functions. - */ - node = NULL; - result = insert_helper(ctx->rbt, "f.", &node); - assert_int_equal(result, ISC_R_SUCCESS); - - node = NULL; - result = insert_helper(ctx->rbt, "m.", &node); - assert_int_equal(result, ISC_R_SUCCESS); - - node = NULL; - result = insert_helper(ctx->rbt, "nm.", &node); - assert_int_equal(result, ISC_R_SUCCESS); - - node = NULL; - result = insert_helper(ctx->rbt, "om.", &node); - assert_int_equal(result, ISC_R_SUCCESS); - - node = NULL; - result = insert_helper(ctx->rbt, "k.", &node); - assert_int_equal(result, ISC_R_SUCCESS); - - node = NULL; - result = insert_helper(ctx->rbt, "l.", &node); - assert_int_equal(result, ISC_R_SUCCESS); - - node = NULL; - result = insert_helper(ctx->rbt, "fe.", &node); - assert_int_equal(result, ISC_R_SUCCESS); - - node = NULL; - result = insert_helper(ctx->rbt, "ge.", &node); - assert_int_equal(result, ISC_R_SUCCESS); - - node = NULL; - result = insert_helper(ctx->rbt, "i.", &node); - assert_int_equal(result, ISC_R_SUCCESS); - - node = NULL; - result = insert_helper(ctx->rbt, "ae.", &node); - assert_int_equal(result, ISC_R_SUCCESS); - - node = NULL; - result = insert_helper(ctx->rbt, "n.", &node); - assert_int_equal(result, ISC_R_SUCCESS); - - test_context_teardown(ctx); -} - -/* - * Test removal from a tree - * - * This testcase checks that after node removal, the binary-search tree is - * valid and all nodes that are supposed to exist are present in the - * correct order. It mainly tests DomainTree as a BST, and not particularly - * as a red-black tree. This test checks node deletion when upper nodes - * have data. - */ -static isc_result_t -deletename(dns_rbt_t *mytree, const dns_name_t *name) { - isc_result_t result; - dns_rbtnode_t *node = NULL; - - result = dns_rbt_findnode(mytree, name, NULL, &node, NULL, 0, NULL, - NULL); - if (result == ISC_R_SUCCESS) { - if (node->data != NULL) { - result = dns_rbt_deletenode(mytree, node, false); - } else { - result = ISC_R_NOTFOUND; - } - } else if (result == DNS_R_PARTIALMATCH) { - result = ISC_R_NOTFOUND; - } - - return (result); -} - -ISC_RUN_TEST_IMPL(rbt_remove) { - isc_result_t result; - size_t j; - - isc_mem_debugging = ISC_MEM_DEBUGRECORD; - - /* - * Delete single nodes and check if the rest of the nodes exist. - */ - for (j = 0; j < ordered_names_count; j++) { - dns_rbt_t *mytree = NULL; - dns_rbtnode_t *node; - size_t i; - size_t *n; - bool tree_ok; - dns_rbtnodechain_t chain; - size_t start_node; - - /* Create a tree. */ - result = dns_rbt_create(mctx, delete_data, NULL, &mytree); - assert_int_equal(result, ISC_R_SUCCESS); - - /* Insert test data into the tree. */ - for (i = 0; i < domain_names_count; i++) { - node = NULL; - result = insert_helper(mytree, domain_names[i], &node); - assert_int_equal(result, ISC_R_SUCCESS); - } - - /* Check that all names exist in order. */ - for (i = 0; i < ordered_names_count; i++) { - dns_fixedname_t fname; - dns_name_t *name; - - dns_test_namefromstring(ordered_names[i], &fname); - - name = dns_fixedname_name(&fname); - node = NULL; - result = dns_rbt_findnode(mytree, name, NULL, &node, - NULL, DNS_RBTFIND_EMPTYDATA, - NULL, NULL); - assert_int_equal(result, ISC_R_SUCCESS); - - /* Add node data */ - assert_non_null(node); - assert_null(node->data); - - n = isc_mem_get(mctx, sizeof(size_t)); - assert_non_null(n); - *n = i; - - node->data = n; - } - - /* Now, delete the j'th node from the tree. */ - { - dns_fixedname_t fname; - dns_name_t *name; - - dns_test_namefromstring(ordered_names[j], &fname); - - name = dns_fixedname_name(&fname); - - result = deletename(mytree, name); - assert_int_equal(result, ISC_R_SUCCESS); - } - - /* Check RB tree properties. */ - tree_ok = dns__rbt_checkproperties(mytree); - assert_true(tree_ok); - - dns_rbtnodechain_init(&chain); - - /* Now, walk through nodes in order. */ - if (j == 0) { - /* - * Node for ordered_names[0] was already deleted - * above. We start from node 1. - */ - dns_fixedname_t fname; - dns_name_t *name; - - dns_test_namefromstring(ordered_names[0], &fname); - name = dns_fixedname_name(&fname); - node = NULL; - result = dns_rbt_findnode(mytree, name, NULL, &node, - NULL, 0, NULL, NULL); - assert_int_equal(result, ISC_R_NOTFOUND); - - dns_test_namefromstring(ordered_names[1], &fname); - name = dns_fixedname_name(&fname); - node = NULL; - result = dns_rbt_findnode(mytree, name, NULL, &node, - &chain, 0, NULL, NULL); - assert_int_equal(result, ISC_R_SUCCESS); - start_node = 1; - } else { - /* Start from node 0. */ - dns_fixedname_t fname; - dns_name_t *name; - - dns_test_namefromstring(ordered_names[0], &fname); - name = dns_fixedname_name(&fname); - node = NULL; - result = dns_rbt_findnode(mytree, name, NULL, &node, - &chain, 0, NULL, NULL); - assert_int_equal(result, ISC_R_SUCCESS); - start_node = 0; - } - - /* - * node and chain have been set by the code above at - * this point. - */ - for (i = start_node; i < ordered_names_count; i++) { - dns_fixedname_t fname_j, fname_i; - dns_name_t *name_j, *name_i; - - dns_test_namefromstring(ordered_names[j], &fname_j); - name_j = dns_fixedname_name(&fname_j); - dns_test_namefromstring(ordered_names[i], &fname_i); - name_i = dns_fixedname_name(&fname_i); - - if (dns_name_equal(name_i, name_j)) { - /* - * This may be true for the last node if - * we seek ahead in the loop using - * dns_rbtnodechain_next() below. - */ - if (node == NULL) { - break; - } - - /* All ordered nodes have data - * initially. If any node is empty, it - * means it was removed, but an empty - * node exists because it is a - * super-domain. Just skip it. - */ - if (node->data == NULL) { - result = dns_rbtnodechain_next( - &chain, NULL, NULL); - if (result == ISC_R_NOMORE) { - node = NULL; - } else { - dns_rbtnodechain_current( - &chain, NULL, NULL, - &node); - } - } - continue; - } - - assert_non_null(node); - - n = (size_t *)node->data; - if (n != NULL) { - /* printf("n=%zu, i=%zu\n", *n, i); */ - assert_int_equal(*n, i); - } - - result = dns_rbtnodechain_next(&chain, NULL, NULL); - if (result == ISC_R_NOMORE) { - node = NULL; - } else { - dns_rbtnodechain_current(&chain, NULL, NULL, - &node); - } - } - - /* We should have reached the end of the tree. */ - assert_null(node); - - dns_rbt_destroy(&mytree, 0); - } -} - -static void -insert_nodes(dns_rbt_t *mytree, char **names, size_t *names_count, - uint32_t num_names) { - uint32_t i; - dns_rbtnode_t *node; - - for (i = 0; i < num_names; i++) { - size_t *n; - char namebuf[34]; - - n = isc_mem_get(mctx, sizeof(size_t)); - assert_non_null(n); - - *n = i; /* Unused value */ - - while (1) { - int j; - dns_fixedname_t fname; - dns_name_t *name; - isc_result_t result; - - for (j = 0; j < 32; j++) { - uint32_t v = isc_random_uniform(26); - namebuf[j] = 'a' + v; - } - namebuf[32] = '.'; - namebuf[33] = 0; - - dns_test_namefromstring(namebuf, &fname); - name = dns_fixedname_name(&fname); - - node = NULL; - result = dns_rbt_addnode(mytree, name, &node); - if (result == ISC_R_SUCCESS) { - node->data = n; - names[*names_count] = isc_mem_strdup(mctx, - namebuf); - assert_non_null(names[*names_count]); - *names_count += 1; - break; - } - } - } -} - -static void -remove_nodes(dns_rbt_t *mytree, char **names, size_t *names_count, - uint32_t num_names) { - uint32_t i; - - UNUSED(mytree); - - for (i = 0; i < num_names; i++) { - isc_result_t result; - dns_fixedname_t fname; - dns_name_t *name = NULL; - uint32_t num; - - num = isc_random_uniform(*names_count); - - dns_test_namefromstring(names[num], &fname); - name = dns_fixedname_name(&fname); - - result = deletename(mytree, name); - assert_int_equal(result, ISC_R_SUCCESS); - - isc_mem_free(mctx, names[num]); - if (*names_count > 0) { - names[num] = names[*names_count - 1]; - names[*names_count - 1] = NULL; - *names_count -= 1; - } - } -} - -static void -check_tree(dns_rbt_t *mytree, char **names, size_t names_count) { - bool tree_ok; - - UNUSED(names); - - assert_int_equal(names_count + 1, dns_rbt_nodecount(mytree)); - - /* - * The distance from each node to its sub-tree root must be less - * than 2 * log_2(1024). - */ - assert_true((2 * 10) >= dns__rbt_getheight(mytree)); - - /* Also check RB tree properties */ - tree_ok = dns__rbt_checkproperties(mytree); - assert_true(tree_ok); -} - -/* - * Test insert and remove in a loop. - * - * What is the best way to test our red-black tree code? It is - * not a good method to test every case handled in the actual - * code itself. This is because our approach itself may be - * incorrect. - * - * We test our code at the interface level here by exercising the - * tree randomly multiple times, checking that red-black tree - * properties are valid, and all the nodes that are supposed to be - * in the tree exist and are in order. - * - * NOTE: These tests are run within a single tree level in the - * forest. The number of nodes in the tree level doesn't grow - * over 1024. - */ -ISC_RUN_TEST_IMPL(rbt_insert_and_remove) { - isc_result_t result; - dns_rbt_t *mytree = NULL; - size_t *n = NULL; - dns_rbtnode_t *node = NULL; - char *names[1024]; - size_t names_count; - int i; - - isc_mem_debugging = ISC_MEM_DEBUGRECORD; - - result = dns_rbt_create(mctx, delete_data, NULL, &mytree); - assert_int_equal(result, ISC_R_SUCCESS); - - n = isc_mem_get(mctx, sizeof(size_t)); - assert_non_null(n); - result = dns_rbt_addnode(mytree, dns_rootname, &node); - assert_int_equal(result, ISC_R_SUCCESS); - node->data = n; - - memset(names, 0, sizeof(names)); - names_count = 0; - - /* Repeat the insert/remove test some 4096 times */ - for (i = 0; i < 4096; i++) { - uint32_t num_names; - - if (names_count < 1024) { - num_names = isc_random_uniform(1024 - names_count); - num_names++; - } else { - num_names = 0; - } - - insert_nodes(mytree, names, &names_count, num_names); - check_tree(mytree, names, names_count); - - if (names_count > 0) { - num_names = isc_random_uniform(names_count); - num_names++; - } else { - num_names = 0; - } - - remove_nodes(mytree, names, &names_count, num_names); - check_tree(mytree, names, names_count); - } - - /* Remove the rest of the nodes */ - remove_nodes(mytree, names, &names_count, names_count); - check_tree(mytree, names, names_count); - - for (i = 0; i < 1024; i++) { - if (names[i] != NULL) { - isc_mem_free(mctx, names[i]); - } - } - - result = deletename(mytree, dns_rootname); - assert_int_equal(result, ISC_R_SUCCESS); - assert_int_equal(dns_rbt_nodecount(mytree), 0); - - dns_rbt_destroy(&mytree, 0); -} - -/* Test nodechain */ -ISC_RUN_TEST_IMPL(rbt_nodechain) { - isc_result_t result; - test_context_t *ctx; - dns_fixedname_t fname, found, expect; - dns_name_t *name, *foundname, *expected; - dns_rbtnode_t *node = NULL; - dns_rbtnodechain_t chain; - - isc_mem_debugging = ISC_MEM_DEBUGRECORD; - - ctx = test_context_setup(); - - dns_rbtnodechain_init(&chain); - - dns_test_namefromstring("a.", &fname); - name = dns_fixedname_name(&fname); - - result = dns_rbt_findnode(ctx->rbt, name, NULL, &node, &chain, 0, NULL, - NULL); - assert_int_equal(result, ISC_R_SUCCESS); - - foundname = dns_fixedname_initname(&found); - - dns_test_namefromstring("a.", &expect); - expected = dns_fixedname_name(&expect); - UNUSED(expected); - - result = dns_rbtnodechain_first(&chain, ctx->rbt, foundname, NULL); - assert_int_equal(result, DNS_R_NEWORIGIN); - assert_int_equal(dns_name_countlabels(foundname), 0); - - result = dns_rbtnodechain_prev(&chain, NULL, NULL); - assert_int_equal(result, ISC_R_NOMORE); - - result = dns_rbtnodechain_next(&chain, NULL, NULL); - assert_int_equal(result, ISC_R_SUCCESS); - - result = dns_rbtnodechain_next(&chain, NULL, NULL); - assert_int_equal(result, ISC_R_SUCCESS); - - result = dns_rbtnodechain_last(&chain, ctx->rbt, NULL, NULL); - assert_int_equal(result, DNS_R_NEWORIGIN); - - result = dns_rbtnodechain_next(&chain, NULL, NULL); - assert_int_equal(result, ISC_R_NOMORE); - - result = dns_rbtnodechain_last(&chain, ctx->rbt, NULL, NULL); - assert_int_equal(result, DNS_R_NEWORIGIN); - - result = dns_rbtnodechain_prev(&chain, NULL, NULL); - assert_int_equal(result, ISC_R_SUCCESS); - - dns_rbtnodechain_invalidate(&chain); - - test_context_teardown(ctx); -} - -/* Test name lengths */ -ISC_RUN_TEST_IMPL(rbtnode_namelen) { - isc_result_t result; - test_context_t *ctx = NULL; - dns_rbtnode_t *node; - unsigned int len; - - isc_mem_debugging = ISC_MEM_DEBUGRECORD; - - ctx = test_context_setup(); - - node = NULL; - result = insert_helper(ctx->rbt, ".", &node); - len = dns__rbtnode_namelen(node); - assert_int_equal(result, ISC_R_EXISTS); - assert_int_equal(len, 1); - node = NULL; - - result = insert_helper(ctx->rbt, "a.b.c.d.e.f.g.h.i.j.k.l.m.", &node); - len = dns__rbtnode_namelen(node); - assert_int_equal(result, ISC_R_SUCCESS); - assert_int_equal(len, 27); - - node = NULL; - result = insert_helper(ctx->rbt, "isc.org.", &node); - len = dns__rbtnode_namelen(node); - assert_int_equal(result, ISC_R_SUCCESS); - assert_int_equal(len, 9); - - node = NULL; - result = insert_helper(ctx->rbt, "example.com.", &node); - len = dns__rbtnode_namelen(node); - assert_int_equal(result, ISC_R_SUCCESS); - assert_int_equal(len, 13); - - test_context_teardown(ctx); -} - -#if defined(DNS_BENCHMARK_TESTS) && !defined(__SANITIZE_THREAD__) - -/* - * XXXMUKS: Don't delete this code. It is useful in benchmarking the - * RBT, but we don't require it as part of the unit test runs. - */ - -static dns_fixedname_t *fnames; -static dns_name_t **names; -static int *values; - -static void * -find_thread(void *arg) { - dns_rbt_t *mytree; - isc_result_t result; - dns_rbtnode_t *node; - unsigned int j, i; - unsigned int start = 0; - - mytree = (dns_rbt_t *)arg; - while (start == 0) { - start = random() % 4000000; - } - - /* Query 32 million random names from it in each thread */ - for (j = 0; j < 8; j++) { - for (i = start; i != start - 1; i = (i + 1) % 4000000) { - node = NULL; - result = dns_rbt_findnode(mytree, names[i], NULL, &node, - NULL, DNS_RBTFIND_EMPTYDATA, - NULL, NULL); - assert_int_equal(result, ISC_R_SUCCESS); - assert_non_null(node); - assert_int_equal(values[i], (intptr_t)node->data); - } - } - - return (NULL); -} - -/* Benchmark RBT implementation */ -ISC_RUN_TEST_IMPL(benchmark) { - isc_result_t result; - char namestr[sizeof("name18446744073709551616.example.org.")]; - unsigned int r; - dns_rbt_t *mytree; - dns_rbtnode_t *node; - unsigned int i; - unsigned int maxvalue = 1000000; - isc_time_t ts1, ts2; - double t; - unsigned int nthreads; - isc_thread_t threads[32]; - - srandom(time(NULL)); - - debug_mem_record = false; - - fnames = (dns_fixedname_t *)malloc(4000000 * sizeof(dns_fixedname_t)); - names = (dns_name_t **)malloc(4000000 * sizeof(dns_name_t *)); - values = (int *)malloc(4000000 * sizeof(int)); - - for (i = 0; i < 4000000; i++) { - r = ((unsigned long)random()) % maxvalue; - snprintf(namestr, sizeof(namestr), "name%u.example.org.", r); - dns_test_namefromstring(namestr, &fnames[i]); - names[i] = dns_fixedname_name(&fnames[i]); - values[i] = r; - } - - /* Create a tree. */ - mytree = NULL; - result = dns_rbt_create(mctx, NULL, NULL, &mytree); - assert_int_equal(result, ISC_R_SUCCESS); - - /* Insert test data into the tree. */ - for (i = 0; i < maxvalue; i++) { - snprintf(namestr, sizeof(namestr), "name%u.example.org.", i); - node = NULL; - result = insert_helper(mytree, namestr, &node); - assert_int_equal(result, ISC_R_SUCCESS); - node->data = (void *)(intptr_t)i; - } - - ts1 = isc_time_now(); - - nthreads = ISC_MIN(isc_os_ncpus(), 32); - nthreads = ISC_MAX(nthreads, 1); - for (i = 0; i < nthreads; i++) { - isc_thread_create(find_thread, mytree, &threads[i]); - } - - for (i = 0; i < nthreads; i++) { - isc_thread_join(threads[i], NULL); - } - - ts2 = isc_time_now(); - - t = isc_time_microdiff(&ts2, &ts1); - - printf("%u findnode calls, %f seconds, %f calls/second\n", - nthreads * 8 * 4000000, t / 1000000.0, - (nthreads * 8 * 4000000) / (t / 1000000.0)); - - free(values); - free(names); - free(fnames); - - dns_rbt_destroy(&mytree, 0); -} -#endif /* defined(DNS_BENCHMARK_TESTS) && !defined(__SANITIZE_THREAD__) */ - -ISC_TEST_LIST_START -ISC_TEST_ENTRY(rbt_create) -ISC_TEST_ENTRY(rbt_nodecount) -ISC_TEST_ENTRY(rbtnode_get_distance) -ISC_TEST_ENTRY(rbt_check_distance_random) -ISC_TEST_ENTRY(rbt_check_distance_ordered) -ISC_TEST_ENTRY(rbt_insert) -ISC_TEST_ENTRY(rbt_remove) -ISC_TEST_ENTRY(rbt_insert_and_remove) -ISC_TEST_ENTRY(rbt_nodechain) -ISC_TEST_ENTRY(rbtnode_namelen) -#if defined(DNS_BENCHMARK_TESTS) && !defined(__SANITIZE_THREAD__) -ISC_TEST_ENTRY(benchmark) -#endif /* defined(DNS_BENCHMARK_TESTS) && !defined(__SANITIZE_THREAD__) */ - -ISC_TEST_LIST_END - -ISC_TEST_MAIN