From 63262fd0f4058175dcd39931d172f66c91c0660e Mon Sep 17 00:00:00 2001 From: Matthijs Mekking Date: Tue, 9 Dec 2025 12:37:20 +0100 Subject: [PATCH] Implement dns_dbiterator_seek3 This is a new seek function for dbiterator that is meant to find an NSEC3 node in a zone database. The difference with dns_dbiterator_seek is that if the node does not exist, this seek function will point the iterator to the next NSEC3 name. (cherry picked from commit 41159e9062b40495aca6178da54d60bbd2f34378) --- lib/dns/dbiterator.c | 8 +++ lib/dns/include/dns/dbiterator.h | 26 +++++++++ lib/dns/qpcache.c | 16 +++++- lib/dns/qpzone.c | 57 ++++++++++++++++++- lib/dns/rbtdb.c | 94 +++++++++++++++++++++++++++++++- lib/dns/sdlz.c | 16 +++++- tests/dns/dbiterator_test.c | 32 ++++++++--- 7 files changed, 228 insertions(+), 21 deletions(-) diff --git a/lib/dns/dbiterator.c b/lib/dns/dbiterator.c index 8f202871a6..23b0abc3bc 100644 --- a/lib/dns/dbiterator.c +++ b/lib/dns/dbiterator.c @@ -68,6 +68,14 @@ dns__dbiterator_seek(dns_dbiterator_t *iterator, return iterator->methods->seek(iterator, name DNS__DB_FLARG_PASS); } +isc_result_t +dns__dbiterator_seek3(dns_dbiterator_t *iterator, + const dns_name_t *name DNS__DB_FLARG) { + REQUIRE(DNS_DBITERATOR_VALID(iterator)); + + return iterator->methods->seek3(iterator, name DNS__DB_FLARG_PASS); +} + isc_result_t dns__dbiterator_prev(dns_dbiterator_t *iterator DNS__DB_FLARG) { /* diff --git a/lib/dns/include/dns/dbiterator.h b/lib/dns/include/dns/dbiterator.h index 306b28b918..0695a76e29 100644 --- a/lib/dns/include/dns/dbiterator.h +++ b/lib/dns/include/dns/dbiterator.h @@ -73,6 +73,8 @@ typedef struct dns_dbiteratormethods { isc_result_t (*last)(dns_dbiterator_t *iterator DNS__DB_FLARG); isc_result_t (*seek)(dns_dbiterator_t *iterator, const dns_name_t *name DNS__DB_FLARG); + isc_result_t (*seek3)(dns_dbiterator_t *iterator, + const dns_name_t *name DNS__DB_FLARG); isc_result_t (*prev)(dns_dbiterator_t *iterator DNS__DB_FLARG); isc_result_t (*next)(dns_dbiterator_t *iterator DNS__DB_FLARG); isc_result_t (*current)(dns_dbiterator_t *iterator, @@ -176,6 +178,30 @@ dns__dbiterator_seek(dns_dbiterator_t *iterator, *\li Other results are possible, depending on the DB implementation. */ +#define dns_dbiterator_seek3(iterator, name) \ + dns__dbiterator_seek3(iterator, name DNS__DB_FILELINE) +isc_result_t +dns__dbiterator_seek3(dns_dbiterator_t *iterator, + const dns_name_t *name DNS__DB_FLARG); +/*%< + * Move the node cursor to the node with NSEC3 name 'name'. + * If not found, the iterator is set to the next name. + * + * Requires: + *\li 'iterator' is a valid iterator. + * + *\li 'name' is a valid name. + * + * Returns: + *\li #ISC_R_SUCCESS + *\li #ISC_R_NOTFOUND + *\li #ISC_R_NOMORE There are no NSEC3 nodes in the database. + *\li #ISC_R_NOTIMPLEMENTED + * (this function is only implemented for NSEC3 only iterators) + * + *\li Other results are possible, depending on the DB implementation. + */ + #define dns_dbiterator_prev(iterator) \ dns__dbiterator_prev(iterator DNS__DB_FILELINE) isc_result_t diff --git a/lib/dns/qpcache.c b/lib/dns/qpcache.c index 3f29643f07..8ceb23acc7 100644 --- a/lib/dns/qpcache.c +++ b/lib/dns/qpcache.c @@ -417,6 +417,9 @@ static isc_result_t dbiterator_seek(dns_dbiterator_t *iterator, const dns_name_t *name DNS__DB_FLARG); static isc_result_t +dbiterator_seek3(dns_dbiterator_t *iterator, + const dns_name_t *name DNS__DB_FLARG); +static isc_result_t dbiterator_prev(dns_dbiterator_t *iterator DNS__DB_FLARG); static isc_result_t dbiterator_next(dns_dbiterator_t *iterator DNS__DB_FLARG); @@ -429,9 +432,10 @@ static isc_result_t dbiterator_origin(dns_dbiterator_t *iterator, dns_name_t *name); static dns_dbiteratormethods_t dbiterator_methods = { - dbiterator_destroy, dbiterator_first, dbiterator_last, - dbiterator_seek, dbiterator_prev, dbiterator_next, - dbiterator_current, dbiterator_pause, dbiterator_origin + dbiterator_destroy, dbiterator_first, dbiterator_last, + dbiterator_seek, dbiterator_seek3, dbiterator_prev, + dbiterator_next, dbiterator_current, dbiterator_pause, + dbiterator_origin }; /* @@ -4098,6 +4102,12 @@ dbiterator_seek(dns_dbiterator_t *iterator, return result; } +static isc_result_t +dbiterator_seek3(dns_dbiterator_t *iterator ISC_ATTR_UNUSED, + const dns_name_t *name ISC_ATTR_UNUSED DNS__DB_FLARG) { + return ISC_R_NOTIMPLEMENTED; +} + static isc_result_t dbiterator_prev(dns_dbiterator_t *iterator DNS__DB_FLARG) { isc_result_t result; diff --git a/lib/dns/qpzone.c b/lib/dns/qpzone.c index 9b36982348..cd576bc33c 100644 --- a/lib/dns/qpzone.c +++ b/lib/dns/qpzone.c @@ -350,6 +350,9 @@ static isc_result_t dbiterator_seek(dns_dbiterator_t *iterator, const dns_name_t *name DNS__DB_FLARG); static isc_result_t +dbiterator_seek3(dns_dbiterator_t *iterator, + const dns_name_t *name DNS__DB_FLARG); +static isc_result_t dbiterator_prev(dns_dbiterator_t *iterator DNS__DB_FLARG); static isc_result_t dbiterator_next(dns_dbiterator_t *iterator DNS__DB_FLARG); @@ -362,9 +365,10 @@ static isc_result_t dbiterator_origin(dns_dbiterator_t *iterator, dns_name_t *name); static dns_dbiteratormethods_t dbiterator_methods = { - dbiterator_destroy, dbiterator_first, dbiterator_last, - dbiterator_seek, dbiterator_prev, dbiterator_next, - dbiterator_current, dbiterator_pause, dbiterator_origin + dbiterator_destroy, dbiterator_first, dbiterator_last, + dbiterator_seek, dbiterator_seek3, dbiterator_prev, + dbiterator_next, dbiterator_current, dbiterator_pause, + dbiterator_origin }; typedef struct qpdb_dbiterator { @@ -4379,6 +4383,53 @@ dbiterator_seek(dns_dbiterator_t *iterator, return result; } +static isc_result_t +dbiterator_seek3(dns_dbiterator_t *iterator, + const dns_name_t *name DNS__DB_FLARG) { + isc_result_t result; + qpdb_dbiterator_t *qpdbiter = (qpdb_dbiterator_t *)iterator; + + if (qpdbiter->result != ISC_R_SUCCESS && + qpdbiter->result != ISC_R_NOTFOUND && + qpdbiter->result != DNS_R_PARTIALMATCH && + qpdbiter->result != ISC_R_NOMORE) + { + return qpdbiter->result; + } + + if (qpdbiter->nsec3mode != nsec3only) { + return ISC_R_NOTIMPLEMENTED; + } + + dereference_iter_node(qpdbiter DNS__DB_FLARG_PASS); + + qpdbiter->current = &qpdbiter->nsec3iter; + result = dns_qp_lookup(qpdbiter->nsnap, name, NULL, qpdbiter->current, + NULL, (void **)&qpdbiter->node, NULL); + + switch (result) { + case ISC_R_SUCCESS: + reference_iter_node(qpdbiter DNS__DB_FLARG_PASS); + break; + case DNS_R_PARTIALMATCH: + /* dbiterator_next() will dereference the node */ + reference_iter_node(qpdbiter DNS__DB_FLARG_PASS); + + result = dbiterator_next(iterator); + if (result == ISC_R_NOMORE) { + result = dbiterator_first(iterator); + } + break; + case ISC_R_NOTFOUND: + default: + break; + } + + qpdbiter->result = result; + + return qpdbiter->result; +} + static isc_result_t dbiterator_prev(dns_dbiterator_t *iterator DNS__DB_FLARG) { isc_result_t result; diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index 0a90e216c4..2abaab2901 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -199,6 +199,9 @@ static isc_result_t dbiterator_seek(dns_dbiterator_t *iterator, const dns_name_t *name DNS__DB_FLARG); static isc_result_t +dbiterator_seek3(dns_dbiterator_t *iterator, + const dns_name_t *name DNS__DB_FLARG); +static isc_result_t dbiterator_prev(dns_dbiterator_t *iterator DNS__DB_FLARG); static isc_result_t dbiterator_next(dns_dbiterator_t *iterator DNS__DB_FLARG); @@ -211,9 +214,10 @@ static isc_result_t dbiterator_origin(dns_dbiterator_t *iterator, dns_name_t *name); static dns_dbiteratormethods_t dbiterator_methods = { - dbiterator_destroy, dbiterator_first, dbiterator_last, - dbiterator_seek, dbiterator_prev, dbiterator_next, - dbiterator_current, dbiterator_pause, dbiterator_origin + dbiterator_destroy, dbiterator_first, dbiterator_last, + dbiterator_seek, dbiterator_seek3, dbiterator_prev, + dbiterator_next, dbiterator_current, dbiterator_pause, + dbiterator_origin }; /* @@ -4658,6 +4662,90 @@ dbiterator_seek(dns_dbiterator_t *iterator, return result; } +static isc_result_t +dbiterator_seek3(dns_dbiterator_t *iterator, + const dns_name_t *name DNS__DB_FLARG) { + isc_result_t result, tresult; + rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator; + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db; + dns_name_t *iname = NULL, *origin = NULL; + + if (rbtdbiter->result != ISC_R_SUCCESS && + rbtdbiter->result != ISC_R_NOTFOUND && + rbtdbiter->result != DNS_R_PARTIALMATCH && + rbtdbiter->result != ISC_R_NOMORE) + { + return rbtdbiter->result; + } + + if (rbtdbiter->nsec3mode != nsec3only) { + return ISC_R_NOTIMPLEMENTED; + } + + if (rbtdbiter->paused) { + resume_iteration(rbtdbiter); + } + + dereference_iter_node(rbtdbiter DNS__DB_FLARG_PASS); + + iname = dns_fixedname_name(&rbtdbiter->name); + origin = dns_fixedname_name(&rbtdbiter->origin); + dns_rbtnodechain_reset(&rbtdbiter->chain); + dns_rbtnodechain_reset(&rbtdbiter->nsec3chain); + + rbtdbiter->current = &rbtdbiter->nsec3chain; + result = dns_rbt_findnode(rbtdb->nsec3, name, NULL, &rbtdbiter->node, + rbtdbiter->current, DNS_RBTFIND_EMPTYDATA, + NULL, NULL); + + if (result == ISC_R_SUCCESS) { + tresult = dns_rbtnodechain_current(rbtdbiter->current, iname, + origin, NULL); + + reference_iter_node(rbtdbiter DNS__DB_FLARG_PASS); + + if (tresult == ISC_R_SUCCESS) { + rbtdbiter->new_origin = true; + } else { + result = tresult; + rbtdbiter->node = NULL; + } + } else if (result == DNS_R_PARTIALMATCH) { + tresult = dns_rbtnodechain_current(rbtdbiter->current, iname, + origin, NULL); + + /* dbiterator_next() will dereference the node */ + reference_iter_node(rbtdbiter DNS__DB_FLARG_PASS); + + if (tresult == ISC_R_SUCCESS) { + rbtdbiter->new_origin = true; + + result = dbiterator_next(iterator); + if (result == ISC_R_NOMORE) { + result = dbiterator_first(iterator); + } + + tresult = dns_rbtnodechain_current(rbtdbiter->current, + iname, origin, NULL); + if (tresult == ISC_R_SUCCESS) { + rbtdbiter->new_origin = true; + } else { + result = tresult; + rbtdbiter->node = NULL; + } + } else { + result = tresult; + rbtdbiter->node = NULL; + } + } else { + rbtdbiter->node = NULL; + } + + rbtdbiter->result = result; + + return result; +} + static isc_result_t dbiterator_prev(dns_dbiterator_t *iterator DNS__DB_FLARG) { isc_result_t result; diff --git a/lib/dns/sdlz.c b/lib/dns/sdlz.c index dcd5ab9ecb..c507902549 100644 --- a/lib/dns/sdlz.c +++ b/lib/dns/sdlz.c @@ -201,6 +201,9 @@ static isc_result_t dbiterator_seek(dns_dbiterator_t *iterator, const dns_name_t *name DNS__DB_FLARG); static isc_result_t +dbiterator_seek3(dns_dbiterator_t *iterator, + const dns_name_t *name DNS__DB_FLARG); +static isc_result_t dbiterator_prev(dns_dbiterator_t *iterator DNS__DB_FLARG); static isc_result_t dbiterator_next(dns_dbiterator_t *iterator DNS__DB_FLARG); @@ -213,9 +216,10 @@ static isc_result_t dbiterator_origin(dns_dbiterator_t *iterator, dns_name_t *name); static dns_dbiteratormethods_t dbiterator_methods = { - dbiterator_destroy, dbiterator_first, dbiterator_last, - dbiterator_seek, dbiterator_prev, dbiterator_next, - dbiterator_current, dbiterator_pause, dbiterator_origin + dbiterator_destroy, dbiterator_first, dbiterator_last, + dbiterator_seek, dbiterator_seek3, dbiterator_prev, + dbiterator_next, dbiterator_current, dbiterator_pause, + dbiterator_origin }; /* @@ -1243,6 +1247,12 @@ dbiterator_seek(dns_dbiterator_t *iterator, return ISC_R_NOTFOUND; } +static isc_result_t +dbiterator_seek3(dns_dbiterator_t *iterator ISC_ATTR_UNUSED, + const dns_name_t *name ISC_ATTR_UNUSED DNS__DB_FLARG) { + return ISC_R_NOTIMPLEMENTED; +} + static isc_result_t dbiterator_prev(dns_dbiterator_t *iterator DNS__DB_FLARG) { sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)iterator; diff --git a/tests/dns/dbiterator_test.c b/tests/dns/dbiterator_test.c index 30980700b9..07a63c680d 100644 --- a/tests/dns/dbiterator_test.c +++ b/tests/dns/dbiterator_test.c @@ -182,10 +182,10 @@ ISC_RUN_TEST_IMPL(reverse_nsec3) { /* seek: walk database starting at a particular node */ static void -test_seek_node(const char *filename, int flags, int nodes) { - isc_result_t result; +test_seek_node(const char *filename, bool nsec3, int flags, int nodes) { + isc_result_t result, result3; dns_db_t *db = NULL; - dns_dbiterator_t *iter = NULL; + dns_dbiterator_t *iter = NULL, *iter3 = NULL; dns_dbnode_t *node = NULL; dns_name_t *name, *seekname; dns_fixedname_t f1, f2; @@ -200,6 +200,9 @@ test_seek_node(const char *filename, int flags, int nodes) { result = dns_db_createiterator(db, flags, &iter); assert_int_equal(result, ISC_R_SUCCESS); + result3 = dns_db_createiterator(db, flags, &iter3); + assert_int_equal(result3, ISC_R_SUCCESS); + result = make_name("c." TEST_ORIGIN, seekname); assert_int_equal(result, ISC_R_SUCCESS); @@ -207,6 +210,14 @@ test_seek_node(const char *filename, int flags, int nodes) { if (flags == DNS_DB_NSEC3ONLY) { /* "c" isn't in the NSEC3 tree but the origin node is */ assert_int_equal(result, DNS_R_PARTIALMATCH); + + /* NSEC3 iterator */ + result3 = dns_dbiterator_seek3(iter3, seekname); + if (nsec3) { + assert_int_equal(result3, ISC_R_SUCCESS); + } else { + assert_int_equal(result3, ISC_R_NOMORE); + } } else { assert_int_equal(result, ISC_R_SUCCESS); } @@ -244,26 +255,29 @@ test_seek_node(const char *filename, int flags, int nodes) { assert_int_equal(i, nodes); dns_dbiterator_destroy(&iter); + dns_dbiterator_destroy(&iter3); dns_db_detach(&db); } ISC_RUN_TEST_IMPL(seek_node) { UNUSED(state); - test_seek_node(TESTS_DIR "/testdata/dbiterator/zone1.data", 0, 9); - test_seek_node(TESTS_DIR "/testdata/dbiterator/zone1.data", + test_seek_node(TESTS_DIR "/testdata/dbiterator/zone1.data", false, 0, + 9); + test_seek_node(TESTS_DIR "/testdata/dbiterator/zone1.data", false, DNS_DB_NONSEC3, 9); - test_seek_node(TESTS_DIR "/testdata/dbiterator/zone1.data", + test_seek_node(TESTS_DIR "/testdata/dbiterator/zone1.data", false, DNS_DB_NSEC3ONLY, 0); } ISC_RUN_TEST_IMPL(seek_node_nsec3) { UNUSED(state); - test_seek_node(TESTS_DIR "/testdata/dbiterator/zone2.data", 0, 29); - test_seek_node(TESTS_DIR "/testdata/dbiterator/zone2.data", + test_seek_node(TESTS_DIR "/testdata/dbiterator/zone2.data", true, 0, + 29); + test_seek_node(TESTS_DIR "/testdata/dbiterator/zone2.data", true, DNS_DB_NONSEC3, 9); - test_seek_node(TESTS_DIR "/testdata/dbiterator/zone2.data", + test_seek_node(TESTS_DIR "/testdata/dbiterator/zone2.data", true, DNS_DB_NSEC3ONLY, 0); }