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