diff --git a/lib/dns/include/dns/nametree.h b/lib/dns/include/dns/nametree.h index acd8bbd21f..bae8dcefb3 100644 --- a/lib/dns/include/dns/nametree.h +++ b/lib/dns/include/dns/nametree.h @@ -40,7 +40,11 @@ /* Define to 1 for detailed reference tracing */ #undef DNS_NAMETREE_TRACE -typedef enum { DNS_NAMETREE_BOOL, DNS_NAMETREE_BITS } dns_nametree_type_t; +typedef enum { + DNS_NAMETREE_BOOL, + DNS_NAMETREE_BITS, + DNS_NAMETREE_COUNT +} dns_nametree_type_t; ISC_LANG_BEGINDECLS @@ -54,7 +58,8 @@ dns_nametree_create(isc_mem_t *mctx, dns_nametree_type_t type, const char *name, * for debugging purposes. * * 'type' indicates whether the tree will be used for storing boolean - * values (DNS_NAMETREE_BOOL) or bitfields (DNS_NAMETREE_BITS). + * values (DNS_NAMETREE_BOOL), bitfields (DNS_NAMETREE_BITS), or counters + * (DNS_NAMETREE_COUNT). * * Requires: * @@ -72,6 +77,12 @@ dns_nametree_add(dns_nametree_t *nametree, const dns_name_t *name, * 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 @@ -96,6 +107,10 @@ 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. diff --git a/lib/dns/nametree.c b/lib/dns/nametree.c index b148dd7565..bbca177aea 100644 --- a/lib/dns/nametree.c +++ b/lib/dns/nametree.c @@ -67,7 +67,8 @@ static void destroy_ntnode(dns_ntnode_t *node) { isc_refcount_destroy(&node->references); if (node->bits != NULL) { - isc_mem_cput(node->mctx, node->bits, 8, sizeof(uint32_t)); + isc_mem_cput(node->mctx, node->bits, node->bits[0], + sizeof(char)); } isc_mem_putanddetach(&node->mctx, node, sizeof(dns_ntnode_t)); } @@ -103,8 +104,20 @@ dns_nametree_create(isc_mem_t *mctx, dns_nametree_type_t type, const char *name, 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); @@ -132,10 +145,10 @@ newnode(isc_mem_t *mctx, const dns_name_t *name) { static bool matchbit(unsigned char *bits, uint32_t val) { - unsigned int len = val / 8; + unsigned int len = val / 8 + 2; unsigned int mask = 1 << (val % 8); - if ((bits[len] & mask) != 0) { + if (len <= bits[0] && (bits[len - 1] & mask) != 0) { return (true); } return (false); @@ -146,7 +159,7 @@ dns_nametree_add(dns_nametree_t *nametree, const dns_name_t *name, uint32_t value) { isc_result_t result; dns_qp_t *qp = NULL; - unsigned int len, mask; + uint32_t size, pos, mask, count = 0; dns_ntnode_t *old = NULL, *new = NULL; REQUIRE(VALID_NAMETREE(nametree)); @@ -160,32 +173,44 @@ dns_nametree_add(dns_nametree_t *nametree, const dns_name_t *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; } - len = value / 8; + 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, 8, sizeof(value)); + new->bits = isc_mem_cget(nametree->mctx, size, sizeof(char)); if (result == ISC_R_SUCCESS) { - INSIST(old != NULL); memmove(new->bits, old->bits, old->bits[0]); result = dns_qp_deletename(qp, name, NULL, NULL); INSIST(result == ISC_R_SUCCESS); } - new->bits[len] |= mask; + new->bits[pos - 1] |= mask; + new->bits[0] = size; break; default: UNREACHABLE(); } - result = dns_qp_insert(qp, new, 0); - + result = dns_qp_insert(qp, new, count); /* * We detach the node here, so any dns_qp_deletename() will * destroy the node directly. @@ -202,16 +227,30 @@ isc_result_t dns_nametree_delete(dns_nametree_t *nametree, const dns_name_t *name) { isc_result_t result; dns_qp_t *qp = NULL; - void *pval = 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, &pval, NULL); - if (result == ISC_R_SUCCESS) { - dns_ntnode_t *n = pval; - dns_ntnode_detach(&n); + 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); @@ -223,18 +262,17 @@ 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; - void *pval = NULL; REQUIRE(VALID_NAMETREE(nametree)); REQUIRE(name != NULL); REQUIRE(ntnodep != NULL && *ntnodep == NULL); dns_qpmulti_query(nametree->table, &qpr); - result = dns_qp_getname(&qpr, name, &pval, NULL); + result = dns_qp_getname(&qpr, name, (void **)&node, NULL); if (result == ISC_R_SUCCESS) { - dns_ntnode_t *knode = pval; - dns_ntnode_attach(knode, ntnodep); + dns_ntnode_attach(node, ntnodep); } dns_qpread_destroy(nametree->table, &qpr); @@ -258,10 +296,16 @@ dns_nametree_covered(dns_nametree_t *nametree, const dns_name_t *name, 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 (nametree->type == DNS_NAMETREE_BOOL) { + switch (nametree->type) { + case DNS_NAMETREE_BOOL: ret = node->set; - } else { + break; + case DNS_NAMETREE_COUNT: + ret = true; + break; + case DNS_NAMETREE_BITS: ret = matchbit(node->bits, bit); + break; } } diff --git a/tests/dns/nametree_test.c b/tests/dns/nametree_test.c index 840d3656e1..23df32f7e6 100644 --- a/tests/dns/nametree_test.c +++ b/tests/dns/nametree_test.c @@ -39,6 +39,7 @@ 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 @@ -55,6 +56,7 @@ create_tables(void) { 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); @@ -73,7 +75,7 @@ create_tables(void) { assert_int_equal(dns_nametree_add(booltree, name, false), ISC_R_SUCCESS); - /* Add a bitfield nodes under a parent */ + /* 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); } @@ -86,6 +88,9 @@ destroy_tables(void) { if (bitstree != NULL) { dns_nametree_detach(&bitstree); } + if (counttree != NULL) { + dns_nametree_detach(&counttree); + } rcu_barrier(); } @@ -161,6 +166,42 @@ ISC_RUN_TEST_IMPL(add_bits) { destroy_tables(); } +ISC_RUN_TEST_IMPL(add_count) { + dns_fixedname_t fn; + dns_name_t *name = dns_fixedname_name(&fn); + + create_tables(); + + /* 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, 0)); + assert_int_equal(dns_nametree_delete(counttree, name), ISC_R_SUCCESS); + + assert_true(dns_nametree_covered(counttree, name, 0)); + assert_int_equal(dns_nametree_delete(counttree, name), ISC_R_SUCCESS); + + assert_true(dns_nametree_covered(counttree, name, 0)); + assert_int_equal(dns_nametree_delete(counttree, name), ISC_R_SUCCESS); + + assert_true(dns_nametree_covered(counttree, name, 0)); + assert_int_equal(dns_nametree_delete(counttree, name), ISC_R_SUCCESS); + + assert_true(dns_nametree_covered(counttree, name, 0)); + assert_int_equal(dns_nametree_delete(counttree, name), ISC_R_SUCCESS); + + assert_false(dns_nametree_covered(counttree, name, 0)); + assert_int_equal(dns_nametree_delete(counttree, name), ISC_R_NOTFOUND); + + destroy_tables(); +} + ISC_RUN_TEST_IMPL(covered_bool) { dns_fixedname_t fn; dns_name_t *name = dns_fixedname_name(&fn); @@ -301,6 +342,7 @@ ISC_RUN_TEST_IMPL(find) { ISC_TEST_LIST_START ISC_TEST_ENTRY(add_bool) ISC_TEST_ENTRY(add_bits) +ISC_TEST_ENTRY(add_count) ISC_TEST_ENTRY(covered_bool) ISC_TEST_ENTRY(covered_bits) ISC_TEST_ENTRY(delete)