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