xref: /netbsd-src/external/mpl/bind/dist/tests/dns/zt_test.c (revision 32d1c65c71fbdb65a012e8392a62a757dd6853e9)
1 /*	$NetBSD: zt_test.c,v 1.2 2024/02/21 22:52:50 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 <stdbool.h>
21 #include <stddef.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 
26 #define UNIT_TESTING
27 #include <cmocka.h>
28 
29 #include <isc/app.h>
30 #include <isc/atomic.h>
31 #include <isc/buffer.h>
32 #include <isc/print.h>
33 #include <isc/task.h>
34 #include <isc/timer.h>
35 #include <isc/util.h>
36 
37 #include <dns/db.h>
38 #include <dns/name.h>
39 #include <dns/view.h>
40 #include <dns/zone.h>
41 #include <dns/zt.h>
42 
43 #include <tests/dns.h>
44 
45 static int
46 _setup(void **state) {
47 	isc_app_start();
48 	setup_managers(state);
49 
50 	return (0);
51 }
52 
53 static int
54 _teardown(void **state) {
55 	teardown_managers(state);
56 	isc_app_finish();
57 
58 	return (0);
59 }
60 
61 struct args {
62 	void *arg1;
63 	void *arg2;
64 	bool arg3;
65 };
66 
67 static isc_result_t
68 count_zone(dns_zone_t *zone, void *uap) {
69 	int *nzones = (int *)uap;
70 
71 	UNUSED(zone);
72 
73 	*nzones += 1;
74 	return (ISC_R_SUCCESS);
75 }
76 
77 static isc_result_t
78 load_done(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task) {
79 	/* We treat zt as a pointer to a boolean for testing purposes */
80 	atomic_bool *done = (atomic_bool *)zt;
81 
82 	UNUSED(zone);
83 	UNUSED(task);
84 
85 	atomic_store(done, true);
86 	isc_app_shutdown();
87 	return (ISC_R_SUCCESS);
88 }
89 
90 static isc_result_t
91 all_done(void *arg) {
92 	atomic_bool *done = (atomic_bool *)arg;
93 
94 	atomic_store(done, true);
95 	isc_app_shutdown();
96 	return (ISC_R_SUCCESS);
97 }
98 
99 static void
100 start_zt_asyncload(isc_task_t *task, isc_event_t *event) {
101 	struct args *args = (struct args *)(event->ev_arg);
102 
103 	UNUSED(task);
104 
105 	dns_zt_asyncload(args->arg1, false, all_done, args->arg2);
106 
107 	isc_event_free(&event);
108 }
109 
110 static void
111 start_zone_asyncload(isc_task_t *task, isc_event_t *event) {
112 	struct args *args = (struct args *)(event->ev_arg);
113 
114 	UNUSED(task);
115 
116 	dns_zone_asyncload(args->arg1, args->arg3, load_done, args->arg2);
117 	isc_event_free(&event);
118 }
119 
120 /* apply a function to a zone table */
121 ISC_RUN_TEST_IMPL(dns_zt_apply) {
122 	isc_result_t result;
123 	dns_zone_t *zone = NULL;
124 	dns_view_t *view = NULL;
125 	int nzones = 0;
126 
127 	UNUSED(state);
128 
129 	result = dns_test_makezone("foo", &zone, NULL, true);
130 	assert_int_equal(result, ISC_R_SUCCESS);
131 
132 	view = dns_zone_getview(zone);
133 	assert_non_null(view->zonetable);
134 
135 	assert_int_equal(nzones, 0);
136 	result = dns_zt_apply(view->zonetable, isc_rwlocktype_read, false, NULL,
137 			      count_zone, &nzones);
138 	assert_int_equal(result, ISC_R_SUCCESS);
139 	assert_int_equal(nzones, 1);
140 
141 	/* These steps are necessary so the zone can be detached properly */
142 	result = dns_test_setupzonemgr();
143 	assert_int_equal(result, ISC_R_SUCCESS);
144 	result = dns_test_managezone(zone);
145 	assert_int_equal(result, ISC_R_SUCCESS);
146 	dns_test_releasezone(zone);
147 	dns_test_closezonemgr();
148 
149 	/* The view was left attached in dns_test_makezone() */
150 	dns_view_detach(&view);
151 	dns_zone_detach(&zone);
152 }
153 
154 /* asynchronous zone load */
155 ISC_RUN_TEST_IMPL(dns_zt_asyncload_zone) {
156 	isc_result_t result;
157 	int n;
158 	dns_zone_t *zone = NULL;
159 	dns_view_t *view = NULL;
160 	dns_db_t *db = NULL;
161 	FILE *zonefile, *origfile;
162 	char buf[4096];
163 	atomic_bool done;
164 	int i = 0;
165 	struct args args;
166 
167 	UNUSED(state);
168 
169 	atomic_init(&done, false);
170 
171 	result = dns_test_makezone("foo", &zone, NULL, true);
172 	assert_int_equal(result, ISC_R_SUCCESS);
173 
174 	result = dns_test_setupzonemgr();
175 	assert_int_equal(result, ISC_R_SUCCESS);
176 	result = dns_test_managezone(zone);
177 	assert_int_equal(result, ISC_R_SUCCESS);
178 
179 	view = dns_zone_getview(zone);
180 	assert_non_null(view->zonetable);
181 
182 	assert_false(dns__zone_loadpending(zone));
183 	assert_false(atomic_load(&done));
184 	zonefile = fopen("./zone.data", "wb");
185 	assert_non_null(zonefile);
186 	origfile = fopen(TESTS_DIR "/testdata/zt/zone1.db", "r+b");
187 	assert_non_null(origfile);
188 	n = fread(buf, 1, 4096, origfile);
189 	fclose(origfile);
190 	fwrite(buf, 1, n, zonefile);
191 	fflush(zonefile);
192 
193 	dns_zone_setfile(zone, "./zone.data", dns_masterformat_text,
194 			 &dns_master_style_default);
195 
196 	args.arg1 = zone;
197 	args.arg2 = &done;
198 	args.arg3 = false;
199 	isc_app_onrun(mctx, maintask, start_zone_asyncload, &args);
200 
201 	isc_app_run();
202 	while (dns__zone_loadpending(zone) && i++ < 5000) {
203 		dns_test_nap(1000);
204 	}
205 	assert_true(atomic_load(&done));
206 	/* The zone should now be loaded; test it */
207 	result = dns_zone_getdb(zone, &db);
208 	assert_int_equal(result, ISC_R_SUCCESS);
209 	dns_db_detach(&db);
210 	/*
211 	 * Add something to zone file, reload zone with newonly - it should
212 	 * not be reloaded.
213 	 */
214 	fprintf(zonefile, "\nb in b 1.2.3.4\n");
215 	fflush(zonefile);
216 	fclose(zonefile);
217 
218 	args.arg1 = zone;
219 	args.arg2 = &done;
220 	args.arg3 = true;
221 	isc_app_onrun(mctx, maintask, start_zone_asyncload, &args);
222 
223 	isc_app_run();
224 
225 	while (dns__zone_loadpending(zone) && i++ < 5000) {
226 		dns_test_nap(1000);
227 	}
228 	assert_true(atomic_load(&done));
229 	/* The zone should now be loaded; test it */
230 	result = dns_zone_getdb(zone, &db);
231 	assert_int_equal(result, ISC_R_SUCCESS);
232 	dns_db_detach(&db);
233 
234 	/* Now reload it without newonly - it should be reloaded */
235 	args.arg1 = zone;
236 	args.arg2 = &done;
237 	args.arg3 = false;
238 	isc_app_onrun(mctx, maintask, start_zone_asyncload, &args);
239 
240 	isc_app_run();
241 
242 	while (dns__zone_loadpending(zone) && i++ < 5000) {
243 		dns_test_nap(1000);
244 	}
245 	assert_true(atomic_load(&done));
246 	/* The zone should now be loaded; test it */
247 	result = dns_zone_getdb(zone, &db);
248 	assert_int_equal(result, ISC_R_SUCCESS);
249 
250 	assert_non_null(db);
251 	if (db != NULL) {
252 		dns_db_detach(&db);
253 	}
254 
255 	dns_test_releasezone(zone);
256 	dns_test_closezonemgr();
257 
258 	dns_zone_detach(&zone);
259 	dns_view_detach(&view);
260 }
261 
262 /* asynchronous zone table load */
263 ISC_RUN_TEST_IMPL(dns_zt_asyncload_zt) {
264 	isc_result_t result;
265 	dns_zone_t *zone1 = NULL, *zone2 = NULL, *zone3 = NULL;
266 	dns_view_t *view;
267 	dns_zt_t *zt = NULL;
268 	dns_db_t *db = NULL;
269 	atomic_bool done;
270 	int i = 0;
271 	struct args args;
272 
273 	UNUSED(state);
274 
275 	atomic_init(&done, false);
276 
277 	result = dns_test_makezone("foo", &zone1, NULL, true);
278 	assert_int_equal(result, ISC_R_SUCCESS);
279 	dns_zone_setfile(zone1, TESTS_DIR "/testdata/zt/zone1.db",
280 			 dns_masterformat_text, &dns_master_style_default);
281 	view = dns_zone_getview(zone1);
282 
283 	result = dns_test_makezone("bar", &zone2, view, false);
284 	assert_int_equal(result, ISC_R_SUCCESS);
285 	dns_zone_setfile(zone2, TESTS_DIR "/testdata/zt/zone1.db",
286 			 dns_masterformat_text, &dns_master_style_default);
287 
288 	/* This one will fail to load */
289 	result = dns_test_makezone("fake", &zone3, view, false);
290 	assert_int_equal(result, ISC_R_SUCCESS);
291 	dns_zone_setfile(zone3, TESTS_DIR "/testdata/zt/nonexistent.db",
292 			 dns_masterformat_text, &dns_master_style_default);
293 
294 	zt = view->zonetable;
295 	assert_non_null(zt);
296 
297 	result = dns_test_setupzonemgr();
298 	assert_int_equal(result, ISC_R_SUCCESS);
299 	result = dns_test_managezone(zone1);
300 	assert_int_equal(result, ISC_R_SUCCESS);
301 	result = dns_test_managezone(zone2);
302 	assert_int_equal(result, ISC_R_SUCCESS);
303 	result = dns_test_managezone(zone3);
304 	assert_int_equal(result, ISC_R_SUCCESS);
305 
306 	assert_false(dns__zone_loadpending(zone1));
307 	assert_false(dns__zone_loadpending(zone2));
308 	assert_false(atomic_load(&done));
309 
310 	args.arg1 = zt;
311 	args.arg2 = &done;
312 	isc_app_onrun(mctx, maintask, start_zt_asyncload, &args);
313 
314 	isc_app_run();
315 	while (!atomic_load(&done) && i++ < 5000) {
316 		dns_test_nap(1000);
317 	}
318 	assert_true(atomic_load(&done));
319 
320 	/* Both zones should now be loaded; test them */
321 	result = dns_zone_getdb(zone1, &db);
322 	assert_int_equal(result, ISC_R_SUCCESS);
323 	assert_non_null(db);
324 	if (db != NULL) {
325 		dns_db_detach(&db);
326 	}
327 
328 	result = dns_zone_getdb(zone2, &db);
329 	assert_int_equal(result, ISC_R_SUCCESS);
330 	assert_non_null(db);
331 	if (db != NULL) {
332 		dns_db_detach(&db);
333 	}
334 
335 	dns_test_releasezone(zone3);
336 	dns_test_releasezone(zone2);
337 	dns_test_releasezone(zone1);
338 	dns_test_closezonemgr();
339 
340 	dns_zone_detach(&zone1);
341 	dns_zone_detach(&zone2);
342 	dns_zone_detach(&zone3);
343 	dns_view_detach(&view);
344 }
345 
346 ISC_TEST_LIST_START
347 
348 ISC_TEST_ENTRY_CUSTOM(dns_zt_apply, _setup, _teardown)
349 ISC_TEST_ENTRY_CUSTOM(dns_zt_asyncload_zone, _setup, _teardown)
350 ISC_TEST_ENTRY_CUSTOM(dns_zt_asyncload_zt, _setup, _teardown)
351 
352 ISC_TEST_LIST_END
353 
354 ISC_TEST_MAIN
355