xref: /netbsd-src/external/mpl/bind/dist/tests/dns/dbiterator_test.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
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