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