]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
add semantics to name trees to support counters
authorEvan Hunt <each@isc.org>
Thu, 17 Aug 2023 06:26:50 +0000 (23:26 -0700)
committerOndřej Surý <ondrej@isc.org>
Mon, 4 Sep 2023 08:19:48 +0000 (10:19 +0200)
name trees can now also hold trees of counters. each time a name
dns_nametree_add() is called with a given name, the counter for that
name is incremented; the name is not deleted until dns_nametree_delete()
is called the same number of times.

this is meant to be used for synth-from-dnssec, which is incremented for
each key defined at a name, and decremented when a key is removed, the
name must continue to exist until the number of keys has reached zero.

lib/dns/include/dns/nametree.h
lib/dns/nametree.c
tests/dns/nametree_test.c

index acd8bbd21f9a9e20cec9a08e930a609c74151d1c..bae8dcefb3e26369bc1a1d39d44d6849a6272990 100644 (file)
 /* 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.
index b148dd756505a124e9beb7852e451890c6f2fb77..bbca177aea283988be86057355b5c3f60915550e 100644 (file)
@@ -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;
                }
        }
 
index 840d3656e1687713855e837a5b2083b1f321c143..23df32f7e67b9accc0e9feab5a3320e1a51cabf7 100644 (file)
@@ -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)