940 lines
25 KiB
C
940 lines
25 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>
|
|
|
|
bool verbose = false;
|
|
|
|
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 },
|
|
.len = 3,
|
|
},
|
|
{
|
|
.namestr = "\\000\\009",
|
|
.key = { 0x03, 0x03, 0x03, 0x0c, 0x02 },
|
|
.len = 5,
|
|
},
|
|
{
|
|
.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 },
|
|
.len = 13,
|
|
},
|
|
{
|
|
.namestr = "example.com",
|
|
.key = { 0x16, 0x22, 0x20, 0x02, 0x18, 0x2b, 0x14, 0x20,
|
|
0x23, 0x1f, 0x18, 0x02 },
|
|
.len = 12,
|
|
},
|
|
{
|
|
.namestr = "EXAMPLE.COM",
|
|
.key = { 0x16, 0x22, 0x20, 0x02, 0x18, 0x2b, 0x14, 0x20,
|
|
0x23, 0x1f, 0x18, 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;
|
|
char namebuf[DNS_NAME_FORMATSIZE];
|
|
|
|
in = dns_fixedname_initname(&fn1);
|
|
if (testcases[i].len != 0) {
|
|
dns_test_namefromstring(testcases[i].namestr, &fn1);
|
|
}
|
|
len = dns_qpkey_fromname(key, in);
|
|
if (verbose) {
|
|
qp_test_printkey(key, len);
|
|
}
|
|
|
|
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));
|
|
/* check that 'out' is properly reset by dns_qpkey_toname */
|
|
dns_qpkey_toname(key, len, out);
|
|
dns_name_format(out, namebuf, sizeof(namebuf));
|
|
}
|
|
}
|
|
|
|
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 = "\\000.\\000." },
|
|
{ .namestr = "\\000\\009." },
|
|
{ .namestr = "\\007." },
|
|
{ .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 };
|
|
uint32_t order[ITER_ITEMS] = { 0 };
|
|
dns_qpiter_t qpi;
|
|
int inserted, n;
|
|
uint32_t ival;
|
|
void *pval = NULL;
|
|
isc_result_t result;
|
|
|
|
dns_qp_create(mctx, &qpiter_methods, item, &qp);
|
|
for (size_t tests = 0; tests < 1234; tests++) {
|
|
ival = isc_random_uniform(ITER_ITEMS - 1) + 1;
|
|
pval = &item[ival];
|
|
|
|
item[ival] = ival;
|
|
|
|
inserted = n = 0;
|
|
|
|
/* 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_init(qp, &qpi);
|
|
while (dns_qpiter_next(&qpi, NULL, &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]);
|
|
order[inserted++] = 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;
|
|
}
|
|
}
|
|
|
|
/* now iterate backward and check correctness */
|
|
n = inserted;
|
|
while (dns_qpiter_prev(&qpi, NULL, NULL, &ival) ==
|
|
ISC_R_SUCCESS)
|
|
{
|
|
--n;
|
|
|
|
assert_int_equal(ival, order[n]);
|
|
|
|
/* and check current iterator value as well */
|
|
result = dns_qpiter_current(&qpi, NULL, NULL, &ival);
|
|
assert_int_equal(result, ISC_R_SUCCESS);
|
|
assert_int_equal(ival, order[n]);
|
|
}
|
|
|
|
assert_int_equal(n, 0);
|
|
|
|
/* ...and forward again */
|
|
while (dns_qpiter_next(&qpi, NULL, NULL, &ival) ==
|
|
ISC_R_SUCCESS)
|
|
{
|
|
assert_int_equal(ival, order[n]);
|
|
|
|
/* and check current iterator value as well */
|
|
result = dns_qpiter_current(&qpi, NULL, NULL, &ival);
|
|
assert_int_equal(result, ISC_R_SUCCESS);
|
|
assert_int_equal(ival, order[n]);
|
|
|
|
n++;
|
|
}
|
|
|
|
assert_int_equal(n, inserted);
|
|
|
|
/*
|
|
* if there are enough items inserted, try going
|
|
* forward a few steps, then back to the start,
|
|
* to confirm we can change directions while iterating.
|
|
*/
|
|
if (inserted > 3) {
|
|
assert_int_equal(
|
|
dns_qpiter_next(&qpi, NULL, NULL, &ival),
|
|
ISC_R_SUCCESS);
|
|
assert_int_equal(ival, order[0]);
|
|
|
|
assert_int_equal(
|
|
dns_qpiter_next(&qpi, NULL, NULL, &ival),
|
|
ISC_R_SUCCESS);
|
|
assert_int_equal(ival, order[1]);
|
|
|
|
assert_int_equal(
|
|
dns_qpiter_prev(&qpi, NULL, NULL, &ival),
|
|
ISC_R_SUCCESS);
|
|
assert_int_equal(ival, order[0]);
|
|
|
|
assert_int_equal(
|
|
dns_qpiter_next(&qpi, NULL, NULL, &ival),
|
|
ISC_R_SUCCESS);
|
|
assert_int_equal(ival, order[1]);
|
|
|
|
assert_int_equal(
|
|
dns_qpiter_prev(&qpi, NULL, NULL, &ival),
|
|
ISC_R_SUCCESS);
|
|
assert_int_equal(ival, order[0]);
|
|
|
|
assert_int_equal(
|
|
dns_qpiter_prev(&qpi, NULL, NULL, &ival),
|
|
ISC_R_NOMORE);
|
|
}
|
|
}
|
|
|
|
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;
|
|
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_lookup(qp, name, foundname, NULL, NULL, &pval,
|
|
NULL);
|
|
|
|
#if 0
|
|
fprintf(stderr, "%s %s (expected %s) "
|
|
"value \"%s\" (expected \"%s\")\n",
|
|
check[i].query,
|
|
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.", ISC_R_SUCCESS, "a.b." },
|
|
{ "b.c.", ISC_R_NOTFOUND, NULL },
|
|
{ "bar.", ISC_R_NOTFOUND, NULL },
|
|
{ "f.bar.", ISC_R_NOTFOUND, NULL },
|
|
{ "foo.bar.", ISC_R_SUCCESS, "foo.bar." },
|
|
{ "foooo.bar.", ISC_R_NOTFOUND, NULL },
|
|
{ "w.foo.bar.", DNS_R_PARTIALMATCH, "foo.bar." },
|
|
{ "www.foo.bar.", DNS_R_PARTIALMATCH, "foo.bar." },
|
|
{ "web.foo.bar.", ISC_R_SUCCESS, "web.foo.bar." },
|
|
{ "webby.foo.bar.", DNS_R_PARTIALMATCH, "foo.bar." },
|
|
{ "my.web.foo.bar.", DNS_R_PARTIALMATCH, "web.foo.bar." },
|
|
{ "my.other.foo.bar.", DNS_R_PARTIALMATCH, "foo.bar." },
|
|
{ NULL, 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_R_PARTIALMATCH, "." },
|
|
{ "bar.", DNS_R_PARTIALMATCH, "." },
|
|
{ "foo.bar.", ISC_R_SUCCESS, "foo.bar." },
|
|
{ "bar", ISC_R_NOTFOUND, NULL },
|
|
{ NULL, 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", ISC_R_NOTFOUND, NULL },
|
|
{ "bar.", ISC_R_NOTFOUND, NULL },
|
|
{ NULL, 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", DNS_R_PARTIALMATCH, "" },
|
|
{ "bar.", DNS_R_PARTIALMATCH, "" },
|
|
{ NULL, 0, NULL },
|
|
});
|
|
|
|
dns_qp_destroy(&qp);
|
|
}
|
|
|
|
struct check_qpchain {
|
|
const char *query;
|
|
isc_result_t result;
|
|
unsigned int length;
|
|
const char *names[10];
|
|
};
|
|
|
|
static void
|
|
check_qpchainiter(dns_qp_t *qp, struct check_qpchain check[],
|
|
dns_qpiter_t *iter) {
|
|
for (int i = 0; check[i].query != NULL; i++) {
|
|
isc_result_t result;
|
|
dns_fixedname_t fn1;
|
|
dns_name_t *name = dns_fixedname_initname(&fn1);
|
|
dns_qpchain_t chain;
|
|
|
|
dns_qpchain_init(qp, &chain);
|
|
dns_test_namefromstring(check[i].query, &fn1);
|
|
result = dns_qp_lookup(qp, name, NULL, iter, &chain, NULL,
|
|
NULL);
|
|
#if 0
|
|
fprintf(stderr, "%s %s (expected %s), "
|
|
"len %d (expected %d)\n", check[i].query,
|
|
isc_result_totext(result),
|
|
isc_result_totext(check[i].result),
|
|
dns_qpchain_length(&chain), check[i].length);
|
|
#endif
|
|
|
|
assert_int_equal(result, check[i].result);
|
|
assert_int_equal(dns_qpchain_length(&chain), check[i].length);
|
|
for (unsigned int j = 0; j < check[i].length; j++) {
|
|
dns_fixedname_t fn2, fn3;
|
|
dns_name_t *expected = dns_fixedname_initname(&fn2);
|
|
dns_name_t *found = dns_fixedname_initname(&fn3);
|
|
dns_test_namefromstring(check[i].names[j], &fn2);
|
|
dns_qpchain_node(&chain, j, found, NULL, NULL);
|
|
#if 0
|
|
char nb[DNS_NAME_FORMATSIZE];
|
|
dns_name_format(found, nb, sizeof(nb));
|
|
fprintf(stderr, "got %s, expected %s\n", nb,
|
|
check[i].names[j]);
|
|
#endif
|
|
assert_true(dns_name_equal(found, expected));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
check_qpchain(dns_qp_t *qp, struct check_qpchain check[]) {
|
|
dns_qpiter_t iter;
|
|
dns_qpiter_init(qp, &iter);
|
|
check_qpchainiter(qp, check, NULL);
|
|
check_qpchainiter(qp, check, &iter);
|
|
}
|
|
|
|
ISC_RUN_TEST_IMPL(qpchain) {
|
|
dns_qp_t *qp = NULL;
|
|
const char insert[][16] = { ".", "a.", "b.",
|
|
"c.b.a.", "e.d.c.b.a.", "c.b.b.",
|
|
"c.d.", "a.b.c.d.", "a.b.c.d.e.",
|
|
"b.a.", "x.k.c.d.", "" };
|
|
int i = 0;
|
|
|
|
dns_qp_create(mctx, &string_methods, NULL, &qp);
|
|
|
|
while (insert[i][0] != '\0') {
|
|
insert_str(qp, insert[i++]);
|
|
}
|
|
|
|
static struct check_qpchain check1[] = {
|
|
{ "b.", ISC_R_SUCCESS, 2, { ".", "b." } },
|
|
{ "b.a.", ISC_R_SUCCESS, 3, { ".", "a.", "b.a." } },
|
|
{ "c.", DNS_R_PARTIALMATCH, 1, { "." } },
|
|
{ "e.d.c.b.a.",
|
|
ISC_R_SUCCESS,
|
|
5,
|
|
{ ".", "a.", "b.a.", "c.b.a.", "e.d.c.b.a." } },
|
|
{ "a.b.c.d.", ISC_R_SUCCESS, 3, { ".", "c.d.", "a.b.c.d." } },
|
|
{ "b.c.d.", DNS_R_PARTIALMATCH, 2, { ".", "c.d." } },
|
|
{ "z.x.k.c.d.",
|
|
DNS_R_PARTIALMATCH,
|
|
3,
|
|
{ ".", "c.d.", "x.k.c.d." } },
|
|
{ NULL, 0, 0, { NULL } },
|
|
};
|
|
|
|
check_qpchain(qp, check1);
|
|
dns_qp_destroy(&qp);
|
|
|
|
const char insert2[][16] = { "a.", "d.b.a.", "z.d.b.a.", "" };
|
|
|
|
i = 0;
|
|
|
|
dns_qp_create(mctx, &string_methods, NULL, &qp);
|
|
|
|
while (insert2[i][0] != '\0') {
|
|
insert_str(qp, insert2[i++]);
|
|
}
|
|
|
|
static struct check_qpchain check2[] = {
|
|
{ "f.c.b.a.", DNS_R_PARTIALMATCH, 1, { "a." } },
|
|
{ NULL, 0, 0, { NULL } },
|
|
};
|
|
|
|
check_qpchain(qp, check2);
|
|
dns_qp_destroy(&qp);
|
|
}
|
|
|
|
struct check_predecessors {
|
|
const char *query;
|
|
const char *predecessor;
|
|
isc_result_t result;
|
|
int remaining;
|
|
};
|
|
|
|
static void
|
|
check_predecessors_withchain(dns_qp_t *qp, struct check_predecessors check[],
|
|
dns_qpchain_t *chain) {
|
|
isc_result_t result;
|
|
dns_fixedname_t fn1, fn2;
|
|
dns_name_t *name = dns_fixedname_initname(&fn1);
|
|
dns_name_t *pred = dns_fixedname_initname(&fn2);
|
|
char *namestr = NULL;
|
|
|
|
for (int i = 0; check[i].query != NULL; i++) {
|
|
dns_qpiter_t it;
|
|
|
|
dns_test_namefromstring(check[i].query, &fn1);
|
|
|
|
/*
|
|
* normalize the expected predecessor name, in
|
|
* case it has escaped characters, so we can compare
|
|
* apples to apples.
|
|
*/
|
|
dns_fixedname_t fn3;
|
|
dns_name_t *expred = dns_fixedname_initname(&fn3);
|
|
char *predstr = NULL;
|
|
dns_test_namefromstring(check[i].predecessor, &fn3);
|
|
result = dns_name_tostring(expred, &predstr, mctx);
|
|
assert_int_equal(result, ISC_R_SUCCESS);
|
|
|
|
result = dns_qp_lookup(qp, name, NULL, &it, chain, NULL, NULL);
|
|
#if 0
|
|
fprintf(stderr, "%s: expected %s got %s\n", check[i].query,
|
|
isc_result_totext(check[i].result),
|
|
isc_result_totext(result));
|
|
#endif
|
|
assert_int_equal(result, check[i].result);
|
|
|
|
if (result == ISC_R_SUCCESS) {
|
|
/*
|
|
* we found an exact match; iterate to find
|
|
* the predecessor.
|
|
*/
|
|
result = dns_qpiter_prev(&it, pred, NULL, NULL);
|
|
if (result == ISC_R_NOMORE) {
|
|
result = dns_qpiter_prev(&it, pred, NULL, NULL);
|
|
}
|
|
} else {
|
|
/*
|
|
* we didn't find a match, so the iterator should
|
|
* already be pointed at the predecessor node.
|
|
*/
|
|
result = dns_qpiter_current(&it, pred, NULL, NULL);
|
|
}
|
|
assert_int_equal(result, ISC_R_SUCCESS);
|
|
|
|
result = dns_name_tostring(pred, &namestr, mctx);
|
|
#if 0
|
|
fprintf(stderr, "... expected predecessor %s got %s\n",
|
|
predstr, namestr);
|
|
#endif
|
|
assert_int_equal(result, ISC_R_SUCCESS);
|
|
assert_string_equal(namestr, predstr);
|
|
|
|
#if 0
|
|
fprintf(stderr, "%d: remaining names after %s:\n", i, namestr);
|
|
#endif
|
|
isc_mem_free(mctx, namestr);
|
|
isc_mem_free(mctx, predstr);
|
|
|
|
int j = 0;
|
|
while (dns_qpiter_next(&it, name, NULL, NULL) == ISC_R_SUCCESS)
|
|
{
|
|
#if 0
|
|
result = dns_name_tostring(name, &namestr, mctx);
|
|
assert_int_equal(result, ISC_R_SUCCESS);
|
|
fprintf(stderr, "%s%s", j > 0 ? "->" : "", namestr);
|
|
isc_mem_free(mctx, namestr);
|
|
#endif
|
|
j++;
|
|
}
|
|
#if 0
|
|
fprintf(stderr, "\n...expected %d got %d\n",
|
|
check[i].remaining, j);
|
|
#endif
|
|
assert_int_equal(j, check[i].remaining);
|
|
}
|
|
}
|
|
|
|
static void
|
|
check_predecessors(dns_qp_t *qp, struct check_predecessors check[]) {
|
|
dns_qpchain_t chain;
|
|
dns_qpchain_init(qp, &chain);
|
|
check_predecessors_withchain(qp, check, NULL);
|
|
check_predecessors_withchain(qp, check, &chain);
|
|
}
|
|
|
|
ISC_RUN_TEST_IMPL(predecessors) {
|
|
dns_qp_t *qp = NULL;
|
|
const char insert[][16] = {
|
|
"a.", "b.", "c.b.a.", "e.d.c.b.a.",
|
|
"c.b.b.", "c.d.", "a.b.c.d.", "a.b.c.d.e.",
|
|
"b.a.", "x.k.c.d.", "moog.", "mooker.",
|
|
"mooko.", "moon.", "moops.", ""
|
|
};
|
|
int i = 0;
|
|
|
|
dns_qp_create(mctx, &string_methods, NULL, &qp);
|
|
while (insert[i][0] != '\0') {
|
|
insert_str(qp, insert[i++]);
|
|
}
|
|
|
|
/* first check: no root label in the database */
|
|
static struct check_predecessors check1[] = {
|
|
{ ".", "moops.", ISC_R_NOTFOUND, 0 },
|
|
{ "a.", "moops.", ISC_R_SUCCESS, 0 },
|
|
{ "b.a.", "a.", ISC_R_SUCCESS, 14 },
|
|
{ "b.", "e.d.c.b.a.", ISC_R_SUCCESS, 11 },
|
|
{ "aaa.a.", "a.", DNS_R_PARTIALMATCH, 14 },
|
|
{ "ddd.a.", "e.d.c.b.a.", DNS_R_PARTIALMATCH, 11 },
|
|
{ "d.c.", "c.b.b.", ISC_R_NOTFOUND, 9 },
|
|
{ "1.2.c.b.a.", "c.b.a.", DNS_R_PARTIALMATCH, 12 },
|
|
{ "a.b.c.e.f.", "a.b.c.d.e.", ISC_R_NOTFOUND, 5 },
|
|
{ "z.y.x.", "moops.", ISC_R_NOTFOUND, 0 },
|
|
{ "w.c.d.", "x.k.c.d.", DNS_R_PARTIALMATCH, 6 },
|
|
{ "z.z.z.z.k.c.d.", "x.k.c.d.", DNS_R_PARTIALMATCH, 6 },
|
|
{ "w.k.c.d.", "a.b.c.d.", DNS_R_PARTIALMATCH, 7 },
|
|
{ "d.a.", "e.d.c.b.a.", DNS_R_PARTIALMATCH, 11 },
|
|
{ "0.b.c.d.e.", "x.k.c.d.", ISC_R_NOTFOUND, 6 },
|
|
{ "b.d.", "c.b.b.", ISC_R_NOTFOUND, 9 },
|
|
{ "mon.", "a.b.c.d.e.", ISC_R_NOTFOUND, 5 },
|
|
{ "moor.", "moops.", ISC_R_NOTFOUND, 0 },
|
|
{ "mopbop.", "moops.", ISC_R_NOTFOUND, 0 },
|
|
{ "moppop.", "moops.", ISC_R_NOTFOUND, 0 },
|
|
{ "mopps.", "moops.", ISC_R_NOTFOUND, 0 },
|
|
{ "mopzop.", "moops.", ISC_R_NOTFOUND, 0 },
|
|
{ "mop.", "moops.", ISC_R_NOTFOUND, 0 },
|
|
{ "monbop.", "a.b.c.d.e.", ISC_R_NOTFOUND, 5 },
|
|
{ "monpop.", "a.b.c.d.e.", ISC_R_NOTFOUND, 5 },
|
|
{ "monps.", "a.b.c.d.e.", ISC_R_NOTFOUND, 5 },
|
|
{ "monzop.", "a.b.c.d.e.", ISC_R_NOTFOUND, 5 },
|
|
{ "mon.", "a.b.c.d.e.", ISC_R_NOTFOUND, 5 },
|
|
{ "moop.", "moon.", ISC_R_NOTFOUND, 1 },
|
|
{ "moopser.", "moops.", ISC_R_NOTFOUND, 0 },
|
|
{ "monky.", "a.b.c.d.e.", ISC_R_NOTFOUND, 5 },
|
|
{ "monkey.", "a.b.c.d.e.", ISC_R_NOTFOUND, 5 },
|
|
{ "monker.", "a.b.c.d.e.", ISC_R_NOTFOUND, 5 },
|
|
{ NULL, NULL, 0, 0 }
|
|
};
|
|
|
|
check_predecessors(qp, check1);
|
|
|
|
/* second check: add a root label and try again */
|
|
const char root[16] = ".";
|
|
insert_str(qp, root);
|
|
|
|
static struct check_predecessors check2[] = {
|
|
{ ".", "moops.", ISC_R_SUCCESS, 0 },
|
|
{ "a.", ".", ISC_R_SUCCESS, 15 },
|
|
{ "b.a.", "a.", ISC_R_SUCCESS, 14 },
|
|
{ "b.", "e.d.c.b.a.", ISC_R_SUCCESS, 11 },
|
|
{ "aaa.a.", "a.", DNS_R_PARTIALMATCH, 14 },
|
|
{ "ddd.a.", "e.d.c.b.a.", DNS_R_PARTIALMATCH, 11 },
|
|
{ "d.c.", "c.b.b.", DNS_R_PARTIALMATCH, 9 },
|
|
{ "1.2.c.b.a.", "c.b.a.", DNS_R_PARTIALMATCH, 12 },
|
|
{ "a.b.c.e.f.", "a.b.c.d.e.", DNS_R_PARTIALMATCH, 5 },
|
|
{ "z.y.x.", "moops.", DNS_R_PARTIALMATCH, 0 },
|
|
{ "w.c.d.", "x.k.c.d.", DNS_R_PARTIALMATCH, 6 },
|
|
{ "z.z.z.z.k.c.d.", "x.k.c.d.", DNS_R_PARTIALMATCH, 6 },
|
|
{ "w.k.c.d.", "a.b.c.d.", DNS_R_PARTIALMATCH, 7 },
|
|
{ "d.a.", "e.d.c.b.a.", DNS_R_PARTIALMATCH, 11 },
|
|
{ "0.b.c.d.e.", "x.k.c.d.", DNS_R_PARTIALMATCH, 6 },
|
|
{ "mon.", "a.b.c.d.e.", DNS_R_PARTIALMATCH, 5 },
|
|
{ "moor.", "moops.", DNS_R_PARTIALMATCH, 0 },
|
|
{ "mopbop.", "moops.", DNS_R_PARTIALMATCH, 0 },
|
|
{ "moppop.", "moops.", DNS_R_PARTIALMATCH, 0 },
|
|
{ "mopps.", "moops.", DNS_R_PARTIALMATCH, 0 },
|
|
{ "mopzop.", "moops.", DNS_R_PARTIALMATCH, 0 },
|
|
{ "mop.", "moops.", DNS_R_PARTIALMATCH, 0 },
|
|
{ "monbop.", "a.b.c.d.e.", DNS_R_PARTIALMATCH, 5 },
|
|
{ "monpop.", "a.b.c.d.e.", DNS_R_PARTIALMATCH, 5 },
|
|
{ "monps.", "a.b.c.d.e.", DNS_R_PARTIALMATCH, 5 },
|
|
{ "monzop.", "a.b.c.d.e.", DNS_R_PARTIALMATCH, 5 },
|
|
{ "mon.", "a.b.c.d.e.", DNS_R_PARTIALMATCH, 5 },
|
|
{ "moop.", "moon.", DNS_R_PARTIALMATCH, 1 },
|
|
{ "moopser.", "moops.", DNS_R_PARTIALMATCH, 0 },
|
|
{ "monky.", "a.b.c.d.e.", DNS_R_PARTIALMATCH, 5 },
|
|
{ "monkey.", "a.b.c.d.e.", DNS_R_PARTIALMATCH, 5 },
|
|
{ "monker.", "a.b.c.d.e.", DNS_R_PARTIALMATCH, 5 },
|
|
{ NULL, NULL, 0, 0 }
|
|
};
|
|
|
|
check_predecessors(qp, check2);
|
|
|
|
dns_qp_destroy(&qp);
|
|
}
|
|
|
|
/*
|
|
* this is a regression test for an infinite loop that could
|
|
* previously occur in fix_iterator()
|
|
*/
|
|
ISC_RUN_TEST_IMPL(fixiterator) {
|
|
dns_qp_t *qp = NULL;
|
|
const char insert1[][32] = { "dynamic.",
|
|
"a.dynamic.",
|
|
"aaaa.dynamic.",
|
|
"cdnskey.dynamic.",
|
|
"cds.dynamic.",
|
|
"cname.dynamic.",
|
|
"dname.dynamic.",
|
|
"dnskey.dynamic.",
|
|
"ds.dynamic.",
|
|
"mx.dynamic.",
|
|
"ns.dynamic.",
|
|
"nsec.dynamic.",
|
|
"private-cdnskey.dynamic.",
|
|
"private-dnskey.dynamic.",
|
|
"rrsig.dynamic.",
|
|
"txt.dynamic.",
|
|
"trailing.",
|
|
"" };
|
|
int i = 0;
|
|
|
|
dns_qp_create(mctx, &string_methods, NULL, &qp);
|
|
while (insert1[i][0] != '\0') {
|
|
insert_str(qp, insert1[i++]);
|
|
}
|
|
|
|
static struct check_predecessors check1[] = {
|
|
{ "newtext.dynamic.", "mx.dynamic.", DNS_R_PARTIALMATCH, 7 },
|
|
{ "nsd.dynamic.", "ns.dynamic.", DNS_R_PARTIALMATCH, 6 },
|
|
{ "nsf.dynamic.", "nsec.dynamic.", DNS_R_PARTIALMATCH, 5 },
|
|
{ "d.", "trailing.", ISC_R_NOTFOUND, 0 },
|
|
{ "absent.", "trailing.", ISC_R_NOTFOUND, 0 },
|
|
{ "nonexistent.", "txt.dynamic.", ISC_R_NOTFOUND, 1 },
|
|
{ "wayback.", "trailing.", ISC_R_NOTFOUND, 0 },
|
|
{ NULL, NULL, 0, 0 }
|
|
};
|
|
|
|
check_predecessors(qp, check1);
|
|
dns_qp_destroy(&qp);
|
|
|
|
const char insert2[][64] = { ".", "abb.", "abc.", "" };
|
|
i = 0;
|
|
|
|
dns_qp_create(mctx, &string_methods, NULL, &qp);
|
|
while (insert2[i][0] != '\0') {
|
|
insert_str(qp, insert2[i++]);
|
|
}
|
|
|
|
static struct check_predecessors check2[] = {
|
|
{ "acb.", "abc.", DNS_R_PARTIALMATCH, 0 },
|
|
{ "acc.", "abc.", DNS_R_PARTIALMATCH, 0 },
|
|
{ "abbb.", "abb.", DNS_R_PARTIALMATCH, 1 },
|
|
{ "aab.", ".", DNS_R_PARTIALMATCH, 2 },
|
|
{ NULL, NULL, 0, 0 }
|
|
};
|
|
|
|
check_predecessors(qp, check2);
|
|
dns_qp_destroy(&qp);
|
|
|
|
const char insert3[][64] = { "example.",
|
|
"key-is-13779.example.",
|
|
"key-is-14779.example.",
|
|
"key-not-13779.example.",
|
|
"key-not-14779.example.",
|
|
"" };
|
|
i = 0;
|
|
|
|
dns_qp_create(mctx, &string_methods, NULL, &qp);
|
|
while (insert3[i][0] != '\0') {
|
|
insert_str(qp, insert3[i++]);
|
|
}
|
|
|
|
static struct check_predecessors check3[] = { { "key-is-21556.example.",
|
|
"key-is-14779.example.",
|
|
DNS_R_PARTIALMATCH, 2 },
|
|
{ NULL, NULL, 0, 0 } };
|
|
|
|
check_predecessors(qp, check3);
|
|
dns_qp_destroy(&qp);
|
|
|
|
const char insert4[][64] = { ".", "\\000.", "\\000.\\000.",
|
|
"\\000\\009.", "" };
|
|
i = 0;
|
|
|
|
dns_qp_create(mctx, &string_methods, NULL, &qp);
|
|
while (insert4[i][0] != '\0') {
|
|
insert_str(qp, insert4[i++]);
|
|
}
|
|
|
|
static struct check_predecessors check4[] = {
|
|
{ "\\007.", "\\000\\009.", DNS_R_PARTIALMATCH, 0 },
|
|
{ "\\009.", "\\000\\009.", DNS_R_PARTIALMATCH, 0 },
|
|
{ "\\045.", "\\000\\009.", DNS_R_PARTIALMATCH, 0 },
|
|
{ "\\044.", "\\000\\009.", DNS_R_PARTIALMATCH, 0 },
|
|
{ "\\000.", ".", ISC_R_SUCCESS, 3 },
|
|
{ NULL, NULL, 0, 0 },
|
|
};
|
|
|
|
check_predecessors(qp, check4);
|
|
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_ENTRY(qpchain)
|
|
ISC_TEST_ENTRY(predecessors)
|
|
ISC_TEST_ENTRY(fixiterator)
|
|
ISC_TEST_LIST_END
|
|
|
|
ISC_TEST_MAIN
|