Merge branch 'each-cleanup-rbt' into 'main'

replace RBT usage throughout named

See merge request isc-projects/bind9!8213
This commit is contained in:
Ondřej Surý 2023-09-04 09:07:32 +00:00
commit 00a6268c8f
13 changed files with 978 additions and 378 deletions

View file

@ -1,3 +1,7 @@
6238. [cleanup] Refactor several objects relying on dns_rbt trees
to instead of dns_nametree, a wrapper around dns_qp.
[GL !8213]
6237. [bug] Address memory leaks due to not clearing OpenSSL error
stack. [GL #4159]

View file

@ -83,6 +83,7 @@
#include <dns/keyvalues.h>
#include <dns/master.h>
#include <dns/masterdump.h>
#include <dns/nametree.h>
#include <dns/nsec3.h>
#include <dns/nta.h>
#include <dns/order.h>
@ -602,21 +603,23 @@ configure_view_sortlist(const cfg_obj_t *vconfig, const cfg_obj_t *config,
static isc_result_t
configure_view_nametable(const cfg_obj_t *vconfig, const cfg_obj_t *config,
const char *confname, const char *conftuplename,
isc_mem_t *mctx, dns_rbt_t **rbtp) {
isc_result_t result;
isc_mem_t *mctx, dns_nametree_t **ntp) {
isc_result_t result = ISC_R_SUCCESS;
const cfg_obj_t *maps[3];
const cfg_obj_t *obj = NULL;
const cfg_listelt_t *element;
const cfg_listelt_t *element = NULL;
int i = 0;
dns_fixedname_t fixed;
dns_name_t *name;
dns_name_t *name = NULL;
isc_buffer_t b;
const char *str;
const cfg_obj_t *nameobj;
const char *str = NULL;
const cfg_obj_t *nameobj = NULL;
if (*rbtp != NULL) {
dns_rbt_destroy(rbtp);
if (*ntp != NULL) {
dns_nametree_detach(ntp);
}
dns_nametree_create(mctx, DNS_NAMETREE_BOOL, confname, ntp);
if (vconfig != NULL) {
maps[i++] = cfg_tuple_get(vconfig, "options");
}
@ -632,7 +635,7 @@ configure_view_nametable(const cfg_obj_t *vconfig, const cfg_obj_t *config,
(void)named_config_get(maps, confname, &obj);
if (obj == NULL) {
/*
* No value available. *rbtp == NULL.
* No value available. *ntp == NULL.
*/
return (ISC_R_SUCCESS);
}
@ -644,11 +647,6 @@ configure_view_nametable(const cfg_obj_t *vconfig, const cfg_obj_t *config,
}
}
result = dns_rbt_create(mctx, NULL, NULL, rbtp);
if (result != ISC_R_SUCCESS) {
return (result);
}
name = dns_fixedname_initname(&fixed);
for (element = cfg_list_first(obj); element != NULL;
element = cfg_list_next(element))
@ -658,14 +656,7 @@ configure_view_nametable(const cfg_obj_t *vconfig, const cfg_obj_t *config,
isc_buffer_constinit(&b, str, strlen(str));
isc_buffer_add(&b, strlen(str));
CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL));
/*
* We don't need the node data, but need to set dummy data to
* avoid a partial match with an empty node. For example, if
* we have foo.example.com and bar.example.com, we'd get a match
* for baz.example.com, which is not the expected result.
* We simply use (void *)1 as the dummy data.
*/
result = dns_rbt_addname(*rbtp, name, (void *)1);
result = dns_nametree_add(*ntp, name, true);
if (result != ISC_R_SUCCESS) {
cfg_obj_log(nameobj, named_g_lctx, ISC_LOG_ERROR,
"failed to add %s for %s: %s", str,
@ -674,10 +665,10 @@ configure_view_nametable(const cfg_obj_t *vconfig, const cfg_obj_t *config,
}
}
return (result);
return (ISC_R_SUCCESS);
cleanup:
dns_rbt_destroy(rbtp);
dns_nametree_detach(ntp);
return (result);
}
@ -4915,7 +4906,6 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config,
/*
* Set supported DNSSEC algorithms.
*/
dns_resolver_reset_algorithms(view->resolver);
disabled = NULL;
(void)named_config_get(maps, "disable-algorithms", &disabled);
if (disabled != NULL) {
@ -4930,7 +4920,6 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config,
/*
* Set supported DS digest types.
*/
dns_resolver_reset_ds_digests(view->resolver);
disabled = NULL;
(void)named_config_get(maps, "disable-ds-digests", &disabled);
if (disabled != NULL) {
@ -5530,7 +5519,7 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config,
*/
CHECK(configure_view_dnsseckeys(view, vconfig, config, bindkeys,
auto_root));
dns_resolver_resetmustbesecure(view->resolver);
obj = NULL;
result = named_config_get(maps, "dnssec-must-be-secure", &obj);
if (result == ISC_R_SUCCESS) {

View file

@ -91,6 +91,7 @@ libdns_la_HEADERS = \
include/dns/masterdump.h \
include/dns/message.h \
include/dns/name.h \
include/dns/nametree.h \
include/dns/ncache.h \
include/dns/nsec.h \
include/dns/nsec3.h \
@ -196,6 +197,7 @@ libdns_la_SOURCES = \
masterdump.c \
message.c \
name.c \
nametree.c \
ncache.c \
nsec.c \
nsec3.c \

View file

@ -0,0 +1,194 @@
/*
* 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
/*****
***** Module Info
*****/
/*! \file
* \brief
* A nametree module is a tree of DNS names containing boolean values
* or bitfields, allowing a quick lookup to see whether a name is included
* in or excluded from some policy.
*/
#include <stdbool.h>
#include <isc/lang.h>
#include <isc/magic.h>
#include <isc/refcount.h>
#include <isc/rwlock.h>
#include <isc/stdtime.h>
#include <dns/rdatastruct.h>
#include <dns/types.h>
#include <dst/dst.h>
/* Define to 1 for detailed reference tracing */
#undef DNS_NAMETREE_TRACE
typedef enum {
DNS_NAMETREE_BOOL,
DNS_NAMETREE_BITS,
DNS_NAMETREE_COUNT
} dns_nametree_type_t;
ISC_LANG_BEGINDECLS
void
dns_nametree_create(isc_mem_t *mctx, dns_nametree_type_t type, const char *name,
dns_nametree_t **ntp);
/*%<
* Create a nametree.
*
* If 'name' is not NULL, it will be saved as the name of the QP trie
* for debugging purposes.
*
* 'type' indicates whether the tree will be used for storing boolean
* values (DNS_NAMETREE_BOOL), bitfields (DNS_NAMETREE_BITS), or counters
* (DNS_NAMETREE_COUNT).
*
* Requires:
*
*\li 'mctx' is a valid memory context.
*\li ntp != NULL && *ntp == NULL
*/
isc_result_t
dns_nametree_add(dns_nametree_t *nametree, const dns_name_t *name,
uint32_t value);
/*%<
* Add a node to 'nametree'.
*
* If the nametree type was set to DNS_NAMETREE_BOOL, then 'value'
* represents a single boolean value, true or false. If the name already
* exists within the tree, then return ISC_R_EXISTS.
*
* If the nametree type was set to DNS_NAMETREE_COUNT, then 'value'
* can only be true. Each time the same name is added to the tree,
* ISC_R_SUCCESS is returned and a counter is incremented.
* dns_nametree_delete() must be deleted the same number of times
* as dns_nametree_add() before the name is removed from the tree.
*
* If the nametree type was set to DNS_NAMETREE_BITS, then 'value' is
* a bit number within a bit field, which is sized to accomodate at least
* 'value' bits. If the name already exists, then that bit will be set
* in the bitfield, other bits will be retained, and ISC_R_SUCCESS will be
* returned. If 'value' excees the number of bits in the existing bit
* field, the field will be expanded.
*
* Requires:
*
*\li 'nametree' points to a valid nametree.
*
* Returns:
*
*\li ISC_R_SUCCESS
*\li ISC_R_EXISTS
*
*\li Any other result indicates failure.
*/
isc_result_t
dns_nametree_delete(dns_nametree_t *nametree, const dns_name_t *name);
/*%<
* Delete 'name' from 'nametree'.
*
* If the nametree type was set to DNS_NAMETREE_COUNT, then this must
* be called for each name the same number of times as dns_nametree_add()
* was called before the name is removed.
*
* Requires:
*
*\li 'nametree' points to a valid nametree.
*\li 'name' is not NULL
*
* Returns:
*
*\li ISC_R_SUCCESS
*
*\li Any other result indicates failure.
*/
isc_result_t
dns_nametree_find(dns_nametree_t *nametree, const dns_name_t *name,
dns_ntnode_t **ntp);
/*%<
* Retrieve the node that exactly matches 'name' from 'nametree'.
*
* Requires:
*
*\li 'nametree' is a valid nametree.
*
*\li 'name' is a valid name.
*
*\li ntp != NULL && *ntp == NULL
*
* Returns:
*
*\li ISC_R_SUCCESS
*\li ISC_R_NOTFOUND
*
*\li Any other result indicates an error.
*/
bool
dns_nametree_covered(dns_nametree_t *nametree, const dns_name_t *name,
dns_name_t *found, uint32_t bit);
/*%<
* Indicates whether a 'name' (with optional 'bit' value) is covered by
* 'nametree'.
*
* In DNS_NAMETREE_BOOL nametrees, this returns true if 'name' has a match
* or a closest ancestor in 'nametree' with its value set to 'true'.
* 'bit' is ignored.
*
* In DNS_NAMETREE_BITS trees, this returns true if 'name' has a match or
* a closest ancestor in 'nametree' with the 'bit' set in its bitfield.
*
* If a name is not found, the default return value is false.
*
* If 'found' is not NULL, the name or ancestor name that was found in
* the tree is copied into it.
*
* Requires:
*
*\li 'nametree' is a valid nametree, or is NULL.
*/
#if DNS_NAMETREE_TRACE
#define dns_nametree_ref(ptr) \
dns_nametree__ref(ptr, __func__, __FILE__, __LINE__)
#define dns_nametree_unref(ptr) \
dns_nametree__unref(ptr, __func__, __FILE__, __LINE__)
#define dns_nametree_attach(ptr, ptrp) \
dns_nametree__attach(ptr, ptrp, __func__, __FILE__, __LINE__)
#define dns_nametree_detach(ptrp) \
dns_nametree__detach(ptrp, __func__, __FILE__, __LINE__)
#define dns_ntnode_ref(ptr) dns_ntnode__ref(ptr, __func__, __FILE__, __LINE__)
#define dns_ntnode_unref(ptr) \
dns_ntnode__unref(ptr, __func__, __FILE__, __LINE__)
#define dns_ntnode_attach(ptr, ptrp) \
dns_ntnode__attach(ptr, ptrp, __func__, __FILE__, __LINE__)
#define dns_ntnode_detach(ptrp) \
dns_ntnode__detach(ptrp, __func__, __FILE__, __LINE__)
ISC_REFCOUNT_TRACE_DECL(dns_nametree);
ISC_REFCOUNT_TRACE_DECL(dns_ntnode);
#else
ISC_REFCOUNT_DECL(dns_nametree);
ISC_REFCOUNT_DECL(dns_ntnode);
#endif
ISC_LANG_ENDDECLS

View file

@ -423,18 +423,6 @@ dns_resolver_addalternate(dns_resolver_t *resolver, const isc_sockaddr_t *alt,
* \li only one of 'name' or 'alt' to be valid.
*/
void
dns_resolver_reset_algorithms(dns_resolver_t *resolver);
/*%<
* Clear the disabled DNSSEC algorithms.
*/
void
dns_resolver_reset_ds_digests(dns_resolver_t *resolver);
/*%<
* Clear the disabled DS digest types.
*/
isc_result_t
dns_resolver_disable_algorithm(dns_resolver_t *resolver, const dns_name_t *name,
unsigned int alg);
@ -482,9 +470,6 @@ dns_resolver_ds_digest_supported(dns_resolver_t *resolver,
* crypto libraries if it was not specifically disabled.
*/
void
dns_resolver_resetmustbesecure(dns_resolver_t *resolver);
isc_result_t
dns_resolver_setmustbesecure(dns_resolver_t *resolver, const dns_name_t *name,
bool value);

View file

@ -116,8 +116,10 @@ typedef struct dns_message dns_message_t;
typedef uint16_t dns_messageid_t;
typedef isc_region_t dns_label_t;
typedef struct dns_name dns_name_t;
typedef struct dns_nametree dns_nametree_t;
typedef ISC_LIST(dns_name_t) dns_namelist_t;
typedef struct dns_ntatable dns_ntatable_t;
typedef struct dns_ntnode dns_ntnode_t;
typedef uint16_t dns_opcode_t;
typedef struct dns_order dns_order_t;
typedef struct dns_peer dns_peer_t;

View file

@ -140,12 +140,11 @@ struct dns_view {
dns_acl_t *denyansweracl;
dns_acl_t *nocasecompress;
bool msgcompression;
dns_rbt_t *answeracl_exclude;
dns_rbt_t *denyanswernames;
dns_rbt_t *answernames_exclude;
dns_nametree_t *answeracl_exclude;
dns_nametree_t *denyanswernames;
dns_nametree_t *answernames_exclude;
dns_nametree_t *sfd;
dns_rrl_t *rrl;
dns_rbt_t *sfd;
isc_rwlock_t sfd_lock;
bool provideixfr;
bool requestnsid;
bool sendcookie;

342
lib/dns/nametree.c Normal file
View file

@ -0,0 +1,342 @@
/*
* 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 <stdbool.h>
#include <isc/mem.h>
#include <isc/refcount.h>
#include <isc/result.h>
#include <isc/string.h>
#include <isc/urcu.h>
#include <isc/util.h>
#include <dns/fixedname.h>
#include <dns/nametree.h>
#include <dns/qp.h>
#define NAMETREE_MAGIC ISC_MAGIC('N', 'T', 'r', 'e')
#define VALID_NAMETREE(kt) ISC_MAGIC_VALID(kt, NAMETREE_MAGIC)
struct dns_nametree {
unsigned int magic;
isc_mem_t *mctx;
isc_refcount_t references;
dns_nametree_type_t type;
dns_qpmulti_t *table;
char name[64];
};
struct dns_ntnode {
isc_mem_t *mctx;
isc_refcount_t references;
dns_fixedname_t fn;
dns_name_t *name;
bool set;
uint8_t *bits;
};
/* QP trie methods */
static void
qp_attach(void *uctx, void *pval, uint32_t ival);
static void
qp_detach(void *uctx, void *pval, uint32_t ival);
static size_t
qp_makekey(dns_qpkey_t key, void *uctx, void *pval, uint32_t ival);
static void
qp_triename(void *uctx, char *buf, size_t size);
static dns_qpmethods_t qpmethods = {
qp_attach,
qp_detach,
qp_makekey,
qp_triename,
};
static void
destroy_ntnode(dns_ntnode_t *node) {
isc_refcount_destroy(&node->references);
if (node->bits != NULL) {
isc_mem_cput(node->mctx, node->bits, node->bits[0],
sizeof(char));
}
isc_mem_putanddetach(&node->mctx, node, sizeof(dns_ntnode_t));
}
#if DNS_NAMETREE_TRACE
ISC_REFCOUNT_TRACE_IMPL(dns_ntnode, destroy_ntnode);
#else
ISC_REFCOUNT_IMPL(dns_ntnode, destroy_ntnode);
#endif
void
dns_nametree_create(isc_mem_t *mctx, dns_nametree_type_t type, const char *name,
dns_nametree_t **ntp) {
dns_nametree_t *nametree = NULL;
REQUIRE(ntp != NULL && *ntp == NULL);
nametree = isc_mem_get(mctx, sizeof(*nametree));
*nametree = (dns_nametree_t){
.magic = NAMETREE_MAGIC,
.type = type,
};
isc_mem_attach(mctx, &nametree->mctx);
isc_refcount_init(&nametree->references, 1);
if (name != NULL) {
strlcpy(nametree->name, name, sizeof(nametree->name));
}
dns_qpmulti_create(mctx, &qpmethods, nametree, &nametree->table);
*ntp = nametree;
}
static void
destroy_nametree(dns_nametree_t *nametree) {
/* dns_qpread_t qpr; */
/* dns_qpiter_t iter; */
/* void *pval = NULL; */
nametree->magic = 0;
/* dns_qpmulti_query(nametree->table, &qpr); */
/* dns_qpiter_init(&qpr, &iter); */
/* while (dns_qpiter_next(&iter, &pval, NULL) == ISC_R_SUCCESS) { */
/* dns_ntnode_t *n = pval; */
/* dns_ntnode_detach(&n); */
/* } */
/* dns_qpread_destroy(nametree->table, &qpr); */
dns_qpmulti_destroy(&nametree->table);
isc_refcount_destroy(&nametree->references);
isc_mem_putanddetach(&nametree->mctx, nametree, sizeof(*nametree));
}
#if DNS_NAMETREE_TRACE
ISC_REFCOUNT_TRACE_IMPL(dns_nametree, destroy_nametree);
#else
ISC_REFCOUNT_IMPL(dns_nametree, destroy_nametree);
#endif
static dns_ntnode_t *
newnode(isc_mem_t *mctx, const dns_name_t *name) {
dns_ntnode_t *node = isc_mem_get(mctx, sizeof(*node));
*node = (dns_ntnode_t){ 0 };
isc_mem_attach(mctx, &node->mctx);
isc_refcount_init(&node->references, 1);
node->name = dns_fixedname_initname(&node->fn);
dns_name_copy(name, node->name);
return (node);
}
static bool
matchbit(unsigned char *bits, uint32_t val) {
unsigned int len = val / 8 + 2;
unsigned int mask = 1 << (val % 8);
if (len <= bits[0] && (bits[len - 1] & mask) != 0) {
return (true);
}
return (false);
}
isc_result_t
dns_nametree_add(dns_nametree_t *nametree, const dns_name_t *name,
uint32_t value) {
isc_result_t result;
dns_qp_t *qp = NULL;
uint32_t size, pos, mask, count = 0;
dns_ntnode_t *old = NULL, *new = NULL;
REQUIRE(VALID_NAMETREE(nametree));
REQUIRE(name != NULL);
dns_qpmulti_write(nametree->table, &qp);
switch (nametree->type) {
case DNS_NAMETREE_BOOL:
new = newnode(nametree->mctx, name);
new->set = value;
break;
case DNS_NAMETREE_COUNT:
new = newnode(nametree->mctx, name);
new->set = true;
result = dns_qp_deletename(qp, name, (void **)&old, &count);
if (result == ISC_R_SUCCESS) {
count += 1;
}
break;
case DNS_NAMETREE_BITS:
result = dns_qp_getname(qp, name, (void **)&old, NULL);
if (result == ISC_R_SUCCESS && matchbit(old->bits, value)) {
goto out;
}
size = pos = value / 8 + 2;
mask = 1 << (value % 8);
if (old != NULL && old->bits[0] > pos) {
size = old->bits[0];
}
new = newnode(nametree->mctx, name);
new->bits = isc_mem_cget(nametree->mctx, size, sizeof(char));
if (result == ISC_R_SUCCESS) {
memmove(new->bits, old->bits, old->bits[0]);
result = dns_qp_deletename(qp, name, NULL, NULL);
INSIST(result == ISC_R_SUCCESS);
}
new->bits[pos - 1] |= mask;
new->bits[0] = size;
break;
default:
UNREACHABLE();
}
result = dns_qp_insert(qp, new, count);
/*
* We detach the node here, so any dns_qp_deletename() will
* destroy the node directly.
*/
dns_ntnode_detach(&new);
out:
dns_qp_compact(qp, DNS_QPGC_MAYBE);
dns_qpmulti_commit(nametree->table, &qp);
return (result);
}
isc_result_t
dns_nametree_delete(dns_nametree_t *nametree, const dns_name_t *name) {
isc_result_t result;
dns_qp_t *qp = NULL;
dns_ntnode_t *old = NULL;
uint32_t count;
REQUIRE(VALID_NAMETREE(nametree));
REQUIRE(name != NULL);
dns_qpmulti_write(nametree->table, &qp);
result = dns_qp_deletename(qp, name, (void **)&old, &count);
switch (nametree->type) {
case DNS_NAMETREE_BOOL:
case DNS_NAMETREE_BITS:
break;
case DNS_NAMETREE_COUNT:
if (result == ISC_R_SUCCESS && count-- != 0) {
dns_ntnode_t *new = newnode(nametree->mctx, name);
new->set = true;
result = dns_qp_insert(qp, new, count);
INSIST(result == ISC_R_SUCCESS);
dns_ntnode_detach(&new);
}
break;
default:
UNREACHABLE();
}
dns_qp_compact(qp, DNS_QPGC_MAYBE);
dns_qpmulti_commit(nametree->table, &qp);
return (result);
}
isc_result_t
dns_nametree_find(dns_nametree_t *nametree, const dns_name_t *name,
dns_ntnode_t **ntnodep) {
isc_result_t result;
dns_ntnode_t *node = NULL;
dns_qpread_t qpr;
REQUIRE(VALID_NAMETREE(nametree));
REQUIRE(name != NULL);
REQUIRE(ntnodep != NULL && *ntnodep == NULL);
dns_qpmulti_query(nametree->table, &qpr);
result = dns_qp_getname(&qpr, name, (void **)&node, NULL);
if (result == ISC_R_SUCCESS) {
dns_ntnode_attach(node, ntnodep);
}
dns_qpread_destroy(nametree->table, &qpr);
return (result);
}
bool
dns_nametree_covered(dns_nametree_t *nametree, const dns_name_t *name,
dns_name_t *found, uint32_t bit) {
isc_result_t result;
dns_qpread_t qpr;
dns_ntnode_t *node = NULL;
bool ret = false;
REQUIRE(VALID_NAMETREE(nametree));
dns_qpmulti_query(nametree->table, &qpr);
result = dns_qp_findname_ancestor(&qpr, name, 0, (void **)&node, NULL);
if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
if (found != NULL) {
dns_name_copy(node->name, found);
}
switch (nametree->type) {
case DNS_NAMETREE_BOOL:
ret = node->set;
break;
case DNS_NAMETREE_COUNT:
ret = true;
break;
case DNS_NAMETREE_BITS:
ret = matchbit(node->bits, bit);
break;
}
}
dns_qpread_destroy(nametree->table, &qpr);
return (ret);
}
static void
qp_attach(void *uctx ISC_ATTR_UNUSED, void *pval,
uint32_t ival ISC_ATTR_UNUSED) {
dns_ntnode_t *ntnode = pval;
dns_ntnode_ref(ntnode);
}
static void
qp_detach(void *uctx ISC_ATTR_UNUSED, void *pval,
uint32_t ival ISC_ATTR_UNUSED) {
dns_ntnode_t *ntnode = pval;
dns_ntnode_detach(&ntnode);
}
static size_t
qp_makekey(dns_qpkey_t key, void *uctx ISC_ATTR_UNUSED, void *pval,
uint32_t ival ISC_ATTR_UNUSED) {
dns_ntnode_t *ntnode = pval;
return (dns_qpkey_fromname(key, ntnode->name));
}
static void
qp_triename(void *uctx, char *buf, size_t size) {
dns_nametree_t *nametree = uctx;
snprintf(buf, size, "%s nametree", nametree->name);
}

View file

@ -52,6 +52,7 @@
#include <dns/log.h>
#include <dns/message.h>
#include <dns/name.h>
#include <dns/nametree.h>
#include <dns/ncache.h>
#include <dns/nsec.h>
#include <dns/nsec3.h>
@ -561,9 +562,9 @@ struct dns_resolver {
uint32_t lame_ttl;
ISC_LIST(alternate_t) alternates;
dns_rbt_t *algorithms;
dns_rbt_t *digests;
dns_rbt_t *mustbesecure;
dns_nametree_t *algorithms;
dns_nametree_t *digests;
dns_nametree_t *mustbesecure;
unsigned int spillatmax;
unsigned int spillatmin;
isc_timer_t *spillattimer;
@ -6751,15 +6752,8 @@ is_answeraddress_allowed(dns_view_t *view, dns_name_t *name,
* If the owner name matches one in the exclusion list, either
* exactly or partially, allow it.
*/
if (view->answeracl_exclude != NULL) {
dns_rbtnode_t *node = NULL;
result = dns_rbt_findnode(view->answeracl_exclude, name, NULL,
&node, NULL, 0, NULL, NULL);
if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
return (true);
}
if (dns_nametree_covered(view->answeracl_exclude, name, NULL, 0)) {
return (true);
}
/*
@ -6806,7 +6800,6 @@ static bool
is_answertarget_allowed(fetchctx_t *fctx, dns_name_t *qname, dns_name_t *rname,
dns_rdataset_t *rdataset, bool *chainingp) {
isc_result_t result;
dns_rbtnode_t *node = NULL;
dns_name_t *tname = NULL;
dns_rdata_cname_t cname;
dns_rdata_dname_t dname;
@ -6872,12 +6865,8 @@ is_answertarget_allowed(fetchctx_t *fctx, dns_name_t *qname, dns_name_t *rname,
* If the owner name matches one in the exclusion list, either
* exactly or partially, allow it.
*/
if (view->answernames_exclude != NULL) {
result = dns_rbt_findnode(view->answernames_exclude, qname,
NULL, &node, NULL, 0, NULL, NULL);
if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
return (true);
}
if (dns_nametree_covered(view->answernames_exclude, qname, NULL, 0)) {
return (true);
}
/*
@ -6896,9 +6885,7 @@ is_answertarget_allowed(fetchctx_t *fctx, dns_name_t *qname, dns_name_t *rname,
/*
* Otherwise, apply filters.
*/
result = dns_rbt_findnode(view->denyanswernames, tname, NULL, &node,
NULL, 0, NULL, NULL);
if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
if (dns_nametree_covered(view->denyanswernames, tname, NULL, 0)) {
char qnamebuf[DNS_NAME_FORMATSIZE];
char tnamebuf[DNS_NAME_FORMATSIZE];
char classbuf[64];
@ -9822,13 +9809,12 @@ dns_resolver__destroy(dns_resolver_t *res) {
REQUIRE(atomic_load_acquire(&res->nfctx) == 0);
/* These must be run before zeroing the magic number */
dns_resolver_reset_algorithms(res);
dns_resolver_reset_ds_digests(res);
dns_resolver_resetmustbesecure(res);
res->magic = 0;
dns_nametree_detach(&res->algorithms);
dns_nametree_detach(&res->digests);
dns_nametree_detach(&res->mustbesecure);
if (res->querystats != NULL) {
dns_stats_detach(&res->querystats);
}
@ -9968,6 +9954,13 @@ dns_resolver_create(dns_view_t *view, isc_loopmgr_t *loopmgr,
isc_mutex_init(&res->lock);
isc_mutex_init(&res->primelock);
dns_nametree_create(res->mctx, DNS_NAMETREE_BITS, "algorithms",
&res->algorithms);
dns_nametree_create(res->mctx, DNS_NAMETREE_BITS, "ds-digests",
&res->digests);
dns_nametree_create(res->mctx, DNS_NAMETREE_BOOL,
"dnssec-must-be-secure", &res->mustbesecure);
res->magic = RES_MAGIC;
*resp = res;
@ -10730,239 +10723,59 @@ dns_resolver_printbadcache(dns_resolver_t *resolver, FILE *fp) {
(void)dns_badcache_print(resolver->badcache, "Bad cache", fp);
}
static void
free_algorithm(void *node, void *arg) {
unsigned char *algorithms = node;
isc_mem_t *mctx = arg;
isc_mem_put(mctx, algorithms, *algorithms);
}
void
dns_resolver_reset_algorithms(dns_resolver_t *resolver) {
REQUIRE(VALID_RESOLVER(resolver));
if (resolver->algorithms != NULL) {
dns_rbt_destroy(&resolver->algorithms);
}
}
isc_result_t
dns_resolver_disable_algorithm(dns_resolver_t *resolver, const dns_name_t *name,
unsigned int alg) {
unsigned int len, mask;
isc_result_t result;
dns_rbtnode_t *node = NULL;
unsigned char *algorithms = NULL;
unsigned int algorithms_len;
/*
* Whether an algorithm is disabled (or not) is stored in a
* per-name bitfield that is stored as the node data of an
* RBT.
*/
REQUIRE(VALID_RESOLVER(resolver));
if (alg > 255) {
return (ISC_R_RANGE);
}
if (resolver->algorithms == NULL) {
result = dns_rbt_create(resolver->mctx, free_algorithm,
resolver->mctx, &resolver->algorithms);
if (result != ISC_R_SUCCESS) {
return (result);
}
return (dns_nametree_add(resolver->algorithms, name, alg));
}
isc_result_t
dns_resolver_disable_ds_digest(dns_resolver_t *resolver, const dns_name_t *name,
unsigned int digest_type) {
REQUIRE(VALID_RESOLVER(resolver));
if (digest_type > 255) {
return (ISC_R_RANGE);
}
len = alg / 8 + 2;
mask = 1 << (alg % 8);
result = dns_rbt_addnode(resolver->algorithms, name, &node);
if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS) {
return (result);
}
/* If algorithms is set, algorithms[0] contains its length. */
algorithms = node->data;
algorithms_len = (algorithms) ? algorithms[0] : 0;
if (algorithms == NULL || len > algorithms_len) {
INSIST(len > 0);
/*
* If no bitfield exists in the node data, or if
* it is not long enough, allocate a new
* bitfield and copy the old (smaller) bitfield
* into it if one exists.
*/
node->data = algorithms =
isc_mem_creget(resolver->mctx, algorithms,
algorithms_len, len, sizeof(char));
/* store the new length */
algorithms[0] = len;
}
algorithms[len - 1] |= mask;
return (ISC_R_SUCCESS);
return (dns_nametree_add(resolver->digests, name, digest_type));
}
bool
dns_resolver_algorithm_supported(dns_resolver_t *resolver,
const dns_name_t *name, unsigned int alg) {
unsigned int len, mask;
unsigned char *algorithms;
void *data = NULL;
isc_result_t result;
bool found = false;
REQUIRE(VALID_RESOLVER(resolver));
if ((alg == DST_ALG_DH) || (alg == DST_ALG_INDIRECT)) {
return (false);
}
if (resolver->algorithms == NULL) {
goto unlock;
}
result = dns_rbt_findname(resolver->algorithms, name, 0, NULL, &data);
if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
len = alg / 8 + 2;
mask = 1 << (alg % 8);
algorithms = data;
if (len <= *algorithms && (algorithms[len - 1] & mask) != 0) {
found = true;
}
}
unlock:
if (found) {
if (dns_nametree_covered(resolver->algorithms, name, NULL, alg)) {
return (false);
}
return (dst_algorithm_supported(alg));
}
static void
free_digest(void *node, void *arg) {
unsigned char *digests = node;
isc_mem_t *mctx = arg;
isc_mem_put(mctx, digests, *digests);
}
void
dns_resolver_reset_ds_digests(dns_resolver_t *resolver) {
REQUIRE(VALID_RESOLVER(resolver));
if (resolver->digests != NULL) {
dns_rbt_destroy(&resolver->digests);
}
}
isc_result_t
dns_resolver_disable_ds_digest(dns_resolver_t *resolver, const dns_name_t *name,
unsigned int digest_type) {
unsigned int len, mask;
isc_result_t result;
dns_rbtnode_t *node = NULL;
/*
* Whether a digest is disabled (or not) is stored in a per-name
* bitfield that is stored as the node data of an RBT.
*/
REQUIRE(VALID_RESOLVER(resolver));
if (digest_type > 255) {
return (ISC_R_RANGE);
}
if (resolver->digests == NULL) {
result = dns_rbt_create(resolver->mctx, free_digest,
resolver->mctx, &resolver->digests);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
}
len = digest_type / 8 + 2;
mask = 1 << (digest_type % 8);
result = dns_rbt_addnode(resolver->digests, name, &node);
if (result == ISC_R_SUCCESS || result == ISC_R_EXISTS) {
unsigned char *digests = node->data;
/* If digests is set, digests[0] contains its length. */
if (digests == NULL || len > *digests) {
/*
* If no bitfield exists in the node data, or if
* it is not long enough, allocate a new
* bitfield and copy the old (smaller) bitfield
* into it if one exists.
*/
unsigned char *tmp = isc_mem_cget(resolver->mctx, 1,
len);
if (digests != NULL) {
memmove(tmp, digests, *digests);
}
tmp[len - 1] |= mask;
/* tmp[0] should contain the length of 'tmp'. */
*tmp = len;
node->data = tmp;
/* Free the older bitfield. */
if (digests != NULL) {
isc_mem_put(resolver->mctx, digests, *digests);
}
} else {
digests[len - 1] |= mask;
}
}
result = ISC_R_SUCCESS;
cleanup:
return (result);
}
bool
dns_resolver_ds_digest_supported(dns_resolver_t *resolver,
const dns_name_t *name,
unsigned int digest_type) {
unsigned int len, mask;
unsigned char *digests;
void *data = NULL;
isc_result_t result;
bool found = false;
REQUIRE(VALID_RESOLVER(resolver));
if (resolver->digests == NULL) {
goto unlock;
}
result = dns_rbt_findname(resolver->digests, name, 0, NULL, &data);
if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
len = digest_type / 8 + 2;
mask = 1 << (digest_type % 8);
digests = data;
if (len <= *digests && (digests[len - 1] & mask) != 0) {
found = true;
}
}
unlock:
if (found) {
if (dns_nametree_covered(resolver->digests, name, NULL, digest_type)) {
return (false);
}
return (dst_ds_digest_supported(digest_type));
}
void
dns_resolver_resetmustbesecure(dns_resolver_t *resolver) {
REQUIRE(VALID_RESOLVER(resolver));
if (resolver->mustbesecure != NULL) {
dns_rbt_destroy(&resolver->mustbesecure);
}
}
static bool yes = true, no = false;
isc_result_t
dns_resolver_setmustbesecure(dns_resolver_t *resolver, const dns_name_t *name,
bool value) {
@ -10970,36 +10783,15 @@ dns_resolver_setmustbesecure(dns_resolver_t *resolver, const dns_name_t *name,
REQUIRE(VALID_RESOLVER(resolver));
if (resolver->mustbesecure == NULL) {
result = dns_rbt_create(resolver->mctx, NULL, NULL,
&resolver->mustbesecure);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
}
result = dns_rbt_addname(resolver->mustbesecure, name,
value ? &yes : &no);
cleanup:
result = dns_nametree_add(resolver->mustbesecure, name, value);
return (result);
}
bool
dns_resolver_getmustbesecure(dns_resolver_t *resolver, const dns_name_t *name) {
void *data = NULL;
bool value = false;
isc_result_t result;
REQUIRE(VALID_RESOLVER(resolver));
if (resolver->mustbesecure == NULL) {
goto unlock;
}
result = dns_rbt_findname(resolver->mustbesecure, name, 0, NULL, &data);
if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
value = *(bool *)data;
}
unlock:
return (value);
return (dns_nametree_covered(resolver->mustbesecure, name, NULL, 0));
}
void

View file

@ -13,6 +13,7 @@
#include <inttypes.h>
#include <isc/hashmap.h>
#include <isc/list.h>
#include <isc/mem.h>
#include <isc/netaddr.h>
@ -22,8 +23,8 @@
#include <isc/sockaddr.h>
#include <isc/util.h>
#include <dns/fixedname.h>
#include <dns/name.h>
#include <dns/rbt.h>
#include <dns/transport.h>
#define TRANSPORT_MAGIC ISC_MAGIC('T', 'r', 'n', 's')
@ -37,7 +38,7 @@ struct dns_transport_list {
isc_refcount_t references;
isc_mem_t *mctx;
isc_rwlock_t lock;
dns_rbt_t *transports[DNS_TRANSPORT_COUNT];
isc_hashmap_t *transports[DNS_TRANSPORT_COUNT];
};
typedef enum ternary { ter_none = 0, ter_true = 1, ter_false = 2 } ternary_t;
@ -47,6 +48,8 @@ struct dns_transport {
isc_refcount_t references;
isc_mem_t *mctx;
dns_transport_type_t type;
dns_fixedname_t fn;
dns_name_t *name;
struct {
char *tlsname;
char *certfile;
@ -64,29 +67,20 @@ struct dns_transport {
} doh;
};
static void
free_dns_transport(void *node, void *arg) {
dns_transport_t *transport = node;
REQUIRE(node != NULL);
UNUSED(arg);
dns_transport_detach(&transport);
}
static isc_result_t
list_add(dns_transport_list_t *list, const dns_name_t *name,
const dns_transport_type_t type, dns_transport_t *transport) {
isc_result_t result;
dns_rbt_t *rbt = NULL;
isc_hashmap_t *hm = NULL;
RWLOCK(&list->lock, isc_rwlocktype_write);
rbt = list->transports[type];
INSIST(rbt != NULL);
result = dns_rbt_addname(rbt, name, transport);
hm = list->transports[type];
INSIST(hm != NULL);
transport->name = dns_fixedname_initname(&transport->fn);
dns_name_copy(name, transport->name);
result = isc_hashmap_add(hm, NULL, transport->name->ndata,
transport->name->length, transport);
RWUNLOCK(&list->lock, isc_rwlocktype_write);
return (result);
@ -629,15 +623,16 @@ dns_transport_find(const dns_transport_type_t type, const dns_name_t *name,
dns_transport_list_t *list) {
isc_result_t result;
dns_transport_t *transport = NULL;
dns_rbt_t *rbt = NULL;
isc_hashmap_t *hm = NULL;
REQUIRE(VALID_TRANSPORT_LIST(list));
REQUIRE(list->transports[type] != NULL);
rbt = list->transports[type];
hm = list->transports[type];
RWLOCK(&list->lock, isc_rwlocktype_read);
result = dns_rbt_findname(rbt, name, 0, NULL, (void *)&transport);
result = isc_hashmap_find(hm, NULL, name->ndata, name->length,
(void **)&transport);
if (result == ISC_R_SUCCESS) {
isc_refcount_increment(&transport->references);
}
@ -660,10 +655,8 @@ dns_transport_list_new(isc_mem_t *mctx) {
list->magic = TRANSPORT_LIST_MAGIC;
for (size_t type = 0; type < DNS_TRANSPORT_COUNT; type++) {
isc_result_t result;
result = dns_rbt_create(list->mctx, free_dns_transport, NULL,
&list->transports[type]);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
isc_hashmap_create(list->mctx, 10, ISC_HASHMAP_CASE_INSENSITIVE,
&list->transports[type]);
}
return (list);
@ -686,9 +679,24 @@ transport_list_destroy(dns_transport_list_t *list) {
list->magic = 0;
for (size_t type = 0; type < DNS_TRANSPORT_COUNT; type++) {
if (list->transports[type] != NULL) {
dns_rbt_destroy(&list->transports[type]);
isc_result_t result;
isc_hashmap_iter_t *it = NULL;
if (list->transports[type] == NULL) {
continue;
}
isc_hashmap_iter_create(list->transports[type], &it);
for (result = isc_hashmap_iter_first(it);
result == ISC_R_SUCCESS;
result = isc_hashmap_iter_delcurrent_next(it))
{
dns_transport_t *transport = NULL;
isc_hashmap_iter_current(it, (void **)&transport);
dns_transport_detach(&transport);
}
isc_hashmap_iter_destroy(&it);
isc_hashmap_destroy(&list->transports[type]);
}
isc_rwlock_destroy(&list->lock);
isc_mem_putanddetach(&list->mctx, list, sizeof(*list));

View file

@ -47,6 +47,7 @@
#include <dns/keyvalues.h>
#include <dns/master.h>
#include <dns/masterdump.h>
#include <dns/nametree.h>
#include <dns/nta.h>
#include <dns/order.h>
#include <dns/peer.h>
@ -136,8 +137,6 @@ dns_view_create(isc_mem_t *mctx, dns_dispatchmgr_t *dispatchmgr,
isc_mutex_init(&view->lock);
isc_rwlock_init(&view->sfd_lock);
dns_zt_create(mctx, view, &view->zonetable);
dns_fwdtable_create(mctx, view, &view->fwdtable);
@ -163,6 +162,8 @@ dns_view_create(isc_mem_t *mctx, dns_dispatchmgr_t *dispatchmgr,
goto cleanup_peerlist;
}
dns_nametree_create(view->mctx, DNS_NAMETREE_COUNT, "sfd", &view->sfd);
view->magic = DNS_VIEW_MAGIC;
*viewp = view;
@ -195,7 +196,6 @@ cleanup_new_zone_lock:
dns_fwdtable_destroy(&view->fwdtable);
dns_zt_detach(&view->zonetable);
isc_rwlock_destroy(&view->sfd_lock);
isc_mutex_destroy(&view->lock);
if (view->nta_file != NULL) {
@ -348,16 +348,16 @@ destroy(dns_view_t *view) {
dns_acl_detach(&view->pad_acl);
}
if (view->answeracl_exclude != NULL) {
dns_rbt_destroy(&view->answeracl_exclude);
dns_nametree_detach(&view->answeracl_exclude);
}
if (view->denyanswernames != NULL) {
dns_rbt_destroy(&view->denyanswernames);
dns_nametree_detach(&view->denyanswernames);
}
if (view->answernames_exclude != NULL) {
dns_rbt_destroy(&view->answernames_exclude);
dns_nametree_detach(&view->answernames_exclude);
}
if (view->sfd != NULL) {
dns_rbt_destroy(&view->sfd);
dns_nametree_detach(&view->sfd);
}
if (view->secroots_priv != NULL) {
dns_keytable_detach(&view->secroots_priv);
@ -407,7 +407,6 @@ destroy(dns_view_t *view) {
dns_badcache_destroy(&view->failcache);
}
isc_mutex_destroy(&view->new_zone_lock);
isc_rwlock_destroy(&view->sfd_lock);
isc_mutex_destroy(&view->lock);
isc_refcount_destroy(&view->references);
isc_refcount_destroy(&view->weakrefs);
@ -2317,58 +2316,21 @@ dns_view_flushonshutdown(dns_view_t *view, bool flush) {
view->flush = flush;
}
static void
free_sfd(void *data, void *arg) {
isc_mem_put(arg, data, sizeof(unsigned int));
}
void
dns_view_sfd_add(dns_view_t *view, const dns_name_t *name) {
isc_result_t result;
dns_rbtnode_t *node = NULL;
REQUIRE(DNS_VIEW_VALID(view));
RWLOCK(&view->sfd_lock, isc_rwlocktype_write);
if (view->sfd == NULL) {
result = dns_rbt_create(view->mctx, free_sfd, view->mctx,
&view->sfd);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
}
result = dns_rbt_addnode(view->sfd, name, &node);
RUNTIME_CHECK(result == ISC_R_SUCCESS || result == ISC_R_EXISTS);
if (node->data != NULL) {
unsigned int *count = node->data;
(*count)++;
} else {
unsigned int *count = isc_mem_get(view->mctx,
sizeof(unsigned int));
*count = 1;
node->data = count;
}
RWUNLOCK(&view->sfd_lock, isc_rwlocktype_write);
result = dns_nametree_add(view->sfd, name, 0);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
}
void
dns_view_sfd_del(dns_view_t *view, const dns_name_t *name) {
isc_result_t result;
void *data = NULL;
REQUIRE(DNS_VIEW_VALID(view));
RWLOCK(&view->sfd_lock, isc_rwlocktype_write);
INSIST(view->sfd != NULL);
result = dns_rbt_findname(view->sfd, name, 0, NULL, &data);
if (result == ISC_R_SUCCESS) {
unsigned int *count = data;
INSIST(count != NULL);
if (--(*count) == 0U) {
result = dns_rbt_deletename(view->sfd, name, false);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
}
}
RWUNLOCK(&view->sfd_lock, isc_rwlocktype_write);
dns_nametree_delete(view->sfd, name);
}
void
@ -2376,17 +2338,7 @@ dns_view_sfd_find(dns_view_t *view, const dns_name_t *name,
dns_name_t *foundname) {
REQUIRE(DNS_VIEW_VALID(view));
if (view->sfd != NULL) {
isc_result_t result;
void *data = NULL;
RWLOCK(&view->sfd_lock, isc_rwlocktype_read);
result = dns_rbt_findname(view->sfd, name, 0, foundname, &data);
RWUNLOCK(&view->sfd_lock, isc_rwlocktype_read);
if (result != ISC_R_SUCCESS && result != DNS_R_PARTIALMATCH) {
dns_name_copy(dns_rootname, foundname);
}
} else {
if (!dns_nametree_covered(view->sfd, name, foundname, 0)) {
dns_name_copy(dns_rootname, foundname);
}
}

View file

@ -29,6 +29,7 @@ check_PROGRAMS = \
dst_test \
keytable_test \
name_test \
nametree_test \
nsec3_test \
nsec3param_test \
private_test \

330
tests/dns/nametree_test.c Normal file
View file

@ -0,0 +1,330 @@
/*
* 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 <inttypes.h>
#include <sched.h> /* IWYU pragma: keep */
#include <setjmp.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define UNIT_TESTING
#include <cmocka.h>
#include <isc/base64.h>
#include <isc/buffer.h>
#include <isc/md.h>
#include <isc/util.h>
#include <dns/fixedname.h>
#include <dns/name.h>
#include <dns/nametree.h>
#include <dst/dst.h>
#include <tests/dns.h>
dns_nametree_t *booltree = NULL;
dns_nametree_t *bitstree = NULL;
dns_nametree_t *counttree = NULL;
/*
* Test utilities. In general, these assume input parameters are valid
* (checking with assert_int_equal, thus aborting if not) and unlikely run time
* errors (such as memory allocation failure) won't happen. This helps keep
* the test code concise.
*/
/* Common setup: create trees of each type with a few keys */
static int
setup(void **state ISC_ATTR_UNUSED) {
dns_fixedname_t fn;
dns_name_t *name = dns_fixedname_name(&fn);
dns_nametree_create(mctx, DNS_NAMETREE_BOOL, "bool test", &booltree);
dns_nametree_create(mctx, DNS_NAMETREE_BITS, "bits test", &bitstree);
dns_nametree_create(mctx, DNS_NAMETREE_COUNT, "count test", &counttree);
/* Add a positive boolean node */
dns_test_namefromstring("example.com.", &fn);
assert_int_equal(dns_nametree_add(booltree, name, true), ISC_R_SUCCESS);
/* Add assorted bits to a bitfield node */
assert_int_equal(dns_nametree_add(bitstree, name, 1), ISC_R_SUCCESS);
assert_int_equal(dns_nametree_add(bitstree, name, 9), ISC_R_SUCCESS);
assert_int_equal(dns_nametree_add(bitstree, name, 53), ISC_R_SUCCESS);
/* Add negative boolean nodes with and without parents */
dns_test_namefromstring("negative.example.com.", &fn);
assert_int_equal(dns_nametree_add(booltree, name, false),
ISC_R_SUCCESS);
dns_test_namefromstring("negative.example.org.", &fn);
assert_int_equal(dns_nametree_add(booltree, name, false),
ISC_R_SUCCESS);
/* Add a bitfield node under a parent */
dns_test_namefromstring("sub.example.com.", &fn);
assert_int_equal(dns_nametree_add(bitstree, name, 2), ISC_R_SUCCESS);
return (0);
}
static int
teardown(void **state ISC_ATTR_UNUSED) {
dns_nametree_detach(&booltree);
dns_nametree_detach(&bitstree);
dns_nametree_detach(&counttree);
rcu_barrier();
return (0);
}
ISC_RUN_TEST_IMPL(add_bool) {
dns_ntnode_t *node = NULL;
dns_fixedname_t fn;
dns_name_t *name = dns_fixedname_name(&fn);
/*
* Getting the node for example.com should succeed.
*/
dns_test_namefromstring("example.com.", &fn);
assert_int_equal(dns_nametree_find(booltree, name, &node),
ISC_R_SUCCESS);
dns_ntnode_detach(&node);
/*
* Try to add the same name. This should fail.
*/
assert_int_equal(dns_nametree_add(booltree, name, false), ISC_R_EXISTS);
assert_int_equal(dns_nametree_find(booltree, name, &node),
ISC_R_SUCCESS);
dns_ntnode_detach(&node);
/*
* Try to add a new name.
*/
dns_test_namefromstring("newname.com.", &fn);
assert_int_equal(dns_nametree_add(booltree, name, true), ISC_R_SUCCESS);
assert_int_equal(dns_nametree_find(booltree, name, &node),
ISC_R_SUCCESS);
dns_ntnode_detach(&node);
}
ISC_RUN_TEST_IMPL(add_bits) {
dns_ntnode_t *node = NULL;
dns_fixedname_t fn;
dns_name_t *name = dns_fixedname_name(&fn);
/*
* Getting the node for example.com should succeed.
*/
dns_test_namefromstring("example.com.", &fn);
assert_int_equal(dns_nametree_find(booltree, name, &node),
ISC_R_SUCCESS);
dns_ntnode_detach(&node);
/*
* Try to add the same name. This should succeed.
*/
assert_int_equal(dns_nametree_add(bitstree, name, 1), ISC_R_SUCCESS);
assert_int_equal(dns_nametree_add(bitstree, name, 2), ISC_R_SUCCESS);
assert_int_equal(dns_nametree_add(bitstree, name, 3), ISC_R_SUCCESS);
assert_int_equal(dns_nametree_find(booltree, name, &node),
ISC_R_SUCCESS);
dns_ntnode_detach(&node);
/*
* Try to add a new name.
*/
dns_test_namefromstring("newname.com.", &fn);
assert_int_equal(dns_nametree_add(booltree, name, true), ISC_R_SUCCESS);
assert_int_equal(dns_nametree_find(booltree, name, &node),
ISC_R_SUCCESS);
dns_ntnode_detach(&node);
}
ISC_RUN_TEST_IMPL(add_count) {
dns_fixedname_t fn;
dns_name_t *name = dns_fixedname_name(&fn);
/* add a counter node five times */
dns_test_namefromstring("example.com.", &fn);
assert_int_equal(dns_nametree_add(counttree, name, 0), ISC_R_SUCCESS);
assert_int_equal(dns_nametree_add(counttree, name, 0), ISC_R_SUCCESS);
assert_int_equal(dns_nametree_add(counttree, name, 0), ISC_R_SUCCESS);
assert_int_equal(dns_nametree_add(counttree, name, 0), ISC_R_SUCCESS);
assert_int_equal(dns_nametree_add(counttree, name, 0), ISC_R_SUCCESS);
/* delete it five times, checking coverage each time */
assert_true(dns_nametree_covered(counttree, name, NULL, 0));
assert_int_equal(dns_nametree_delete(counttree, name), ISC_R_SUCCESS);
assert_true(dns_nametree_covered(counttree, name, NULL, 0));
assert_int_equal(dns_nametree_delete(counttree, name), ISC_R_SUCCESS);
assert_true(dns_nametree_covered(counttree, name, NULL, 0));
assert_int_equal(dns_nametree_delete(counttree, name), ISC_R_SUCCESS);
assert_true(dns_nametree_covered(counttree, name, NULL, 0));
assert_int_equal(dns_nametree_delete(counttree, name), ISC_R_SUCCESS);
assert_true(dns_nametree_covered(counttree, name, NULL, 0));
assert_int_equal(dns_nametree_delete(counttree, name), ISC_R_SUCCESS);
assert_false(dns_nametree_covered(counttree, name, NULL, 0));
assert_int_equal(dns_nametree_delete(counttree, name), ISC_R_NOTFOUND);
}
ISC_RUN_TEST_IMPL(covered_bool) {
dns_fixedname_t fn, fn2;
dns_name_t *name = dns_fixedname_initname(&fn);
dns_name_t *found = dns_fixedname_initname(&fn2);
char buf[DNS_NAME_FORMATSIZE];
const char *yesnames[] = { "example.com.", "sub.example.com.", NULL };
const char *nonames[] = { "whatever.com.", "negative.example.com.",
"example.org.", "negative.example.org.",
NULL };
for (const char **n = yesnames; *n != NULL; n++) {
dns_test_namefromstring(*n, &fn);
assert_true(dns_nametree_covered(booltree, name, NULL, 0));
}
for (const char **n = nonames; *n != NULL; n++) {
dns_test_namefromstring(*n, &fn);
assert_false(dns_nametree_covered(booltree, name, NULL, 0));
}
/* Check that the found name is as expected */
dns_test_namefromstring("other.example.com.", &fn);
assert_true(dns_nametree_covered(booltree, name, found, 0));
dns_name_format(found, buf, sizeof(buf));
assert_string_equal(buf, "example.com");
}
ISC_RUN_TEST_IMPL(covered_bits) {
dns_fixedname_t fn;
dns_name_t *name = dns_fixedname_name(&fn);
/* check existing bit values */
dns_test_namefromstring("example.com.", &fn);
assert_false(dns_nametree_covered(bitstree, name, NULL, 0));
assert_true(dns_nametree_covered(bitstree, name, NULL, 1));
assert_false(dns_nametree_covered(bitstree, name, NULL, 2));
assert_false(dns_nametree_covered(bitstree, name, NULL, 3));
assert_true(dns_nametree_covered(bitstree, name, NULL, 9));
assert_true(dns_nametree_covered(bitstree, name, NULL, 53));
assert_false(dns_nametree_covered(bitstree, name, NULL, 288));
/* add a small bit value, test again */
assert_int_equal(dns_nametree_add(bitstree, name, 3), ISC_R_SUCCESS);
assert_true(dns_nametree_covered(bitstree, name, NULL, 3));
/* add a large bit value, test again */
assert_false(dns_nametree_covered(bitstree, name, NULL, 615));
assert_int_equal(dns_nametree_add(bitstree, name, 615), ISC_R_SUCCESS);
assert_true(dns_nametree_covered(bitstree, name, NULL, 615));
assert_int_equal(dns_nametree_add(bitstree, name, 999), ISC_R_SUCCESS);
assert_true(dns_nametree_covered(bitstree, name, NULL, 999));
assert_false(dns_nametree_covered(bitstree, name, NULL, 998));
/* check existing bit values for subdomain */
dns_test_namefromstring("sub.example.com.", &fn);
assert_false(dns_nametree_covered(bitstree, name, NULL, 0));
assert_false(dns_nametree_covered(bitstree, name, NULL, 1));
assert_true(dns_nametree_covered(bitstree, name, NULL, 2));
assert_false(dns_nametree_covered(bitstree, name, NULL, 3));
assert_false(dns_nametree_covered(bitstree, name, NULL, 9));
assert_false(dns_nametree_covered(bitstree, name, NULL, 53));
assert_false(dns_nametree_covered(bitstree, name, NULL, 288));
/* check nonexistent subdomain is all false */
dns_test_namefromstring("other.example.com", &fn);
assert_false(dns_nametree_covered(bitstree, name, NULL, 0));
assert_false(dns_nametree_covered(bitstree, name, NULL, 1));
assert_false(dns_nametree_covered(bitstree, name, NULL, 2));
assert_false(dns_nametree_covered(bitstree, name, NULL, 3));
assert_false(dns_nametree_covered(bitstree, name, NULL, 9));
assert_false(dns_nametree_covered(bitstree, name, NULL, 53));
assert_false(dns_nametree_covered(bitstree, name, NULL, 288));
/* check nonexistent domain is all false */
dns_test_namefromstring("anyname.", &fn);
assert_false(dns_nametree_covered(bitstree, name, NULL, 0));
assert_false(dns_nametree_covered(bitstree, name, NULL, 1));
assert_false(dns_nametree_covered(bitstree, name, NULL, 2));
assert_false(dns_nametree_covered(bitstree, name, NULL, 3));
assert_false(dns_nametree_covered(bitstree, name, NULL, 9));
assert_false(dns_nametree_covered(bitstree, name, NULL, 53));
assert_false(dns_nametree_covered(bitstree, name, NULL, 288));
}
ISC_RUN_TEST_IMPL(delete) {
dns_fixedname_t fn;
dns_name_t *name = dns_fixedname_name(&fn);
/* name doesn't match */
dns_test_namefromstring("example.org.", &fn);
assert_int_equal(dns_nametree_delete(booltree, name), ISC_R_NOTFOUND);
/* subdomain match is the same as no match */
dns_test_namefromstring("sub.example.org.", &fn);
assert_int_equal(dns_nametree_delete(booltree, name), ISC_R_NOTFOUND);
/*
* delete requires exact match: this should return SUCCESS on
* the first try, then NOTFOUND on the second even though an
* ancestor does exist.
*/
dns_test_namefromstring("negative.example.com.", &fn);
assert_int_equal(dns_nametree_delete(booltree, name), ISC_R_SUCCESS);
assert_int_equal(dns_nametree_delete(booltree, name), ISC_R_NOTFOUND);
dns_test_namefromstring("negative.example.org.", &fn);
assert_int_equal(dns_nametree_delete(booltree, name), ISC_R_SUCCESS);
assert_int_equal(dns_nametree_delete(booltree, name), ISC_R_NOTFOUND);
}
ISC_RUN_TEST_IMPL(find) {
dns_ntnode_t *node = NULL;
dns_fixedname_t fn;
dns_name_t *name = dns_fixedname_name(&fn);
/*
* dns_nametree_find() requires exact name match. It matches node
* that has a null key, too.
*/
dns_test_namefromstring("example.org.", &fn);
assert_int_equal(dns_nametree_find(booltree, name, &node),
ISC_R_NOTFOUND);
dns_test_namefromstring("sub.example.com.", &fn);
assert_int_equal(dns_nametree_find(booltree, name, &node),
ISC_R_NOTFOUND);
dns_test_namefromstring("example.com.", &fn);
assert_int_equal(dns_nametree_find(booltree, name, &node),
ISC_R_SUCCESS);
dns_ntnode_detach(&node);
}
ISC_TEST_LIST_START
ISC_TEST_ENTRY_CUSTOM(add_bool, setup, teardown)
ISC_TEST_ENTRY_CUSTOM(add_bits, setup, teardown)
ISC_TEST_ENTRY_CUSTOM(add_count, setup, teardown)
ISC_TEST_ENTRY_CUSTOM(covered_bool, setup, teardown)
ISC_TEST_ENTRY_CUSTOM(covered_bits, setup, teardown)
ISC_TEST_ENTRY_CUSTOM(delete, setup, teardown)
ISC_TEST_ENTRY_CUSTOM(find, setup, teardown)
ISC_TEST_LIST_END
ISC_TEST_MAIN