mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-08-22 18:19:42 +00:00
add a 'foundname' parameter to dns_qp_findname_ancestor(), and use it to set the found name in dns_nametree. this required adding a dns_qpkey_toname() function; that was done by moving qp_test_keytoname() from the test library to qp.c. added some more test cases and fixed bugs with the handling of relative and empty names.
443 lines
11 KiB
C
443 lines
11 KiB
C
/*
|
|
* 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 <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#define UNIT_TESTING
|
|
#include <cmocka.h>
|
|
|
|
#include <isc/random.h>
|
|
#include <isc/refcount.h>
|
|
#include <isc/result.h>
|
|
#include <isc/string.h>
|
|
#include <isc/urcu.h>
|
|
#include <isc/util.h>
|
|
|
|
#include <dns/name.h>
|
|
#include <dns/qp.h>
|
|
|
|
#include "qp_p.h"
|
|
|
|
#include <tests/dns.h>
|
|
#include <tests/qp.h>
|
|
|
|
ISC_RUN_TEST_IMPL(qpkey_name) {
|
|
struct {
|
|
const char *namestr;
|
|
uint8_t key[512];
|
|
size_t len;
|
|
} testcases[] = {
|
|
{
|
|
.namestr = "",
|
|
.key = { 0x02 },
|
|
.len = 0,
|
|
},
|
|
{
|
|
.namestr = ".",
|
|
.key = { 0x02, 0x02 },
|
|
.len = 1,
|
|
},
|
|
{
|
|
.namestr = "\\000",
|
|
.key = { 0x03, 0x03, 0x02, 0x02 },
|
|
.len = 3,
|
|
},
|
|
{
|
|
.namestr = "com",
|
|
.key = { 0x16, 0x22, 0x20, 0x02 },
|
|
.len = 4,
|
|
},
|
|
{
|
|
.namestr = "com.",
|
|
.key = { 0x02, 0x16, 0x22, 0x20, 0x02 },
|
|
.len = 5,
|
|
},
|
|
{
|
|
.namestr = "example.com.",
|
|
.key = { 0x02, 0x16, 0x22, 0x20, 0x02, 0x18, 0x2b, 0x14,
|
|
0x20, 0x23, 0x1f, 0x18, 0x02, 0x02 },
|
|
.len = 13,
|
|
},
|
|
{
|
|
.namestr = "example.com",
|
|
.key = { 0x16, 0x22, 0x20, 0x02, 0x18, 0x2b, 0x14, 0x20,
|
|
0x23, 0x1f, 0x18, 0x02, 0x02 },
|
|
.len = 12,
|
|
},
|
|
{
|
|
.namestr = "EXAMPLE.COM",
|
|
.key = { 0x16, 0x22, 0x20, 0x02, 0x18, 0x2b, 0x14, 0x20,
|
|
0x23, 0x1f, 0x18, 0x02, 0x02 },
|
|
.len = 12,
|
|
},
|
|
};
|
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(testcases); i++) {
|
|
size_t len;
|
|
dns_qpkey_t key;
|
|
dns_fixedname_t fn1, fn2;
|
|
dns_name_t *in = NULL, *out = NULL;
|
|
|
|
in = dns_fixedname_initname(&fn1);
|
|
if (testcases[i].len != 0) {
|
|
dns_test_namefromstring(testcases[i].namestr, &fn1);
|
|
}
|
|
len = dns_qpkey_fromname(key, in);
|
|
|
|
assert_int_equal(testcases[i].len, len);
|
|
assert_memory_equal(testcases[i].key, key, len);
|
|
/* also check key correctness for empty name */
|
|
if (len == 0) {
|
|
assert_int_equal(testcases[i].key[0], ((char *)key)[0]);
|
|
}
|
|
|
|
out = dns_fixedname_initname(&fn2);
|
|
dns_qpkey_toname(key, len, out);
|
|
assert_true(dns_name_equal(in, out));
|
|
}
|
|
}
|
|
|
|
ISC_RUN_TEST_IMPL(qpkey_sort) {
|
|
struct {
|
|
const char *namestr;
|
|
dns_name_t *name;
|
|
dns_fixedname_t fixed;
|
|
size_t len;
|
|
dns_qpkey_t key;
|
|
} testcases[] = {
|
|
{ .namestr = "." },
|
|
{ .namestr = "\\000." },
|
|
{ .namestr = "example.com." },
|
|
{ .namestr = "EXAMPLE.COM." },
|
|
{ .namestr = "www.example.com." },
|
|
{ .namestr = "exam.com." },
|
|
{ .namestr = "exams.com." },
|
|
{ .namestr = "exam\\000.com." },
|
|
};
|
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(testcases); i++) {
|
|
dns_test_namefromstring(testcases[i].namestr,
|
|
&testcases[i].fixed);
|
|
testcases[i].name = dns_fixedname_name(&testcases[i].fixed);
|
|
testcases[i].len = dns_qpkey_fromname(testcases[i].key,
|
|
testcases[i].name);
|
|
}
|
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(testcases); i++) {
|
|
for (size_t j = 0; j < ARRAY_SIZE(testcases); j++) {
|
|
int namecmp = dns_name_compare(testcases[i].name,
|
|
testcases[j].name);
|
|
size_t len = ISC_MIN(testcases[i].len,
|
|
testcases[j].len);
|
|
/* include extra terminating NOBYTE */
|
|
int keycmp = memcmp(testcases[i].key, testcases[j].key,
|
|
len + 1);
|
|
assert_true((namecmp < 0) == (keycmp < 0));
|
|
assert_true((namecmp == 0) == (keycmp == 0));
|
|
assert_true((namecmp > 0) == (keycmp > 0));
|
|
}
|
|
}
|
|
}
|
|
|
|
#define ITER_ITEMS 100
|
|
|
|
static void
|
|
check_leaf(void *uctx, void *pval, uint32_t ival) {
|
|
uint32_t *items = uctx;
|
|
assert_in_range(ival, 1, ITER_ITEMS - 1);
|
|
assert_ptr_equal(items + ival, pval);
|
|
}
|
|
|
|
static size_t
|
|
qpiter_makekey(dns_qpkey_t key, void *uctx, void *pval, uint32_t ival) {
|
|
check_leaf(uctx, pval, ival);
|
|
|
|
char str[8];
|
|
snprintf(str, sizeof(str), "%03u", ival);
|
|
|
|
size_t i = 0;
|
|
while (str[i] != '\0') {
|
|
key[i] = str[i] - '0' + SHIFT_BITMAP;
|
|
i++;
|
|
}
|
|
key[i++] = SHIFT_NOBYTE;
|
|
|
|
return (i);
|
|
}
|
|
|
|
static void
|
|
getname(void *uctx, char *buf, size_t size) {
|
|
strlcpy(buf, "test", size);
|
|
UNUSED(uctx);
|
|
UNUSED(size);
|
|
}
|
|
|
|
const dns_qpmethods_t qpiter_methods = {
|
|
check_leaf,
|
|
check_leaf,
|
|
qpiter_makekey,
|
|
getname,
|
|
};
|
|
|
|
ISC_RUN_TEST_IMPL(qpiter) {
|
|
dns_qp_t *qp = NULL;
|
|
uint32_t item[ITER_ITEMS] = { 0 };
|
|
|
|
dns_qp_create(mctx, &qpiter_methods, item, &qp);
|
|
for (size_t tests = 0; tests < 1234; tests++) {
|
|
uint32_t ival = isc_random_uniform(ITER_ITEMS - 1) + 1;
|
|
void *pval = &item[ival];
|
|
item[ival] = ival;
|
|
|
|
/* randomly insert or remove */
|
|
dns_qpkey_t key;
|
|
size_t len = qpiter_makekey(key, item, pval, ival);
|
|
if (dns_qp_insert(qp, pval, ival) == ISC_R_EXISTS) {
|
|
void *pvald = NULL;
|
|
uint32_t ivald = 0;
|
|
dns_qp_deletekey(qp, key, len, &pvald, &ivald);
|
|
assert_ptr_equal(pval, pvald);
|
|
assert_int_equal(ival, ivald);
|
|
item[ival] = 0;
|
|
}
|
|
|
|
/* check that we see only valid items in the correct order */
|
|
uint32_t prev = 0;
|
|
dns_qpiter_t qpi;
|
|
dns_qpiter_init(qp, &qpi);
|
|
while (dns_qpiter_next(&qpi, &pval, &ival) == ISC_R_SUCCESS) {
|
|
assert_in_range(ival, prev + 1, ITER_ITEMS - 1);
|
|
assert_int_equal(ival, item[ival]);
|
|
assert_ptr_equal(pval, &item[ival]);
|
|
item[ival] = ~ival;
|
|
prev = ival;
|
|
}
|
|
|
|
/* ensure we saw every item */
|
|
for (ival = 0; ival < ITER_ITEMS; ival++) {
|
|
if (item[ival] != 0) {
|
|
assert_int_equal(item[ival], ~ival);
|
|
item[ival] = ival;
|
|
}
|
|
}
|
|
}
|
|
dns_qp_destroy(&qp);
|
|
}
|
|
|
|
static void
|
|
no_op(void *uctx, void *pval, uint32_t ival) {
|
|
UNUSED(uctx);
|
|
UNUSED(pval);
|
|
UNUSED(ival);
|
|
}
|
|
|
|
static size_t
|
|
qpkey_fromstring(dns_qpkey_t key, void *uctx, void *pval, uint32_t ival) {
|
|
dns_fixedname_t fixed;
|
|
|
|
UNUSED(uctx);
|
|
UNUSED(ival);
|
|
if (*(char *)pval == '\0') {
|
|
return (0);
|
|
}
|
|
dns_test_namefromstring(pval, &fixed);
|
|
return (dns_qpkey_fromname(key, dns_fixedname_name(&fixed)));
|
|
}
|
|
|
|
const dns_qpmethods_t string_methods = {
|
|
no_op,
|
|
no_op,
|
|
qpkey_fromstring,
|
|
getname,
|
|
};
|
|
|
|
struct check_partialmatch {
|
|
const char *query;
|
|
dns_qpfind_t options;
|
|
isc_result_t result;
|
|
const char *found;
|
|
};
|
|
|
|
static void
|
|
check_partialmatch(dns_qp_t *qp, struct check_partialmatch check[]) {
|
|
for (int i = 0; check[i].query != NULL; i++) {
|
|
isc_result_t result;
|
|
dns_fixedname_t fn1, fn2;
|
|
dns_name_t *name = dns_fixedname_initname(&fn1);
|
|
dns_name_t *foundname = dns_fixedname_initname(&fn2);
|
|
void *pval = NULL;
|
|
|
|
dns_test_namefromstring(check[i].query, &fn1);
|
|
result = dns_qp_findname_ancestor(qp, name, check[i].options,
|
|
foundname, &pval, NULL);
|
|
|
|
#if 0
|
|
fprintf(stderr, "%s (flags %u) %s (expected %s) "
|
|
"value \"%s\" (expected \"%s\")\n",
|
|
check[i].query, check[i].options,
|
|
isc_result_totext(result),
|
|
isc_result_totext(check[i].result), (char *)pval,
|
|
check[i].found);
|
|
#endif
|
|
|
|
assert_int_equal(result, check[i].result);
|
|
if (result == ISC_R_SUCCESS) {
|
|
assert_true(dns_name_equal(name, foundname));
|
|
} else if (result == DNS_R_PARTIALMATCH) {
|
|
/*
|
|
* there are cases where we may have passed a
|
|
* query name that was relative to the zone apex,
|
|
* and gotten back an absolute name from the
|
|
* partial match. it's also possible for an
|
|
* absolute query to get a partial match on a
|
|
* node that had an empty name. in these cases,
|
|
* sanity checking the relations between name
|
|
* and foundname can trigger an assertion, so
|
|
* let's just skip them.
|
|
*/
|
|
if (dns_name_isabsolute(name) ==
|
|
dns_name_isabsolute(foundname))
|
|
{
|
|
assert_false(dns_name_equal(name, foundname));
|
|
assert_true(
|
|
dns_name_issubdomain(name, foundname));
|
|
}
|
|
}
|
|
if (check[i].found == NULL) {
|
|
assert_null(pval);
|
|
} else {
|
|
assert_string_equal(pval, check[i].found);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
insert_str(dns_qp_t *qp, const char *str) {
|
|
isc_result_t result;
|
|
uintptr_t pval = (uintptr_t)str;
|
|
INSIST((pval & 3) == 0);
|
|
result = dns_qp_insert(qp, (void *)pval, 0);
|
|
assert_int_equal(result, ISC_R_SUCCESS);
|
|
}
|
|
|
|
ISC_RUN_TEST_IMPL(partialmatch) {
|
|
isc_result_t result;
|
|
dns_qp_t *qp = NULL;
|
|
int i = 0;
|
|
|
|
dns_qp_create(mctx, &string_methods, NULL, &qp);
|
|
|
|
/*
|
|
* Fixed size strings [16] should ensure leaf-compatible alignment.
|
|
*/
|
|
const char insert[][16] = {
|
|
"a.b.", "b.", "fo.bar.", "foo.bar.",
|
|
"fooo.bar.", "web.foo.bar.", ".", "",
|
|
};
|
|
|
|
/*
|
|
* omit the root node for now, otherwise we'll get "partial match"
|
|
* results when we want "not found".
|
|
*/
|
|
while (insert[i][0] != '.') {
|
|
insert_str(qp, insert[i++]);
|
|
}
|
|
|
|
static struct check_partialmatch check1[] = {
|
|
{ "a.b.", 0, ISC_R_SUCCESS, "a.b." },
|
|
{ "a.b.", DNS_QPFIND_NOEXACT, DNS_R_PARTIALMATCH, "b." },
|
|
{ "b.c.", DNS_QPFIND_NOEXACT, ISC_R_NOTFOUND, NULL },
|
|
{ "bar.", 0, ISC_R_NOTFOUND, NULL },
|
|
{ "f.bar.", 0, ISC_R_NOTFOUND, NULL },
|
|
{ "foo.bar.", 0, ISC_R_SUCCESS, "foo.bar." },
|
|
{ "foo.bar.", DNS_QPFIND_NOEXACT, ISC_R_NOTFOUND, NULL },
|
|
{ "foooo.bar.", 0, ISC_R_NOTFOUND, NULL },
|
|
{ "w.foo.bar.", 0, DNS_R_PARTIALMATCH, "foo.bar." },
|
|
{ "www.foo.bar.", 0, DNS_R_PARTIALMATCH, "foo.bar." },
|
|
{ "web.foo.bar.", 0, ISC_R_SUCCESS, "web.foo.bar." },
|
|
{ "webby.foo.bar.", 0, DNS_R_PARTIALMATCH, "foo.bar." },
|
|
{ "my.web.foo.bar.", 0, DNS_R_PARTIALMATCH, "web.foo.bar." },
|
|
{ "web.foo.bar.", DNS_QPFIND_NOEXACT, DNS_R_PARTIALMATCH,
|
|
"foo.bar." },
|
|
{ "my.web.foo.bar.", DNS_QPFIND_NOEXACT, DNS_R_PARTIALMATCH,
|
|
"web.foo.bar." },
|
|
{ "my.other.foo.bar.", DNS_QPFIND_NOEXACT, DNS_R_PARTIALMATCH,
|
|
"foo.bar." },
|
|
{ NULL, 0, 0, NULL },
|
|
};
|
|
check_partialmatch(qp, check1);
|
|
|
|
/* what if the trie contains the root? */
|
|
INSIST(insert[i][0] == '.');
|
|
insert_str(qp, insert[i++]);
|
|
|
|
static struct check_partialmatch check2[] = {
|
|
{ "b.c.", DNS_QPFIND_NOEXACT, DNS_R_PARTIALMATCH, "." },
|
|
{ "bar.", 0, DNS_R_PARTIALMATCH, "." },
|
|
{ "foo.bar.", 0, ISC_R_SUCCESS, "foo.bar." },
|
|
{ "foo.bar.", DNS_QPFIND_NOEXACT, DNS_R_PARTIALMATCH, "." },
|
|
{ "bar", 0, ISC_R_NOTFOUND, NULL },
|
|
{ "bar", DNS_QPFIND_NOEXACT, DNS_R_PARTIALMATCH, "." },
|
|
{ NULL, 0, 0, NULL },
|
|
};
|
|
check_partialmatch(qp, check2);
|
|
|
|
/*
|
|
* what if entries in the trie are relative to the zone apex
|
|
* and there's no root node?
|
|
*/
|
|
dns_qpkey_t rootkey = { SHIFT_NOBYTE };
|
|
result = dns_qp_deletekey(qp, rootkey, 1, NULL, NULL);
|
|
assert_int_equal(result, ISC_R_SUCCESS);
|
|
check_partialmatch(
|
|
qp,
|
|
(struct check_partialmatch[]){
|
|
{ "bar", 0, ISC_R_NOTFOUND, NULL },
|
|
{ "bar", DNS_QPFIND_NOEXACT, ISC_R_NOTFOUND, NULL },
|
|
{ "bar.", 0, ISC_R_NOTFOUND, NULL },
|
|
{ "bar.", DNS_QPFIND_NOEXACT, ISC_R_NOTFOUND, NULL },
|
|
{ NULL, 0, 0, NULL },
|
|
});
|
|
|
|
/* what if there's a root node with an empty key? */
|
|
INSIST(insert[i][0] == '\0');
|
|
insert_str(qp, insert[i++]);
|
|
check_partialmatch(
|
|
qp,
|
|
(struct check_partialmatch[]){
|
|
{ "bar", 0, DNS_R_PARTIALMATCH, "" },
|
|
{ "bar", DNS_QPFIND_NOEXACT, DNS_R_PARTIALMATCH, "" },
|
|
{ "bar.", 0, DNS_R_PARTIALMATCH, "" },
|
|
{ "bar.", DNS_QPFIND_NOEXACT, DNS_R_PARTIALMATCH, "" },
|
|
{ NULL, 0, 0, NULL },
|
|
});
|
|
|
|
dns_qp_destroy(&qp);
|
|
}
|
|
|
|
ISC_TEST_LIST_START
|
|
ISC_TEST_ENTRY(qpkey_name)
|
|
ISC_TEST_ENTRY(qpkey_sort)
|
|
ISC_TEST_ENTRY(qpiter)
|
|
ISC_TEST_ENTRY(partialmatch)
|
|
ISC_TEST_LIST_END
|
|
|
|
ISC_TEST_MAIN
|