1 /* $NetBSD: dbiterator_test.c,v 1.4 2025/01/26 16:25:47 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, int flags, int nodes) { 188 isc_result_t result; 189 dns_db_t *db = NULL; 190 dns_dbiterator_t *iter = 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 result = make_name("c." TEST_ORIGIN, seekname); 206 assert_int_equal(result, ISC_R_SUCCESS); 207 208 result = dns_dbiterator_seek(iter, seekname); 209 if (flags == DNS_DB_NSEC3ONLY) { 210 /* "c" isn't in the NSEC3 tree but the origin node is */ 211 assert_int_equal(result, DNS_R_PARTIALMATCH); 212 } else { 213 assert_int_equal(result, ISC_R_SUCCESS); 214 } 215 216 while (result == ISC_R_SUCCESS) { 217 result = dns_dbiterator_current(iter, &node, name); 218 assert_int_equal(result, ISC_R_SUCCESS); 219 dns_db_detachnode(db, &node); 220 result = dns_dbiterator_next(iter); 221 i++; 222 } 223 224 assert_int_equal(i, nodes); 225 226 /* now reset the iterator and walk backwards */ 227 i = 0; 228 result = dns_dbiterator_seek(iter, seekname); 229 if (flags == DNS_DB_NSEC3ONLY) { 230 /* "c" isn't in the NSEC3 tree but the origin node is */ 231 assert_int_equal(result, DNS_R_PARTIALMATCH); 232 nodes = 0; 233 } else { 234 assert_int_equal(result, ISC_R_SUCCESS); 235 nodes = 4; 236 } 237 238 while (result == ISC_R_SUCCESS) { 239 result = dns_dbiterator_current(iter, &node, name); 240 assert_int_equal(result, ISC_R_SUCCESS); 241 dns_db_detachnode(db, &node); 242 result = dns_dbiterator_prev(iter); 243 i++; 244 } 245 246 assert_int_equal(i, nodes); 247 248 dns_dbiterator_destroy(&iter); 249 dns_db_detach(&db); 250 } 251 252 ISC_RUN_TEST_IMPL(seek_node) { 253 UNUSED(state); 254 255 test_seek_node(TESTS_DIR "/testdata/dbiterator/zone1.data", 0, 9); 256 test_seek_node(TESTS_DIR "/testdata/dbiterator/zone1.data", 257 DNS_DB_NONSEC3, 9); 258 test_seek_node(TESTS_DIR "/testdata/dbiterator/zone1.data", 259 DNS_DB_NSEC3ONLY, 0); 260 } 261 262 ISC_RUN_TEST_IMPL(seek_node_nsec3) { 263 UNUSED(state); 264 265 test_seek_node(TESTS_DIR "/testdata/dbiterator/zone2.data", 0, 29); 266 test_seek_node(TESTS_DIR "/testdata/dbiterator/zone2.data", 267 DNS_DB_NONSEC3, 9); 268 test_seek_node(TESTS_DIR "/testdata/dbiterator/zone2.data", 269 DNS_DB_NSEC3ONLY, 0); 270 } 271 272 /* 273 * seek_emty: walk database starting at an empty nonterminal node 274 * (should fail) 275 */ 276 static void 277 test_seek_empty(const char *filename) { 278 isc_result_t result; 279 dns_db_t *db = NULL; 280 dns_dbiterator_t *iter = NULL; 281 dns_name_t *seekname; 282 dns_fixedname_t f1; 283 284 seekname = dns_fixedname_initname(&f1); 285 286 result = dns_test_loaddb(&db, dns_dbtype_zone, TEST_ORIGIN, filename); 287 assert_int_equal(result, ISC_R_SUCCESS); 288 289 result = dns_db_createiterator(db, 0, &iter); 290 assert_int_equal(result, ISC_R_SUCCESS); 291 292 result = make_name("d." TEST_ORIGIN, seekname); 293 assert_int_equal(result, ISC_R_SUCCESS); 294 295 result = dns_dbiterator_seek(iter, seekname); 296 assert_int_equal(result, DNS_R_PARTIALMATCH); 297 298 dns_dbiterator_destroy(&iter); 299 dns_db_detach(&db); 300 } 301 302 ISC_RUN_TEST_IMPL(seek_empty) { 303 UNUSED(state); 304 305 test_seek_empty(TESTS_DIR "/testdata/dbiterator/zone1.data"); 306 } 307 308 ISC_RUN_TEST_IMPL(seek_empty_nsec3) { 309 UNUSED(state); 310 311 test_seek_empty(TESTS_DIR "/testdata/dbiterator/zone2.data"); 312 } 313 314 /* 315 * seek_nx: walk database starting at a nonexistent node 316 */ 317 static void 318 test_seek_nx(const char *filename) { 319 isc_result_t result; 320 dns_db_t *db = NULL; 321 dns_dbiterator_t *iter = NULL; 322 dns_name_t *seekname; 323 dns_fixedname_t f1; 324 325 seekname = dns_fixedname_initname(&f1); 326 327 result = dns_test_loaddb(&db, dns_dbtype_zone, TEST_ORIGIN, filename); 328 assert_int_equal(result, ISC_R_SUCCESS); 329 330 result = dns_db_createiterator(db, 0, &iter); 331 assert_int_equal(result, ISC_R_SUCCESS); 332 333 result = make_name("nonexistent." TEST_ORIGIN, seekname); 334 assert_int_equal(result, ISC_R_SUCCESS); 335 336 result = dns_dbiterator_seek(iter, seekname); 337 assert_int_equal(result, DNS_R_PARTIALMATCH); 338 339 result = make_name("nonexistent.", seekname); 340 assert_int_equal(result, ISC_R_SUCCESS); 341 342 result = dns_dbiterator_seek(iter, seekname); 343 assert_int_equal(result, ISC_R_NOTFOUND); 344 345 dns_dbiterator_destroy(&iter); 346 dns_db_detach(&db); 347 } 348 349 ISC_RUN_TEST_IMPL(seek_nx) { 350 UNUSED(state); 351 352 test_seek_nx(TESTS_DIR "/testdata/dbiterator/zone1.data"); 353 } 354 355 ISC_RUN_TEST_IMPL(seek_nx_nsec3) { 356 UNUSED(state); 357 358 test_seek_nx(TESTS_DIR "/testdata/dbiterator/zone2.data"); 359 } 360 361 /* 362 * XXX: 363 * dns_dbiterator API calls that are not yet part of this unit test: 364 * 365 * dns_dbiterator_pause 366 * dns_dbiterator_origin 367 * dns_dbiterator_setcleanmode 368 */ 369 ISC_TEST_LIST_START 370 ISC_TEST_ENTRY(create) 371 ISC_TEST_ENTRY(create_nsec3) 372 ISC_TEST_ENTRY(walk) 373 ISC_TEST_ENTRY(walk_nsec3) 374 ISC_TEST_ENTRY(reverse) 375 ISC_TEST_ENTRY(reverse_nsec3) 376 ISC_TEST_ENTRY(seek_node) 377 ISC_TEST_ENTRY(seek_node_nsec3) 378 ISC_TEST_ENTRY(seek_empty) 379 ISC_TEST_ENTRY(seek_empty_nsec3) 380 ISC_TEST_ENTRY(seek_nx) 381 ISC_TEST_ENTRY(seek_nx_nsec3) 382 ISC_TEST_LIST_END 383 384 ISC_TEST_MAIN 385