unsigned int magic;
isc_mem_t *mctx;
isc_refcount_t references;
+ dns_nametree_type_t type;
dns_qpmulti_t *table;
char name[64];
};
isc_refcount_t references;
dns_fixedname_t fn;
dns_name_t *name;
- union {
- bool value;
- unsigned char *bits;
- };
+ bool set;
+ uint8_t *bits;
};
/* QP trie methods */
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_putanddetach(&node->mctx, node, sizeof(dns_ntnode_t));
}
#endif
void
-dns_nametree_create(isc_mem_t *mctx, const char *name, dns_nametree_t **ntp) {
+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);
return (node);
}
+static bool
+matchbit(unsigned char *bits, uint32_t val) {
+ unsigned int len = val / 8;
+ unsigned int mask = 1 << (val % 8);
+
+ if ((bits[len] & mask) != 0) {
+ return (true);
+ }
+ return (false);
+}
+
isc_result_t
-dns_nametree_add(dns_nametree_t *nametree, const dns_name_t *name, bool value) {
+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;
+ dns_ntnode_t *old = NULL, *new = NULL;
REQUIRE(VALID_NAMETREE(nametree));
REQUIRE(name != NULL);
dns_qpmulti_write(nametree->table, &qp);
- result = dns_qp_getname(qp, name, NULL, NULL);
- if (result == ISC_R_SUCCESS) {
- result = ISC_R_EXISTS;
- } else {
- dns_ntnode_t *node = newnode(nametree->mctx, name);
- node->value = value;
- result = dns_qp_insert(qp, node, 0);
-
- /*
- * We detach the node here, so any dns_qp_deletename() will
- * destroy the node directly.
- */
- dns_ntnode_detach(&node);
+ switch (nametree->type) {
+ case DNS_NAMETREE_BOOL:
+ new = newnode(nametree->mctx, name);
+ new->set = value;
+ 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;
+ mask = 1 << (value % 8);
+
+ new = newnode(nametree->mctx, name);
+ new->bits = isc_mem_cget(nametree->mctx, 8, sizeof(value));
+ 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;
+ break;
+ default:
+ UNREACHABLE();
}
+ result = dns_qp_insert(qp, new, 0);
+
+ /*
+ * 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);
}
bool
-dns_nametree_covered(dns_nametree_t *nametree, const dns_name_t *name) {
+dns_nametree_covered(dns_nametree_t *nametree, const dns_name_t *name,
+ uint32_t bit) {
isc_result_t result;
dns_qpread_t qpr;
- dns_ntnode_t *ntnode = NULL;
- void *pval = NULL;
- bool value = false;
+ dns_ntnode_t *node = NULL;
+ bool ret = false;
REQUIRE(nametree == NULL || VALID_NAMETREE(nametree));
}
dns_qpmulti_query(nametree->table, &qpr);
- result = dns_qp_findname_ancestor(&qpr, name, 0, &pval, NULL);
+ result = dns_qp_findname_ancestor(&qpr, name, 0, (void **)&node, NULL);
if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
- ntnode = pval;
- value = ntnode->value;
+ if (nametree->type == DNS_NAMETREE_BOOL) {
+ ret = node->set;
+ } else {
+ ret = matchbit(node->bits, bit);
+ }
}
dns_qpread_destroy(nametree->table, &qpr);
- return (value);
+ return (ret);
}
static void
#include <tests/dns.h>
-dns_nametree_t *nametree = NULL;
+dns_nametree_t *booltree = NULL;
+dns_nametree_t *bitstree = NULL;
/*
* Test utilities. In general, these assume input parameters are valid
* the test code concise.
*/
-/* Common setup: create a nametree to test with a few keys */
+/* Common setup: create a booltree to test with a few keys */
static void
create_tables(void) {
dns_fixedname_t fn;
dns_name_t *name = dns_fixedname_name(&fn);
- dns_nametree_create(mctx, "test", &nametree);
+ dns_nametree_create(mctx, DNS_NAMETREE_BOOL, "bool test", &booltree);
+ dns_nametree_create(mctx, DNS_NAMETREE_BITS, "bits test", &bitstree);
- /* Add a positive node */
+ /* Add a positive boolean node */
dns_test_namefromstring("example.com.", &fn);
- assert_int_equal(dns_nametree_add(nametree, name, true), ISC_R_SUCCESS);
+ assert_int_equal(dns_nametree_add(booltree, name, true), ISC_R_SUCCESS);
- /* Add a negative node below it */
+ /* 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(nametree, name, false),
+ assert_int_equal(dns_nametree_add(booltree, name, false),
ISC_R_SUCCESS);
-
- /* Add a negative node with no parent */
dns_test_namefromstring("negative.example.org.", &fn);
- assert_int_equal(dns_nametree_add(nametree, name, false),
+ assert_int_equal(dns_nametree_add(booltree, name, false),
ISC_R_SUCCESS);
+
+ /* Add a bitfield nodes under a parent */
+ dns_test_namefromstring("sub.example.com.", &fn);
+ assert_int_equal(dns_nametree_add(bitstree, name, 2), ISC_R_SUCCESS);
}
static void
destroy_tables(void) {
- if (nametree != NULL) {
- dns_nametree_detach(&nametree);
+ if (booltree != NULL) {
+ dns_nametree_detach(&booltree);
+ }
+ if (bitstree != NULL) {
+ dns_nametree_detach(&bitstree);
}
rcu_barrier();
}
-ISC_RUN_TEST_IMPL(add) {
+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(nametree, name, &node),
+ 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(nametree, name, false), ISC_R_EXISTS);
- assert_int_equal(dns_nametree_find(nametree, name, &node),
+ 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(nametree, name, true), ISC_R_SUCCESS);
- assert_int_equal(dns_nametree_find(nametree, name, &node),
+ 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);
+
+ destroy_tables();
+}
+
+ISC_RUN_TEST_IMPL(add_bits) {
+ dns_ntnode_t *node = NULL;
+ dns_fixedname_t fn;
+ dns_name_t *name = dns_fixedname_name(&fn);
+
+ create_tables();
+
+ /*
+ * 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);
+
+ destroy_tables();
+}
+
+ISC_RUN_TEST_IMPL(covered_bool) {
+ dns_fixedname_t fn;
+ dns_name_t *name = dns_fixedname_name(&fn);
+ const char *yesnames[] = { "example.com.", "sub.example.com.", NULL };
+ const char *nonames[] = { "whatever.com.", "negative.example.com.",
+ "example.org.", "negative.example.org.",
+ NULL };
+ create_tables();
+
+ for (const char **n = yesnames; *n != NULL; n++) {
+ dns_test_namefromstring(*n, &fn);
+ assert_true(dns_nametree_covered(booltree, name, 0));
+ }
+ for (const char **n = nonames; *n != NULL; n++) {
+ dns_test_namefromstring(*n, &fn);
+ assert_false(dns_nametree_covered(booltree, name, 0));
+ }
+
+ /* If the nametree is NULL, dns_nametree_covered() returns false. */
+ dns_test_namefromstring("anyname.example.", &fn);
+ assert_false(dns_nametree_covered(NULL, name, 0));
+
+ destroy_tables();
+}
+
+ISC_RUN_TEST_IMPL(covered_bits) {
+ dns_fixedname_t fn;
+ dns_name_t *name = dns_fixedname_name(&fn);
+
+ create_tables();
+
+ /* check existing bit values */
+ dns_test_namefromstring("example.com.", &fn);
+ assert_false(dns_nametree_covered(bitstree, name, 0));
+ assert_true(dns_nametree_covered(bitstree, name, 1));
+ assert_false(dns_nametree_covered(bitstree, name, 2));
+ assert_false(dns_nametree_covered(bitstree, name, 3));
+ assert_true(dns_nametree_covered(bitstree, name, 9));
+ assert_true(dns_nametree_covered(bitstree, name, 53));
+ assert_false(dns_nametree_covered(bitstree, name, 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, 3));
+
+ /* add a large bit value, test again */
+ assert_int_equal(dns_nametree_add(bitstree, name, 615), ISC_R_SUCCESS);
+ assert_true(dns_nametree_covered(bitstree, name, 615));
+
+ /* check existing bit values for subdomain */
+ dns_test_namefromstring("sub.example.com.", &fn);
+ assert_false(dns_nametree_covered(bitstree, name, 0));
+ assert_false(dns_nametree_covered(bitstree, name, 1));
+ assert_true(dns_nametree_covered(bitstree, name, 2));
+ assert_false(dns_nametree_covered(bitstree, name, 3));
+ assert_false(dns_nametree_covered(bitstree, name, 9));
+ assert_false(dns_nametree_covered(bitstree, name, 53));
+ assert_false(dns_nametree_covered(bitstree, name, 288));
+
+ /* check nonexistent subdomain is all false */
+ dns_test_namefromstring("other.example.com", &fn);
+ assert_false(dns_nametree_covered(bitstree, name, 0));
+ assert_false(dns_nametree_covered(bitstree, name, 1));
+ assert_false(dns_nametree_covered(bitstree, name, 2));
+ assert_false(dns_nametree_covered(bitstree, name, 3));
+ assert_false(dns_nametree_covered(bitstree, name, 9));
+ assert_false(dns_nametree_covered(bitstree, name, 53));
+ assert_false(dns_nametree_covered(bitstree, name, 288));
+
+ /* check nonexistent domain is all false */
+ dns_test_namefromstring("anyname.", &fn);
+ assert_false(dns_nametree_covered(bitstree, name, 0));
+ assert_false(dns_nametree_covered(bitstree, name, 1));
+ assert_false(dns_nametree_covered(bitstree, name, 2));
+ assert_false(dns_nametree_covered(bitstree, name, 3));
+ assert_false(dns_nametree_covered(bitstree, name, 9));
+ assert_false(dns_nametree_covered(bitstree, name, 53));
+ assert_false(dns_nametree_covered(bitstree, name, 288));
+
destroy_tables();
}
/* name doesn't match */
dns_test_namefromstring("example.org.", &fn);
- assert_int_equal(dns_nametree_delete(nametree, name), ISC_R_NOTFOUND);
+ 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(nametree, name), ISC_R_NOTFOUND);
+ assert_int_equal(dns_nametree_delete(booltree, name), ISC_R_NOTFOUND);
/*
* delete requires exact match: this should return SUCCESS on
* ancestor does exist.
*/
dns_test_namefromstring("negative.example.com.", &fn);
- assert_int_equal(dns_nametree_delete(nametree, name), ISC_R_SUCCESS);
- assert_int_equal(dns_nametree_delete(nametree, name), ISC_R_NOTFOUND);
+ 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(nametree, name), ISC_R_SUCCESS);
- assert_int_equal(dns_nametree_delete(nametree, name), ISC_R_NOTFOUND);
+ assert_int_equal(dns_nametree_delete(booltree, name), ISC_R_SUCCESS);
+ assert_int_equal(dns_nametree_delete(booltree, name), ISC_R_NOTFOUND);
destroy_tables();
}
* that has a null key, too.
*/
dns_test_namefromstring("example.org.", &fn);
- assert_int_equal(dns_nametree_find(nametree, name, &node),
+ 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(nametree, name, &node),
+ assert_int_equal(dns_nametree_find(booltree, name, &node),
ISC_R_NOTFOUND);
dns_test_namefromstring("example.com.", &fn);
- assert_int_equal(dns_nametree_find(nametree, name, &node),
+ assert_int_equal(dns_nametree_find(booltree, name, &node),
ISC_R_SUCCESS);
dns_ntnode_detach(&node);
destroy_tables();
}
-ISC_RUN_TEST_IMPL(covered) {
- dns_fixedname_t fn;
- dns_name_t *name = dns_fixedname_name(&fn);
- const char *yesnames[] = { "example.com.", "sub.example.com.", NULL };
- const char *nonames[] = { "whatever.com.", "negative.example.com.",
- "example.org.", "negative.example.org.",
- NULL };
- create_tables();
-
- for (const char **n = yesnames; *n != NULL; n++) {
- dns_test_namefromstring(*n, &fn);
- assert_true(dns_nametree_covered(nametree, name));
- }
- for (const char **n = nonames; *n != NULL; n++) {
- dns_test_namefromstring(*n, &fn);
- assert_false(dns_nametree_covered(nametree, name));
- }
-
- /* If nametree is NULL, dns_nametree_covered() returns false. */
- dns_test_namefromstring("anyname.example.", &fn);
- assert_false(dns_nametree_covered(NULL, name));
-
- destroy_tables();
-}
-
ISC_TEST_LIST_START
-ISC_TEST_ENTRY(add)
-ISC_TEST_ENTRY(covered)
-ISC_TEST_ENTRY(find)
+ISC_TEST_ENTRY(add_bool)
+ISC_TEST_ENTRY(add_bits)
+ISC_TEST_ENTRY(covered_bool)
+ISC_TEST_ENTRY(covered_bits)
ISC_TEST_ENTRY(delete)
+ISC_TEST_ENTRY(find)
ISC_TEST_LIST_END
ISC_TEST_MAIN