From 947bc0a4320c68f5c556c68d414481b8d7bff0a4 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Sun, 5 Nov 2023 13:42:42 +0100 Subject: [PATCH 1/3] add an iterator argument to dns_qp_lookup() the 'predecessor' argument to dns_qp_lookup() turns out not to be sufficient for our needs: the predecessor node in a QP database could have become "empty" (for the current version) because of an update or because cache data expired, and in that case the caller would have to iterate more than one step back to find the predecessor node that it needs. it may also be necessary for a caller to iterate forward, in order to determine whether a node has any children. for both of these reasons, we now replace the 'predecessor' argument with an 'iter' argument. if set, this points to memory with enough space for a dns_qpiter object. when an exact match is found by the lookup, the iterator will be pointing to the matching node. if not, it will be pointing to the lexical predecessor of the nae that was searched for. a dns_qpiter_current() method has been added for examining the current value of the iterator without moving it in either direction. --- lib/dns/include/dns/qp.h | 38 ++++++++++++++------ lib/dns/qp.c | 75 ++++++++++++++++++++++++++++------------ tests/dns/qp_test.c | 42 ++++++++++++++++++++-- 3 files changed, 119 insertions(+), 36 deletions(-) diff --git a/lib/dns/include/dns/qp.h b/lib/dns/include/dns/qp.h index cb92d4f78d..236d63faaf 100644 --- a/lib/dns/include/dns/qp.h +++ b/lib/dns/include/dns/qp.h @@ -528,8 +528,8 @@ dns_qp_getname(dns_qpreadable_t qpr, const dns_name_t *name, void **pval_r, isc_result_t dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name, - dns_name_t *foundname, dns_name_t *predecessor, - dns_qpchain_t *chain, void **pval_r, uint32_t *ival_r); + dns_name_t *foundname, dns_qpiter_t *iter, dns_qpchain_t *chain, + void **pval_r, uint32_t *ival_r); /*%< * Look up a leaf in a qp-trie that is equal to, or an ancestor domain of, * 'name'. @@ -547,9 +547,9 @@ dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name, * ISC_R_SUCCESS then it terminates at the name that was requested. * If the result is ISC_R_NOTFOUND, 'chain' will not be updated. * - * If 'predecessor' is not NULL, it will be updated to contain the - * closest predecessor of the searched-for name that exists in the - * trie. + * If 'iter' is not NULL, it will be updated to point to a QP iterator + * which is pointed at the searched-for name if it exists in the trie, + * or the closest predecessor if it doesn't. * * The leaf data for the node that was found will be assigned to * whichever of `*pval_r` and `*ival_r` are not NULL, unless the @@ -559,7 +559,7 @@ dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name, * \li `qpr` is a pointer to a readable qp-trie * \li `name` is a pointer to a valid `dns_name_t` * \li `foundname` is a pointer to a valid `dns_name_t` with - * buffer and offset space available, or is NULL. + * buffer and offset space available, or is NULL * * Returns: * \li ISC_R_SUCCESS if an exact match was found @@ -666,6 +666,24 @@ dns_qpiter_prev(dns_qpiter_t *qpi, dns_name_t *name, void **pval_r, * \li ISC_R_NOMORE otherwise */ +isc_result_t +dns_qpiter_current(dns_qpiter_t *qpi, dns_name_t *name, void **pval_r, + uint32_t *ival_r); +/*%< + * Sets the values of `name`, `pval_r` and `ival_r` to those at the + * node currently pointed to by `qpi`, but without moving the iterator + * in either direction. If the iterator is not currently pointed at a + * leaf node, ISC_R_FAILURE is returned. + * Requires: + * + * \li `qpi` is a pointer to a valid qp iterator + * + * Returns: + * \li ISC_R_SUCCESS if a leaf was found and pval_r and ival_r were set + * \li ISC_R_FAILURE if the iterator is not initialized or not pointing + * at a leaf node + */ + void dns_qpchain_init(dns_qpreadable_t qpr, dns_qpchain_t *chain); /*%< @@ -673,7 +691,7 @@ dns_qpchain_init(dns_qpreadable_t qpr, dns_qpchain_t *chain); * * Requires: * \li `qpr` is a pointer to a valid qp-trie - * \li `chain` is not NULL. + * \li `chain` is not NULL */ unsigned int @@ -682,7 +700,7 @@ dns_qpchain_length(dns_qpchain_t *chain); * Returns the length of a QP chain. * * Requires: - * \li `chain` is a pointer to an initialized QP chain object. + * \li `chain` is a pointer to an initialized QP chain object */ void @@ -695,8 +713,8 @@ dns_qpchain_node(dns_qpchain_t *chain, unsigned int level, dns_name_t *name, * are not null. * * Requires: - * \li `chain` is a pointer to an initialized QP chain object. - * \li `level` is less than `chain->len`. + * \li `chain` is a pointer to an initialized QP chain object + * \li `level` is less than `chain->len` */ /*********************************************************************** diff --git a/lib/dns/qp.c b/lib/dns/qp.c index 24be5ce18f..ee78b98c30 100644 --- a/lib/dns/qp.c +++ b/lib/dns/qp.c @@ -1948,6 +1948,24 @@ dns_qpiter_prev(dns_qpiter_t *qpi, dns_name_t *name, void **pval_r, return (iterate(false, qpi, name, pval_r, ival_r)); } +isc_result_t +dns_qpiter_current(dns_qpiter_t *qpi, dns_name_t *name, void **pval_r, + uint32_t *ival_r) { + dns_qpnode_t *node = NULL; + + REQUIRE(QPITER_VALID(qpi)); + + node = qpi->stack[qpi->sp]; + if (node == NULL || is_branch(node)) { + return (ISC_R_FAILURE); + } + + SET_IF_NOT_NULL(pval_r, leaf_pval(node)); + SET_IF_NOT_NULL(ival_r, leaf_ival(node)); + maybe_set_name(qpi->qp, node, name); + return (ISC_R_SUCCESS); +} + /*********************************************************************** * * search @@ -2022,8 +2040,8 @@ prevleaf(dns_qpiter_t *it) { isc_result_t dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name, - dns_name_t *foundname, dns_name_t *predecessor, - dns_qpchain_t *chain, void **pval_r, uint32_t *ival_r) { + dns_name_t *foundname, dns_qpiter_t *iter, dns_qpchain_t *chain, + void **pval_r, uint32_t *ival_r) { dns_qpreader_t *qp = dns_qpreader(qpr); dns_qpkey_t search, found; size_t searchlen, foundlen; @@ -2032,6 +2050,7 @@ dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name, dns_qpchain_t oc; dns_qpiter_t it; bool matched = true; + bool getpred = true; REQUIRE(QP_VALID(qp)); REQUIRE(foundname == NULL || ISC_MAGIC_VALID(name, DNS_NAME_MAGIC)); @@ -2041,14 +2060,18 @@ dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name, if (chain == NULL) { chain = &oc; } + if (iter == NULL) { + iter = ⁢ + getpred = false; + } dns_qpchain_init(qp, chain); - dns_qpiter_init(qp, &it); + dns_qpiter_init(qp, iter); n = get_root(qp); if (n == NULL) { return (ISC_R_NOTFOUND); } - it.stack[0] = n; + iter->stack[0] = n; /* * Like `dns_qp_insert()`, we must find a leaf. However, we don't make a @@ -2093,11 +2116,11 @@ dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name, * the loop. */ n = branch_twig_ptr(qp, n, bit); - } else if (predecessor != NULL) { + } else if (getpred) { /* - * this branch is a dead end, but the caller wants - * the predecessor to the name we were searching - * for, so let's go find that. + * this branch is a dead end, but the caller + * passed us an iterator: position it at the + * predecessor node. */ dns_qpweight_t pos = branch_twig_pos(n, bit); if (pos == 0) { @@ -2106,8 +2129,8 @@ dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name, * the key we wanted, so we step back to * the predecessor using the iterator. */ - prevleaf(&it); - n = it.stack[it.sp]; + prevleaf(iter); + n = iter->stack[iter->sp]; } else { /* * the name we want would've been between @@ -2119,7 +2142,7 @@ dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name, n = twigs + pos - 1; while (is_branch(n)) { prefetch_twigs(qp, n); - it.stack[++it.sp] = n; + iter->stack[++iter->sp] = n; pos = branch_twigs_size(n) - 1; n = ref_ptr(qp, branch_twigs_ref(n) + pos); @@ -2143,7 +2166,7 @@ dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name, } } - it.stack[++it.sp] = n; + iter->stack[++iter->sp] = n; } /* do the keys differ, and if so, where? */ @@ -2151,20 +2174,26 @@ dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name, offset = qpkey_compare(search, searchlen, found, foundlen); /* - * if we've been asked to return the predecessor name, we - * work that out here. + * if we've been passed an iterator, we want it to point + * at the matching name in the case of an exact match, or at + * the predecessor name for a non-exact match. * - * if 'matched' is true, the search ended at a leaf. it's either - * an exact match or the immediate successor of the searched-for - * name, and in either case, we can use the qpiter stack we've - * constructed to step back to the predecessor. if 'matched' is - * false, then the search failed at a branch node, and we would - * have already found the predecessor. + * if 'matched' is true, then the search ended at a leaf. + * if it was not an exact match, then we're now pointing at + * the immediate successor of the searched-for name, and can + * use the qpiter stack we've constructed to step back to + * the predecessor. if it was an exact match, we don't need to + * do anything. + * + * if 'matched' is false, then the search failed at a branch + * node, and we would already have positioned the iterator + * at the predecessor. */ - if (predecessor != NULL && matched) { - prevleaf(&it); + if (getpred && matched) { + if (offset != QPKEY_EQUAL) { + prevleaf(iter); + } } - maybe_set_name(qp, it.stack[it.sp], predecessor); if (offset == QPKEY_EQUAL || offset == foundlen) { SET_IF_NOT_NULL(pval_r, leaf_pval(n)); diff --git a/tests/dns/qp_test.c b/tests/dns/qp_test.c index 39c9b688b5..d68ac263c1 100644 --- a/tests/dns/qp_test.c +++ b/tests/dns/qp_test.c @@ -204,6 +204,7 @@ ISC_RUN_TEST_IMPL(qpiter) { 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++) { @@ -253,15 +254,30 @@ ISC_RUN_TEST_IMPL(qpiter) { while (dns_qpiter_prev(&qpi, NULL, NULL, &ival) == ISC_R_SUCCESS) { - assert_int_equal(ival, order[--n]); + --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++]); + 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); @@ -575,16 +591,36 @@ check_predecessors(dns_qp_t *qp, struct check_predecessors check[]) { dns_name_t *pred = dns_fixedname_initname(&fn2); for (int i = 0; check[i].query != NULL; i++) { + dns_qpiter_t it; char *predname = NULL; dns_test_namefromstring(check[i].query, &fn1); - result = dns_qp_lookup(qp, name, NULL, pred, NULL, NULL, NULL); + result = dns_qp_lookup(qp, name, NULL, &it, NULL, 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, &predname, mctx); #if 0 fprintf(stderr, "... expected predecessor %s got %s\n", From 861290247615f66a5d0c2804c4c51a940a6047f1 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Sun, 3 Dec 2023 14:02:29 -0800 Subject: [PATCH 2/3] fix dns_qp_lookup() iterator bug in some cases it was possible for the iterator to be positioned in the wrong place by dns_qp_lookup(). previously, when a leaf node was found which matched the search key at its parent branch's offset point, but did not match after that point, the code incorrectly assumed the leaf it had found was a successor to the searched-for name, and stepped the iterator back to find a predecessor. however, it was possible for the non-matching leaf to be the predecessor, in which case stepping the iterator back was wrong. (for example: a branch contains "aba" and "abcd", and we are searching for "abcde". we step down to the twig matching the letter "c" in position 3. "abcd" is the predecessor of "abcde", so the iterator is already correctly positioned, but because the twig was an exact match, we would have moved it back one step to "aba".) this previously went unnoticed due to a mistake in the qp_test unit test, which had the wrong expected result for the test case that should have detected the error. both the code and the test have been fixed. --- lib/dns/qp.c | 16 ++++++++++------ tests/dns/qp_test.c | 4 ++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/dns/qp.c b/lib/dns/qp.c index ee78b98c30..f71f4dd925 100644 --- a/lib/dns/qp.c +++ b/lib/dns/qp.c @@ -2179,18 +2179,22 @@ dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name, * the predecessor name for a non-exact match. * * if 'matched' is true, then the search ended at a leaf. - * if it was not an exact match, then we're now pointing at - * the immediate successor of the searched-for name, and can - * use the qpiter stack we've constructed to step back to - * the predecessor. if it was an exact match, we don't need to - * do anything. + * if it was not an exact match, then we're now pointing + * at either the immediate predecessor or the immediate + * successor of the searched-for name; if successor, we can + * now use the qpiter stack we've constructed to step back to + * the predecessor. if we're pointed at the predecessor + * or it was an exact match, we don't need to do anything. * * if 'matched' is false, then the search failed at a branch * node, and we would already have positioned the iterator * at the predecessor. */ if (getpred && matched) { - if (offset != QPKEY_EQUAL) { + if (offset != QPKEY_EQUAL && + (offset <= searchlen && offset <= foundlen && + found[offset] > search[offset])) + { prevleaf(iter); } } diff --git a/tests/dns/qp_test.c b/tests/dns/qp_test.c index d68ac263c1..28ea2266b7 100644 --- a/tests/dns/qp_test.c +++ b/tests/dns/qp_test.c @@ -660,7 +660,7 @@ ISC_RUN_TEST_IMPL(predecessors) { { "a.b.c.e.f.", "a.b.c.d.e.", ISC_R_NOTFOUND }, { "z.y.x.", "a.b.c.d.e.", ISC_R_NOTFOUND }, { "w.c.d.", "x.k.c.d.", DNS_R_PARTIALMATCH }, - { "z.z.z.z.k.c.d.", "a.b.c.d.", DNS_R_PARTIALMATCH }, + { "z.z.z.z.k.c.d.", "x.k.c.d.", DNS_R_PARTIALMATCH }, { "w.k.c.d.", "a.b.c.d.", DNS_R_PARTIALMATCH }, { "d.a.", "e.d.c.b.a.", DNS_R_PARTIALMATCH }, { "0.b.c.d.e.", "x.k.c.d.", ISC_R_NOTFOUND }, @@ -686,7 +686,7 @@ ISC_RUN_TEST_IMPL(predecessors) { { "a.b.c.e.f.", "a.b.c.d.e.", DNS_R_PARTIALMATCH }, { "z.y.x.", "a.b.c.d.e.", DNS_R_PARTIALMATCH }, { "w.c.d.", "x.k.c.d.", DNS_R_PARTIALMATCH }, - { "z.z.z.z.k.c.d.", "a.b.c.d.", DNS_R_PARTIALMATCH }, + { "z.z.z.z.k.c.d.", "x.k.c.d.", DNS_R_PARTIALMATCH }, { "w.k.c.d.", "a.b.c.d.", DNS_R_PARTIALMATCH }, { "d.a.", "e.d.c.b.a.", DNS_R_PARTIALMATCH }, { "0.b.c.d.e.", "x.k.c.d.", DNS_R_PARTIALMATCH }, From 60a33ae6bb7ecdb59aaf78e8806c9ec129563765 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Mon, 4 Dec 2023 11:21:40 -0800 Subject: [PATCH 3/3] fix another dns_qp_lookup() iterator bug there was another edge case in which an iterator could be positioned at the wrong node after dns_qp_lookup(). when searching for a key, it's possible to reach a dead-end branch that doesn't match, because the branch offset point is *after* the point where the search key differs from the branch's contents. for example, if searching for the key "mop", we could reach a branch containing "moon" and "moor". the branch offset point - i.e., the point after which the branch's leaves differ from each other - is the fourth character ("n" or "r"). however, both leaves differ from the search key at position *three* ("o" or "p"). the old code failed to detect this condition, and would have incorrectly left the iterator pointing at some lower value and not at "moor". this has been fixed and the unit test now includes this scenario. --- lib/dns/qp.c | 86 +++++++++++++++++++++++++++++++++------------ tests/dns/qp_test.c | 26 ++++++++------ 2 files changed, 79 insertions(+), 33 deletions(-) diff --git a/lib/dns/qp.c b/lib/dns/qp.c index f71f4dd925..3a6d7147ad 100644 --- a/lib/dns/qp.c +++ b/lib/dns/qp.c @@ -2038,6 +2038,17 @@ prevleaf(dns_qpiter_t *it) { RUNTIME_CHECK(result == ISC_R_SUCCESS); } +static inline dns_qpnode_t * +greatest_leaf(dns_qpreadable_t qpr, dns_qpnode_t *n, dns_qpiter_t *iter) { + while (is_branch(n)) { + dns_qpref_t ref = branch_twigs_ref(n) + branch_twigs_size(n) - + 1; + iter->stack[++iter->sp] = n; + n = ref_ptr(qpr, ref); + } + return (n); +} + isc_result_t dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name, dns_name_t *foundname, dns_qpiter_t *iter, dns_qpchain_t *chain, @@ -2118,35 +2129,64 @@ dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name, n = branch_twig_ptr(qp, n, bit); } else if (getpred) { /* - * this branch is a dead end, but the caller - * passed us an iterator: position it at the - * predecessor node. + * this branch is a dead end. however, the caller + * passed us an iterator, so we need to find the + * predecessor of the searched-for-name. + * first step: find out if we've overshot + * the search key; we do that by finding an + * arbitrary leaf to compare against. */ - dns_qpweight_t pos = branch_twig_pos(n, bit); - if (pos == 0) { + size_t to; + dns_qpnode_t *least = n; + while (is_branch(least)) { + least = branch_twigs(qp, least); + } + foundlen = leaf_qpkey(qp, least, found); + to = qpkey_compare(search, searchlen, found, foundlen); + if (to == offset) { /* - * this entire branch is greater than - * the key we wanted, so we step back to - * the predecessor using the iterator. + * we're on the right branch, so find + * the best match. */ - prevleaf(iter); - n = iter->stack[iter->sp]; - } else { + + dns_qpweight_t pos = branch_twig_pos(n, bit); + if (pos == 0) { + /* + * every leaf in the branch is greater + * than the one we wanted; use the + * iterator to walk back to the + * predecessor. + */ + prevleaf(iter); + n = iter->stack[iter->sp--]; + } else { + /* + * the name we want would've been + * after some twig in this + * branch. point n to that twig, + * then walk down to the highest + * leaf in that subtree to get the + * predecessor. + */ + n = greatest_leaf(qp, twigs + pos - 1, + iter); + } + } else if (to <= searchlen && to <= foundlen && + search[to] < found[to]) + { /* - * the name we want would've been between - * two twigs in this branch. point n to the - * lesser of those, then walk down to the - * highest leaf in that subtree to get the + * every leaf is greater than the one + * we wanted, so iterate back to the * predecessor. */ - n = twigs + pos - 1; - while (is_branch(n)) { - prefetch_twigs(qp, n); - iter->stack[++iter->sp] = n; - pos = branch_twigs_size(n) - 1; - n = ref_ptr(qp, - branch_twigs_ref(n) + pos); - } + prevleaf(iter); + n = iter->stack[iter->sp--]; + } else { + /* + * every leaf is less than the one we + * wanted, so get the highest. + */ + n = greatest_leaf(qp, n, iter); } } else { /* diff --git a/tests/dns/qp_test.c b/tests/dns/qp_test.c index 28ea2266b7..b78281dfeb 100644 --- a/tests/dns/qp_test.c +++ b/tests/dns/qp_test.c @@ -635,11 +635,11 @@ check_predecessors(dns_qp_t *qp, struct check_predecessors check[]) { 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.", "" - }; + 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.", "mook.", + "moon.", "moops.", "" }; int i = 0; dns_qp_create(mctx, &string_methods, NULL, &qp); @@ -649,8 +649,8 @@ ISC_RUN_TEST_IMPL(predecessors) { /* first check: no root label in the database */ static struct check_predecessors check1[] = { - { ".", "a.b.c.d.e.", ISC_R_NOTFOUND }, - { "a.", "a.b.c.d.e.", ISC_R_SUCCESS }, + { ".", "moops.", ISC_R_NOTFOUND }, + { "a.", "moops.", ISC_R_SUCCESS }, { "b.a.", "a.", ISC_R_SUCCESS }, { "b.", "e.d.c.b.a.", ISC_R_SUCCESS }, { "aaa.a.", "a.", DNS_R_PARTIALMATCH }, @@ -658,13 +658,16 @@ ISC_RUN_TEST_IMPL(predecessors) { { "d.c.", "c.b.b.", ISC_R_NOTFOUND }, { "1.2.c.b.a.", "c.b.a.", DNS_R_PARTIALMATCH }, { "a.b.c.e.f.", "a.b.c.d.e.", ISC_R_NOTFOUND }, - { "z.y.x.", "a.b.c.d.e.", ISC_R_NOTFOUND }, + { "z.y.x.", "moops.", ISC_R_NOTFOUND }, { "w.c.d.", "x.k.c.d.", DNS_R_PARTIALMATCH }, { "z.z.z.z.k.c.d.", "x.k.c.d.", DNS_R_PARTIALMATCH }, { "w.k.c.d.", "a.b.c.d.", DNS_R_PARTIALMATCH }, { "d.a.", "e.d.c.b.a.", DNS_R_PARTIALMATCH }, { "0.b.c.d.e.", "x.k.c.d.", ISC_R_NOTFOUND }, { "b.d.", "c.b.b.", ISC_R_NOTFOUND }, + { "mon.", "a.b.c.d.e.", ISC_R_NOTFOUND }, + { "moor.", "moops.", ISC_R_NOTFOUND }, + { "mop.", "moops.", ISC_R_NOTFOUND }, { NULL, NULL, 0 } }; @@ -675,7 +678,7 @@ ISC_RUN_TEST_IMPL(predecessors) { insert_str(qp, root); static struct check_predecessors check2[] = { - { ".", "a.b.c.d.e.", ISC_R_SUCCESS }, + { ".", "moops.", ISC_R_SUCCESS }, { "a.", ".", ISC_R_SUCCESS }, { "b.a.", "a.", ISC_R_SUCCESS }, { "b.", "e.d.c.b.a.", ISC_R_SUCCESS }, @@ -684,12 +687,15 @@ ISC_RUN_TEST_IMPL(predecessors) { { "d.c.", "c.b.b.", DNS_R_PARTIALMATCH }, { "1.2.c.b.a.", "c.b.a.", DNS_R_PARTIALMATCH }, { "a.b.c.e.f.", "a.b.c.d.e.", DNS_R_PARTIALMATCH }, - { "z.y.x.", "a.b.c.d.e.", DNS_R_PARTIALMATCH }, + { "z.y.x.", "moops.", DNS_R_PARTIALMATCH }, { "w.c.d.", "x.k.c.d.", DNS_R_PARTIALMATCH }, { "z.z.z.z.k.c.d.", "x.k.c.d.", DNS_R_PARTIALMATCH }, { "w.k.c.d.", "a.b.c.d.", DNS_R_PARTIALMATCH }, { "d.a.", "e.d.c.b.a.", DNS_R_PARTIALMATCH }, { "0.b.c.d.e.", "x.k.c.d.", DNS_R_PARTIALMATCH }, + { "mon.", "a.b.c.d.e.", DNS_R_PARTIALMATCH }, + { "moor.", "moops.", DNS_R_PARTIALMATCH }, + { "mop.", "moops.", DNS_R_PARTIALMATCH }, { NULL, NULL, 0 } };