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
#include <dns/order.h>
#include <dns/peer.h>
#include <dns/private.h>
-#include <dns/rbt.h>
#include <dns/rdataclass.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
* 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.
*/
#include <dns/db.h>
#include <dns/diff.h>
#include <dns/enumclass.h>
-#include <dns/rbt.h>
#include <dns/rdatalist.h>
#include <dns/rdatastruct.h>
#include <dns/soa.h>
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;
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));
REQUIRE(VALID_SAMPLEDB(sampledb));
- dns_db_currentversion(sampledb->rbtdb, versionp);
+ dns_db_currentversion(sampledb->db, versionp);
}
static isc_result_t
REQUIRE(VALID_SAMPLEDB(sampledb));
- return (dns_db_newversion(sampledb->rbtdb, versionp));
+ return (dns_db_newversion(sampledb->db, versionp));
}
static void
REQUIRE(VALID_SAMPLEDB(sampledb));
- dns_db_attachversion(sampledb->rbtdb, source, targetp);
+ dns_db_attachversion(sampledb->db, source, targetp);
}
static void
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
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));
}
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));
}
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));
}
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
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
REQUIRE(VALID_SAMPLEDB(sampledb));
- return (dns_db_createiterator(sampledb->rbtdb, options, iteratorp));
+ return (dns_db_createiterator(sampledb->db, options, iteratorp));
}
static isc_result_t
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));
}
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
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));
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;
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));
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));
}
REQUIRE(VALID_SAMPLEDB(sampledb));
- return (dns_db_issecure(sampledb->rbtdb));
+ return (dns_db_issecure(sampledb->db));
}
static unsigned int
REQUIRE(VALID_SAMPLEDB(sampledb));
- return (dns_db_nodecount(sampledb->rbtdb, tree));
+ return (dns_db_nodecount(sampledb->db, tree));
}
static void
REQUIRE(VALID_SAMPLEDB(sampledb));
- dns_db_setloop(sampledb->rbtdb, loop);
+ dns_db_setloop(sampledb->db, loop);
}
static isc_result_t
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
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));
}
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));
}
REQUIRE(VALID_SAMPLEDB(sampledb));
- return (dns_db_setsigningtime(sampledb->rbtdb, rdataset, resign));
+ return (dns_db_setsigningtime(sampledb->db, rdataset, resign));
}
static isc_result_t
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 *
REQUIRE(VALID_SAMPLEDB(sampledb));
- return (dns_db_getrrsetstats(sampledb->rbtdb));
+ return (dns_db_getrrsetstats(sampledb->db));
}
static isc_result_t
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));
}
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
REQUIRE(VALID_SAMPLEDB(sampledb));
- return (dns_db_setcachestats(sampledb->rbtdb, stats));
+ return (dns_db_setcachestats(sampledb->db, stats));
}
static isc_result_t
REQUIRE(VALID_SAMPLEDB(sampledb));
- return (dns_db_nodefullname(sampledb->rbtdb, node, name));
+ return (dns_db_nodefullname(sampledb->db, node, name));
}
/*
/* 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;
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
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
+++ /dev/null
-<!--
-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.
--->
-
- 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
-<dns/rbt.h>. This header file automatically includes <isc/result.h),
-<isc/mem.h>, <dns/types.h>, and <dns/name.h>.
-
-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;
-
-@@@
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 \
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 \
#include "db_p.h"
#include "qpcache_p.h"
#include "qpzone_p.h"
-#include "rbtdb_p.h"
unsigned int dns_pps = 0U;
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;
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,
.link = ISC_LINK_INITIALIZER,
};
- ISC_LIST_APPEND(implementations, &rbtimp, link);
ISC_LIST_APPEND(implementations, &qpimp, link);
ISC_LIST_APPEND(implementations, &qpzoneimp, link);
}
#include <isc/urcu.h>
#include <dns/nsec3.h>
-#include <dns/rbt.h>
#include <dns/types.h>
#define GLUETABLE_INIT_SIZE 1 << 2
#include <dns/ecs.h>
#include <dns/nsec.h>
-#include <dns/rbt.h>
#include <dns/rdata.h>
#include <dns/rdatatype.h>
#include <dns/result.h>
+++ /dev/null
-/*
- * 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 <inttypes.h>
-#include <stdbool.h>
-
-#include <isc/assertions.h>
-#include <isc/lang.h>
-#include <isc/magic.h>
-#include <isc/refcount.h>
-
-#include <dns/types.h>
-
-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
#include <dns/nsec3.h>
#include <dns/qp.h>
-#include <dns/rbt.h>
#include <dns/types.h>
/*****
+++ /dev/null
-/*
- * 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 <inttypes.h>
-#include <stdbool.h>
-#include <sys/mman.h>
-
-#include <isc/ascii.h>
-#include <isc/async.h>
-#include <isc/atomic.h>
-#include <isc/file.h>
-#include <isc/hash.h>
-#include <isc/hashmap.h>
-#include <isc/heap.h>
-#include <isc/hex.h>
-#include <isc/log.h>
-#include <isc/loop.h>
-#include <isc/mem.h>
-#include <isc/mutex.h>
-#include <isc/once.h>
-#include <isc/random.h>
-#include <isc/refcount.h>
-#include <isc/result.h>
-#include <isc/rwlock.h>
-#include <isc/serial.h>
-#include <isc/stdio.h>
-#include <isc/string.h>
-#include <isc/time.h>
-#include <isc/urcu.h>
-#include <isc/util.h>
-
-#include <dns/callbacks.h>
-#include <dns/db.h>
-#include <dns/dbiterator.h>
-#include <dns/fixedname.h>
-#include <dns/masterdump.h>
-#include <dns/nsec.h>
-#include <dns/nsec3.h>
-#include <dns/rbt.h>
-#include <dns/rdata.h>
-#include <dns/rdataset.h>
-#include <dns/rdatasetiter.h>
-#include <dns/rdataslab.h>
-#include <dns/rdatastruct.h>
-#include <dns/stats.h>
-#include <dns/time.h>
-#include <dns/view.h>
-#include <dns/zone.h>
-#include <dns/zonekey.h>
-
-#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;
- }
- }
- }
-}
+++ /dev/null
-/*
- * 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 <inttypes.h>
-#include <stdbool.h>
-#include <sys/mman.h>
-
-#include <isc/ascii.h>
-#include <isc/async.h>
-#include <isc/atomic.h>
-#include <isc/file.h>
-#include <isc/hash.h>
-#include <isc/hashmap.h>
-#include <isc/heap.h>
-#include <isc/hex.h>
-#include <isc/log.h>
-#include <isc/loop.h>
-#include <isc/mem.h>
-#include <isc/mutex.h>
-#include <isc/once.h>
-#include <isc/random.h>
-#include <isc/refcount.h>
-#include <isc/result.h>
-#include <isc/rwlock.h>
-#include <isc/serial.h>
-#include <isc/stdio.h>
-#include <isc/string.h>
-#include <isc/time.h>
-#include <isc/urcu.h>
-#include <isc/util.h>
-
-#include <dns/callbacks.h>
-#include <dns/db.h>
-#include <dns/dbiterator.h>
-#include <dns/fixedname.h>
-#include <dns/masterdump.h>
-#include <dns/nsec.h>
-#include <dns/nsec3.h>
-#include <dns/rbt.h>
-#include <dns/rdata.h>
-#include <dns/rdataset.h>
-#include <dns/rdatasetiter.h>
-#include <dns/rdataslab.h>
-#include <dns/rdatastruct.h>
-#include <dns/stats.h>
-#include <dns/time.h>
-#include <dns/view.h>
-#include <dns/zone.h>
-#include <dns/zonekey.h>
-
-#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);
-}
+++ /dev/null
-/*
- * 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 <inttypes.h>
-#include <stdbool.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <isc/file.h>
-#include <isc/hash.h>
-#include <isc/hex.h>
-#include <isc/log.h>
-#include <isc/mem.h>
-#include <isc/once.h>
-#include <isc/refcount.h>
-#include <isc/result.h>
-#include <isc/stdio.h>
-#include <isc/string.h>
-#include <isc/util.h>
-
-#include <dns/db.h>
-#include <dns/fixedname.h>
-#include <dns/rbt.h>
-
-#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, "<error building name: %s>",
- 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 = \"<f0> |<f1> ", *nodecount);
- printnodename(node, false, f);
- fprintf(f, "|<f2>");
-
- if (show_pointers) {
- fprintf(f, "|<f3> n=%p|<f4> 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.
- */
+++ /dev/null
-/*
- * 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 <inttypes.h>
-#include <stdbool.h>
-#include <sys/mman.h>
-
-#include <isc/ascii.h>
-#include <isc/async.h>
-#include <isc/atomic.h>
-#include <isc/file.h>
-#include <isc/hash.h>
-#include <isc/hashmap.h>
-#include <isc/heap.h>
-#include <isc/hex.h>
-#include <isc/log.h>
-#include <isc/loop.h>
-#include <isc/mem.h>
-#include <isc/mutex.h>
-#include <isc/once.h>
-#include <isc/random.h>
-#include <isc/refcount.h>
-#include <isc/result.h>
-#include <isc/rwlock.h>
-#include <isc/serial.h>
-#include <isc/stdio.h>
-#include <isc/string.h>
-#include <isc/time.h>
-#include <isc/urcu.h>
-#include <isc/util.h>
-
-#include <dns/callbacks.h>
-#include <dns/db.h>
-#include <dns/dbiterator.h>
-#include <dns/fixedname.h>
-#include <dns/masterdump.h>
-#include <dns/nsec.h>
-#include <dns/nsec3.h>
-#include <dns/rbt.h>
-#include <dns/rdata.h>
-#include <dns/rdataset.h>
-#include <dns/rdatasetiter.h>
-#include <dns/rdataslab.h>
-#include <dns/rdatastruct.h>
-#include <dns/stats.h>
-#include <dns/time.h>
-#include <dns/view.h>
-#include <dns/zone.h>
-#include <dns/zonekey.h>
-
-#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, "<UNKNOWN>", 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, "<UNKNOWN>", 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, "<UNKNOWN>", 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;
-}
+++ /dev/null
-/*
- * 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 <isc/heap.h>
-#include <isc/lang.h>
-#include <isc/rwlock.h>
-#include <isc/urcu.h>
-
-#include <dns/nsec3.h>
-#include <dns/rbt.h>
-#include <dns/types.h>
-
-#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
#include <dns/nsec3.h>
#include <dns/opcode.h>
#include <dns/peer.h>
-#include <dns/rbt.h>
#include <dns/rcode.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
#include <dns/nta.h>
#include <dns/order.h>
#include <dns/peer.h>
-#include <dns/rbt.h>
#include <dns/rdataset.h>
#include <dns/request.h>
#include <dns/resolver.h>
#include <dns/keystore.h>
#include <dns/keyvalues.h>
#include <dns/peer.h>
-#include <dns/rbt.h>
#include <dns/rdataclass.h>
#include <dns/rdatatype.h>
#include <dns/rpz.h>
#include <dns/nsec.h>
#include <dns/nsec3.h>
#include <dns/order.h>
-#include <dns/rbt.h>
#include <dns/rcode.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
#include <isc/hashmap.h>
#include <isc/ht.h>
#include <isc/list.h>
+#include <isc/refcount.h>
#include <isc/rwlock.h>
#include <isc/thread.h>
#include <isc/urcu.h>
#include <dns/fixedname.h>
#include <dns/qp.h>
-#include <dns/rbt.h>
#include <dns/types.h>
#include "qp_p.h"
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
*/
{ "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 },
#include <dns/fixedname.h>
#include <dns/qp.h>
-#include <dns/rbt.h>
#include <dns/types.h>
#include <tests/dns.h>
#include <dns/fixedname.h>
#include <dns/qp.h>
-#include <dns/rbt.h>
#include <dns/types.h>
#include <tests/dns.h>
qpmulti_test \
qpdb_test \
qpzone_test \
- rbt_test \
rdata_test \
rdataset_test \
rdatasetstats_test \
#include <isc/util.h>
-#include <dns/rbt.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
#include <dns/rdatastruct.h>
+++ /dev/null
-/*
- * 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 <ctype.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <sched.h> /* IWYU pragma: keep */
-#include <setjmp.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
-#define UNIT_TESTING
-#include <cmocka.h>
-
-#include <isc/buffer.h>
-#include <isc/file.h>
-#include <isc/hash.h>
-#include <isc/log.h>
-#include <isc/mem.h>
-#include <isc/os.h>
-#include <isc/random.h>
-#include <isc/result.h>
-#include <isc/stdio.h>
-#include <isc/string.h>
-#include <isc/thread.h>
-#include <isc/time.h>
-#include <isc/timer.h>
-#include <isc/util.h>
-
-#include <dns/compress.h>
-#include <dns/fixedname.h>
-#include <dns/name.h>
-#include <dns/rbt.h>
-
-#include <dst/dst.h>
-
-#include <tests/dns.h>
-
-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