Home | History | Annotate | Line # | Download | only in dns
      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