1 /* $NetBSD: zt_test.c,v 1.3 2025/01/26 16:25:48 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/atomic.h> 30 #include <isc/buffer.h> 31 #include <isc/loop.h> 32 #include <isc/timer.h> 33 #include <isc/urcu.h> 34 #include <isc/util.h> 35 36 #include <dns/db.h> 37 #include <dns/name.h> 38 #include <dns/view.h> 39 #include <dns/zone.h> 40 #include <dns/zt.h> 41 42 #include <tests/dns.h> 43 44 static dns_db_t *db = NULL; 45 static FILE *zonefile, *origfile; 46 static dns_view_t *view = NULL; 47 48 static isc_result_t 49 count_zone(dns_zone_t *zone, void *uap) { 50 int *nzones = (int *)uap; 51 52 UNUSED(zone); 53 54 *nzones += 1; 55 return ISC_R_SUCCESS; 56 } 57 58 /* apply a function to a zone table */ 59 ISC_LOOP_TEST_IMPL(apply) { 60 isc_result_t result; 61 dns_zone_t *zone = NULL; 62 dns_zt_t *zt = NULL; 63 int nzones = 0; 64 65 result = dns_test_makezone("foo", &zone, NULL, true); 66 assert_int_equal(result, ISC_R_SUCCESS); 67 68 view = dns_zone_getview(zone); 69 rcu_read_lock(); 70 zt = rcu_dereference(view->zonetable); 71 rcu_read_unlock(); 72 73 assert_non_null(zt); 74 75 assert_int_equal(nzones, 0); 76 result = dns_view_apply(view, false, NULL, count_zone, &nzones); 77 assert_int_equal(result, ISC_R_SUCCESS); 78 assert_int_equal(nzones, 1); 79 80 /* These steps are necessary so the zone can be detached properly */ 81 dns_test_setupzonemgr(); 82 result = dns_test_managezone(zone); 83 assert_int_equal(result, ISC_R_SUCCESS); 84 dns_test_releasezone(zone); 85 dns_test_closezonemgr(); 86 87 /* The view was left attached in dns_test_makezone() */ 88 dns_view_detach(&view); 89 dns_zone_detach(&zone); 90 isc_loopmgr_shutdown(loopmgr); 91 } 92 93 static isc_result_t 94 load_done_last(void *uap) { 95 dns_zone_t *zone = uap; 96 isc_result_t result; 97 98 /* The zone should now be loaded; test it */ 99 result = dns_zone_getdb(zone, &db); 100 assert_int_equal(result, ISC_R_SUCCESS); 101 102 assert_non_null(db); 103 if (db != NULL) { 104 dns_db_detach(&db); 105 } 106 107 dns_test_releasezone(zone); 108 dns_test_closezonemgr(); 109 110 dns_zone_detach(&zone); 111 dns_view_detach(&view); 112 113 isc_loopmgr_shutdown(loopmgr); 114 115 return ISC_R_SUCCESS; 116 } 117 118 static isc_result_t 119 load_done_new_only(void *uap) { 120 dns_zone_t *zone = uap; 121 isc_result_t result; 122 123 /* The zone should now be loaded; test it */ 124 result = dns_zone_getdb(zone, &db); 125 assert_int_equal(result, ISC_R_SUCCESS); 126 dns_db_detach(&db); 127 128 dns_zone_asyncload(zone, true, load_done_last, zone); 129 130 return ISC_R_SUCCESS; 131 } 132 133 static isc_result_t 134 load_done_first(void *uap) { 135 dns_zone_t *zone = uap; 136 isc_result_t result; 137 138 /* The zone should now be loaded; test it */ 139 result = dns_zone_getdb(zone, &db); 140 assert_int_equal(result, ISC_R_SUCCESS); 141 dns_db_detach(&db); 142 143 /* 144 * Add something to zone file, reload zone with newonly - it should 145 * not be reloaded. 146 */ 147 fprintf(zonefile, "\nb in b 1.2.3.4\n"); 148 fflush(zonefile); 149 fclose(zonefile); 150 151 dns_zone_asyncload(zone, true, load_done_new_only, zone); 152 153 return ISC_R_SUCCESS; 154 } 155 156 /* asynchronous zone load */ 157 ISC_LOOP_TEST_IMPL(asyncload_zone) { 158 isc_result_t result; 159 int n; 160 dns_zone_t *zone = NULL; 161 dns_zt_t *zt = NULL; 162 char buf[4096]; 163 164 result = dns_test_makezone("foo", &zone, NULL, true); 165 assert_int_equal(result, ISC_R_SUCCESS); 166 167 dns_test_setupzonemgr(); 168 result = dns_test_managezone(zone); 169 assert_int_equal(result, ISC_R_SUCCESS); 170 171 view = dns_zone_getview(zone); 172 rcu_read_lock(); 173 zt = rcu_dereference(view->zonetable); 174 rcu_read_unlock(); 175 assert_non_null(zt); 176 177 assert_false(dns__zone_loadpending(zone)); 178 zonefile = fopen("./zone.data", "wb"); 179 assert_non_null(zonefile); 180 origfile = fopen(TESTS_DIR "/testdata/zt/zone1.db", "r+b"); 181 assert_non_null(origfile); 182 n = fread(buf, 1, 4096, origfile); 183 fclose(origfile); 184 fwrite(buf, 1, n, zonefile); 185 fflush(zonefile); 186 187 dns_zone_setfile(zone, "./zone.data", dns_masterformat_text, 188 &dns_master_style_default); 189 190 dns_zone_asyncload(zone, false, load_done_first, zone); 191 } 192 193 dns_zone_t *zone1 = NULL, *zone2 = NULL, *zone3 = NULL; 194 195 static isc_result_t 196 all_done(void *arg ISC_ATTR_UNUSED) { 197 isc_result_t result; 198 199 /* Both zones should now be loaded; test them */ 200 result = dns_zone_getdb(zone1, &db); 201 assert_int_equal(result, ISC_R_SUCCESS); 202 assert_non_null(db); 203 if (db != NULL) { 204 dns_db_detach(&db); 205 } 206 207 result = dns_zone_getdb(zone2, &db); 208 assert_int_equal(result, ISC_R_SUCCESS); 209 assert_non_null(db); 210 if (db != NULL) { 211 dns_db_detach(&db); 212 } 213 214 dns_test_releasezone(zone3); 215 dns_test_releasezone(zone2); 216 dns_test_releasezone(zone1); 217 dns_test_closezonemgr(); 218 219 dns_zone_detach(&zone1); 220 dns_zone_detach(&zone2); 221 dns_zone_detach(&zone3); 222 dns_view_detach(&view); 223 224 isc_loopmgr_shutdown(loopmgr); 225 return ISC_R_SUCCESS; 226 } 227 228 /* asynchronous zone table load */ 229 ISC_LOOP_TEST_IMPL(asyncload_zt) { 230 isc_result_t result; 231 dns_zt_t *zt = NULL; 232 atomic_bool done; 233 234 atomic_init(&done, false); 235 236 result = dns_test_makezone("foo", &zone1, NULL, true); 237 assert_int_equal(result, ISC_R_SUCCESS); 238 dns_zone_setfile(zone1, TESTS_DIR "/testdata/zt/zone1.db", 239 dns_masterformat_text, &dns_master_style_default); 240 view = dns_zone_getview(zone1); 241 242 result = dns_test_makezone("bar", &zone2, view, false); 243 assert_int_equal(result, ISC_R_SUCCESS); 244 dns_zone_setfile(zone2, TESTS_DIR "/testdata/zt/zone1.db", 245 dns_masterformat_text, &dns_master_style_default); 246 247 /* This one will fail to load */ 248 result = dns_test_makezone("fake", &zone3, view, false); 249 assert_int_equal(result, ISC_R_SUCCESS); 250 dns_zone_setfile(zone3, TESTS_DIR "/testdata/zt/nonexistent.db", 251 dns_masterformat_text, &dns_master_style_default); 252 253 rcu_read_lock(); 254 zt = rcu_dereference(view->zonetable); 255 rcu_read_unlock(); 256 assert_non_null(zt); 257 258 dns_test_setupzonemgr(); 259 result = dns_test_managezone(zone1); 260 assert_int_equal(result, ISC_R_SUCCESS); 261 result = dns_test_managezone(zone2); 262 assert_int_equal(result, ISC_R_SUCCESS); 263 result = dns_test_managezone(zone3); 264 assert_int_equal(result, ISC_R_SUCCESS); 265 266 assert_false(dns__zone_loadpending(zone1)); 267 assert_false(dns__zone_loadpending(zone2)); 268 assert_false(atomic_load(&done)); 269 270 rcu_read_lock(); 271 zt = rcu_dereference(view->zonetable); 272 dns_zt_asyncload(zt, false, all_done, NULL); 273 rcu_read_unlock(); 274 } 275 276 ISC_TEST_LIST_START 277 ISC_TEST_ENTRY_CUSTOM(apply, setup_managers, teardown_managers) 278 ISC_TEST_ENTRY_CUSTOM(asyncload_zone, setup_managers, teardown_managers) 279 ISC_TEST_ENTRY_CUSTOM(asyncload_zt, setup_managers, teardown_managers) 280 ISC_TEST_LIST_END 281 282 ISC_TEST_MAIN 283