1 /* $NetBSD: dbiterator_test.c,v 1.5 2026/01/29 18:37:56 christos Exp $ */ 2 3 /* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * SPDX-License-Identifier: MPL-2.0 7 * 8 * This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 * 12 * See the COPYRIGHT file distributed with this work for additional 13 * information regarding copyright ownership. 14 */ 15 16 #include <inttypes.h> 17 #include <sched.h> /* IWYU pragma: keep */ 18 #include <setjmp.h> 19 #include <stdarg.h> 20 #include <stddef.h> 21 #include <stdlib.h> 22 #include <string.h> 23 #include <unistd.h> 24 25 #define UNIT_TESTING 26 #include <cmocka.h> 27 28 #include <isc/util.h> 29 30 #include <dns/db.h> 31 #include <dns/dbiterator.h> 32 #include <dns/name.h> 33 34 #include <tests/dns.h> 35 36 #define BUFLEN 255 37 #define BIGBUFLEN (64 * 1024) 38 #define TEST_ORIGIN "test" 39 40 static isc_result_t 41 make_name(const char *src, dns_name_t *name) { 42 isc_buffer_t b; 43 isc_buffer_constinit(&b, src, strlen(src)); 44 isc_buffer_add(&b, strlen(src)); 45 return dns_name_fromtext(name, &b, dns_rootname, 0, NULL); 46 } 47 48 /* create: make sure we can create a dbiterator */ 49 static void 50 test_create(const char *filename) { 51 isc_result_t result; 52 dns_db_t *db = NULL; 53 dns_dbiterator_t *iter = NULL; 54 55 result = dns_test_loaddb(&db, dns_dbtype_zone, TEST_ORIGIN, filename); 56 assert_int_equal(result, ISC_R_SUCCESS); 57 58 result = dns_db_createiterator(db, 0, &iter); 59 assert_int_equal(result, ISC_R_SUCCESS); 60 61 dns_dbiterator_destroy(&iter); 62 dns_db_detach(&db); 63 } 64 65 ISC_RUN_TEST_IMPL(create) { 66 UNUSED(state); 67 68 test_create(TESTS_DIR "/testdata/dbiterator/zone1.data"); 69 } 70 71 ISC_RUN_TEST_IMPL(create_nsec3) { 72 UNUSED(state); 73 74 test_create(TESTS_DIR "/testdata/dbiterator/zone2.data"); 75 } 76 77 /* walk: walk a database */ 78 static void 79 test_walk(const char *filename, int flags, int nodes) { 80 isc_result_t result; 81 dns_db_t *db = NULL; 82 dns_dbiterator_t *iter = NULL; 83 dns_dbnode_t *node = NULL; 84 dns_name_t *name; 85 dns_fixedname_t f; 86 int i = 0; 87 88 name = dns_fixedname_initname(&f); 89 90 result = dns_test_loaddb(&db, dns_dbtype_zone, TEST_ORIGIN, filename); 91 assert_int_equal(result, ISC_R_SUCCESS); 92 93 result = dns_db_createiterator(db, flags, &iter); 94 assert_int_equal(result, ISC_R_SUCCESS); 95 96 for (result = dns_dbiterator_first(iter); result == ISC_R_SUCCESS; 97 result = dns_dbiterator_next(iter)) 98 { 99 result = dns_dbiterator_current(iter, &node, name); 100 assert_int_equal(result, ISC_R_SUCCESS); 101 dns_db_detachnode(db, &node); 102 i++; 103 } 104 105 assert_int_equal(i, nodes); 106 107 dns_dbiterator_destroy(&iter); 108 dns_db_detach(&db); 109 } 110 111 ISC_RUN_TEST_IMPL(walk) { 112 UNUSED(state); 113 114 test_walk(TESTS_DIR "/testdata/dbiterator/zone1.data", 0, 12); 115 test_walk(TESTS_DIR "/testdata/dbiterator/zone1.data", DNS_DB_NONSEC3, 116 12); 117 test_walk(TESTS_DIR "/testdata/dbiterator/zone1.data", DNS_DB_NSEC3ONLY, 118 0); 119 } 120 121 ISC_RUN_TEST_IMPL(walk_nsec3) { 122 UNUSED(state); 123 124 test_walk(TESTS_DIR "/testdata/dbiterator/zone2.data", 0, 32); 125 test_walk(TESTS_DIR "/testdata/dbiterator/zone2.data", DNS_DB_NONSEC3, 126 12); 127 test_walk(TESTS_DIR "/testdata/dbiterator/zone2.data", DNS_DB_NSEC3ONLY, 128 20); 129 } 130 131 /* reverse: walk database backwards */ 132 static void 133 test_reverse(const char *filename, int flags, int nodes) { 134 isc_result_t result; 135 dns_db_t *db = NULL; 136 dns_dbiterator_t *iter = NULL; 137 dns_dbnode_t *node = NULL; 138 dns_name_t *name; 139 dns_fixedname_t f; 140 int i = 0; 141 142 name = dns_fixedname_initname(&f); 143 144 result = dns_test_loaddb(&db, dns_dbtype_zone, TEST_ORIGIN, filename); 145 assert_int_equal(result, ISC_R_SUCCESS); 146 147 result = dns_db_createiterator(db, flags, &iter); 148 assert_int_equal(result, ISC_R_SUCCESS); 149 150 for (result = dns_dbiterator_last(iter); result == ISC_R_SUCCESS; 151 result = dns_dbiterator_prev(iter)) 152 { 153 result = dns_dbiterator_current(iter, &node, name); 154 assert_int_equal(result, ISC_R_SUCCESS); 155 dns_db_detachnode(db, &node); 156 i++; 157 } 158 159 assert_int_equal(i, nodes); 160 161 dns_dbiterator_destroy(&iter); 162 dns_db_detach(&db); 163 } 164 165 ISC_RUN_TEST_IMPL(reverse) { 166 UNUSED(state); 167 168 test_reverse(TESTS_DIR "/testdata/dbiterator/zone1.data", 0, 12); 169 test_reverse(TESTS_DIR "/testdata/dbiterator/zone1.data", 170 DNS_DB_NONSEC3, 12); 171 test_reverse(TESTS_DIR "/testdata/dbiterator/zone1.data", 172 DNS_DB_NSEC3ONLY, 0); 173 } 174 175 ISC_RUN_TEST_IMPL(reverse_nsec3) { 176 UNUSED(state); 177 178 test_reverse(TESTS_DIR "/testdata/dbiterator/zone2.data", 0, 32); 179 test_reverse(TESTS_DIR "/testdata/dbiterator/zone2.data", 180 DNS_DB_NONSEC3, 12); 181 test_reverse(TESTS_DIR "/testdata/dbiterator/zone2.data", 182 DNS_DB_NSEC3ONLY, 20); 183 } 184 185 /* seek: walk database starting at a particular node */ 186 static void 187 test_seek_node(const char *filename, bool nsec3, int flags, int nodes) { 188 isc_result_t result, result3; 189 dns_db_t *db = NULL; 190 dns_dbiterator_t *iter = NULL, *iter3 = NULL; 191 dns_dbnode_t *node = NULL; 192 dns_name_t *name, *seekname; 193 dns_fixedname_t f1, f2; 194 int i = 0; 195 196 name = dns_fixedname_initname(&f1); 197 seekname = dns_fixedname_initname(&f2); 198 199 result = dns_test_loaddb(&db, dns_dbtype_zone, TEST_ORIGIN, filename); 200 assert_int_equal(result, ISC_R_SUCCESS); 201 202 result = dns_db_createiterator(db, flags, &iter); 203 assert_int_equal(result, ISC_R_SUCCESS); 204 205 result3 = dns_db_createiterator(db, flags, &iter3); 206 assert_int_equal(result3, ISC_R_SUCCESS); 207 208 result = make_name("c." TEST_ORIGIN, seekname); 209 assert_int_equal(result, ISC_R_SUCCESS); 210 211 result = dns_dbiterator_seek(iter, seekname); 212 if (flags == DNS_DB_NSEC3ONLY) { 213 /* "c" isn't in the NSEC3 tree but the origin node is */ 214 assert_int_equal(result, DNS_R_PARTIALMATCH); 215 216 /* NSEC3 iterator */ 217 result3 = dns_dbiterator_seek3(iter3, seekname); 218 if (nsec3) { 219 assert_int_equal(result3, ISC_R_SUCCESS); 220 } else { 221 assert_int_equal(result3, ISC_R_NOMORE); 222 } 223 } else { 224 assert_int_equal(result, ISC_R_SUCCESS); 225 } 226 227 while (result == ISC_R_SUCCESS) { 228 result = dns_dbiterator_current(iter, &node, name); 229 assert_int_equal(result, ISC_R_SUCCESS); 230 dns_db_detachnode(db, &node); 231 result = dns_dbiterator_next(iter); 232 i++; 233 } 234 235 assert_int_equal(i, nodes); 236 237 /* now reset the iterator and walk backwards */ 238 i = 0; 239 result = dns_dbiterator_seek(iter, seekname); 240 if (flags == DNS_DB_NSEC3ONLY) { 241 /* "c" isn't in the NSEC3 tree but the origin node is */ 242 assert_int_equal(result, DNS_R_PARTIALMATCH); 243 nodes = 0; 244 } else { 245 assert_int_equal(result, ISC_R_SUCCESS); 246 nodes = 4; 247 } 248 249 while (result == ISC_R_SUCCESS) { 250 result = dns_dbiterator_current(iter, &node, name); 251 assert_int_equal(result, ISC_R_SUCCESS); 252 dns_db_detachnode(db, &node); 253 result = dns_dbiterator_prev(iter); 254 i++; 255 } 256 257 assert_int_equal(i, nodes); 258 259 dns_dbiterator_destroy(&iter); 260 dns_dbiterator_destroy(&iter3); 261 dns_db_detach(&db); 262 } 263 264 ISC_RUN_TEST_IMPL(seek_node) { 265 UNUSED(state); 266 267 test_seek_node(TESTS_DIR "/testdata/dbiterator/zone1.data", false, 0, 268 9); 269 test_seek_node(TESTS_DIR "/testdata/dbiterator/zone1.data", false, 270 DNS_DB_NONSEC3, 9); 271 test_seek_node(TESTS_DIR "/testdata/dbiterator/zone1.data", false, 272 DNS_DB_NSEC3ONLY, 0); 273 } 274 275 ISC_RUN_TEST_IMPL(seek_node_nsec3) { 276 UNUSED(state); 277 278 test_seek_node(TESTS_DIR "/testdata/dbiterator/zone2.data", true, 0, 279 29); 280 test_seek_node(TESTS_DIR "/testdata/dbiterator/zone2.data", true, 281 DNS_DB_NONSEC3, 9); 282 test_seek_node(TESTS_DIR "/testdata/dbiterator/zone2.data", true, 283 DNS_DB_NSEC3ONLY, 0); 284 } 285 286 /* 287 * seek_emty: walk database starting at an empty nonterminal node 288 * (should fail) 289 */ 290 static void 291 test_seek_empty(const char *filename) { 292 isc_result_t result; 293 dns_db_t *db = NULL; 294 dns_dbiterator_t *iter = NULL; 295 dns_name_t *seekname; 296 dns_fixedname_t f1; 297 298 seekname = dns_fixedname_initname(&f1); 299 300 result = dns_test_loaddb(&db, dns_dbtype_zone, TEST_ORIGIN, filename); 301 assert_int_equal(result, ISC_R_SUCCESS); 302 303 result = dns_db_createiterator(db, 0, &iter); 304 assert_int_equal(result, ISC_R_SUCCESS); 305 306 result = make_name("d." TEST_ORIGIN, seekname); 307 assert_int_equal(result, ISC_R_SUCCESS); 308 309 result = dns_dbiterator_seek(iter, seekname); 310 assert_int_equal(result, DNS_R_PARTIALMATCH); 311 312 dns_dbiterator_destroy(&iter); 313 dns_db_detach(&db); 314 } 315 316 ISC_RUN_TEST_IMPL(seek_empty) { 317 UNUSED(state); 318 319 test_seek_empty(TESTS_DIR "/testdata/dbiterator/zone1.data"); 320 } 321 322 ISC_RUN_TEST_IMPL(seek_empty_nsec3) { 323 UNUSED(state); 324 325 test_seek_empty(TESTS_DIR "/testdata/dbiterator/zone2.data"); 326 } 327 328 /* 329 * seek_nx: walk database starting at a nonexistent node 330 */ 331 static void 332 test_seek_nx(const char *filename) { 333 isc_result_t result; 334 dns_db_t *db = NULL; 335 dns_dbiterator_t *iter = NULL; 336 dns_name_t *seekname; 337 dns_fixedname_t f1; 338 339 seekname = dns_fixedname_initname(&f1); 340 341 result = dns_test_loaddb(&db, dns_dbtype_zone, TEST_ORIGIN, filename); 342 assert_int_equal(result, ISC_R_SUCCESS); 343 344 result = dns_db_createiterator(db, 0, &iter); 345 assert_int_equal(result, ISC_R_SUCCESS); 346 347 result = make_name("nonexistent." TEST_ORIGIN, seekname); 348 assert_int_equal(result, ISC_R_SUCCESS); 349 350 result = dns_dbiterator_seek(iter, seekname); 351 assert_int_equal(result, DNS_R_PARTIALMATCH); 352 353 result = make_name("nonexistent.", seekname); 354 assert_int_equal(result, ISC_R_SUCCESS); 355 356 result = dns_dbiterator_seek(iter, seekname); 357 assert_int_equal(result, ISC_R_NOTFOUND); 358 359 dns_dbiterator_destroy(&iter); 360 dns_db_detach(&db); 361 } 362 363 ISC_RUN_TEST_IMPL(seek_nx) { 364 UNUSED(state); 365 366 test_seek_nx(TESTS_DIR "/testdata/dbiterator/zone1.data"); 367 } 368 369 ISC_RUN_TEST_IMPL(seek_nx_nsec3) { 370 UNUSED(state); 371 372 test_seek_nx(TESTS_DIR "/testdata/dbiterator/zone2.data"); 373 } 374 375 /* 376 * XXX: 377 * dns_dbiterator API calls that are not yet part of this unit test: 378 * 379 * dns_dbiterator_pause 380 * dns_dbiterator_origin 381 * dns_dbiterator_setcleanmode 382 */ 383 ISC_TEST_LIST_START 384 ISC_TEST_ENTRY(create) 385 ISC_TEST_ENTRY(create_nsec3) 386 ISC_TEST_ENTRY(walk) 387 ISC_TEST_ENTRY(walk_nsec3) 388 ISC_TEST_ENTRY(reverse) 389 ISC_TEST_ENTRY(reverse_nsec3) 390 ISC_TEST_ENTRY(seek_node) 391 ISC_TEST_ENTRY(seek_node_nsec3) 392 ISC_TEST_ENTRY(seek_empty) 393 ISC_TEST_ENTRY(seek_empty_nsec3) 394 ISC_TEST_ENTRY(seek_nx) 395 ISC_TEST_ENTRY(seek_nx_nsec3) 396 ISC_TEST_LIST_END 397 398 ISC_TEST_MAIN 399