1/* $NetBSD: zt.c,v 1.10 2024/02/21 22:52:09 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/*! \file */ 17 18#include <inttypes.h> 19#include <stdbool.h> 20 21#include <isc/atomic.h> 22#include <isc/file.h> 23#include <isc/magic.h> 24#include <isc/mem.h> 25#include <isc/result.h> 26#include <isc/string.h> 27#include <isc/task.h> 28#include <isc/util.h> 29 30#include <dns/log.h> 31#include <dns/name.h> 32#include <dns/rbt.h> 33#include <dns/rdataclass.h> 34#include <dns/view.h> 35#include <dns/zone.h> 36#include <dns/zt.h> 37 38struct zt_load_params { 39 dns_zt_zoneloaded_t dl; 40 bool newonly; 41}; 42 43struct dns_zt { 44 /* Unlocked. */ 45 unsigned int magic; 46 isc_mem_t *mctx; 47 dns_rdataclass_t rdclass; 48 isc_rwlock_t rwlock; 49 dns_zt_allloaded_t loaddone; 50 void *loaddone_arg; 51 struct zt_load_params *loadparams; 52 53 /* Atomic */ 54 atomic_bool flush; 55 isc_refcount_t references; 56 isc_refcount_t loads_pending; 57 58 /* Locked by lock. */ 59 dns_rbt_t *table; 60}; 61 62struct zt_freeze_params { 63 dns_view_t *view; 64 bool freeze; 65}; 66 67#define ZTMAGIC ISC_MAGIC('Z', 'T', 'b', 'l') 68#define VALID_ZT(zt) ISC_MAGIC_VALID(zt, ZTMAGIC) 69 70static void 71auto_detach(void *, void *); 72 73static isc_result_t 74load(dns_zone_t *zone, void *uap); 75 76static isc_result_t 77asyncload(dns_zone_t *zone, void *callback); 78 79static isc_result_t 80freezezones(dns_zone_t *zone, void *uap); 81 82static isc_result_t 83doneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task); 84 85isc_result_t 86dns_zt_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, dns_zt_t **ztp) { 87 dns_zt_t *zt; 88 isc_result_t result; 89 90 REQUIRE(ztp != NULL && *ztp == NULL); 91 92 zt = isc_mem_get(mctx, sizeof(*zt)); 93 94 zt->table = NULL; 95 result = dns_rbt_create(mctx, auto_detach, zt, &zt->table); 96 if (result != ISC_R_SUCCESS) { 97 goto cleanup_zt; 98 } 99 100 isc_rwlock_init(&zt->rwlock, 0, 0); 101 zt->mctx = NULL; 102 isc_mem_attach(mctx, &zt->mctx); 103 isc_refcount_init(&zt->references, 1); 104 atomic_init(&zt->flush, false); 105 zt->rdclass = rdclass; 106 zt->magic = ZTMAGIC; 107 zt->loaddone = NULL; 108 zt->loaddone_arg = NULL; 109 zt->loadparams = NULL; 110 isc_refcount_init(&zt->loads_pending, 0); 111 *ztp = zt; 112 113 return (ISC_R_SUCCESS); 114 115cleanup_zt: 116 isc_mem_put(mctx, zt, sizeof(*zt)); 117 118 return (result); 119} 120 121isc_result_t 122dns_zt_mount(dns_zt_t *zt, dns_zone_t *zone) { 123 isc_result_t result; 124 dns_zone_t *dummy = NULL; 125 dns_name_t *name; 126 127 REQUIRE(VALID_ZT(zt)); 128 129 name = dns_zone_getorigin(zone); 130 131 RWLOCK(&zt->rwlock, isc_rwlocktype_write); 132 133 result = dns_rbt_addname(zt->table, name, zone); 134 if (result == ISC_R_SUCCESS) { 135 dns_zone_attach(zone, &dummy); 136 } 137 138 RWUNLOCK(&zt->rwlock, isc_rwlocktype_write); 139 140 return (result); 141} 142 143isc_result_t 144dns_zt_unmount(dns_zt_t *zt, dns_zone_t *zone) { 145 isc_result_t result; 146 dns_name_t *name; 147 148 REQUIRE(VALID_ZT(zt)); 149 150 name = dns_zone_getorigin(zone); 151 152 RWLOCK(&zt->rwlock, isc_rwlocktype_write); 153 154 result = dns_rbt_deletename(zt->table, name, false); 155 156 RWUNLOCK(&zt->rwlock, isc_rwlocktype_write); 157 158 return (result); 159} 160 161isc_result_t 162dns_zt_find(dns_zt_t *zt, const dns_name_t *name, unsigned int options, 163 dns_name_t *foundname, dns_zone_t **zonep) { 164 isc_result_t result; 165 dns_zone_t *dummy = NULL; 166 unsigned int rbtoptions = 0; 167 168 REQUIRE(VALID_ZT(zt)); 169 170 if ((options & DNS_ZTFIND_NOEXACT) != 0) { 171 rbtoptions |= DNS_RBTFIND_NOEXACT; 172 } 173 174 RWLOCK(&zt->rwlock, isc_rwlocktype_read); 175 176 result = dns_rbt_findname(zt->table, name, rbtoptions, foundname, 177 (void **)(void *)&dummy); 178 if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) { 179 /* 180 * If DNS_ZTFIND_MIRROR is set and the zone which was 181 * determined to be the deepest match for the supplied name is 182 * a mirror zone which is expired or not yet loaded, treat it 183 * as non-existent. This will trigger a fallback to recursion 184 * instead of returning a SERVFAIL. 185 * 186 * Note that currently only the deepest match in the zone table 187 * is checked. Consider a server configured with two mirror 188 * zones: "bar" and its child, "foo.bar". If zone data is 189 * available for "bar" but not for "foo.bar", a query with 190 * QNAME equal to or below "foo.bar" will cause ISC_R_NOTFOUND 191 * to be returned, not DNS_R_PARTIALMATCH, despite zone data 192 * being available for "bar". This is considered to be an edge 193 * case, handling which more appropriately is possible, but 194 * arguably not worth the added complexity. 195 */ 196 if ((options & DNS_ZTFIND_MIRROR) != 0 && 197 dns_zone_gettype(dummy) == dns_zone_mirror && 198 !dns_zone_isloaded(dummy)) 199 { 200 result = ISC_R_NOTFOUND; 201 } else { 202 dns_zone_attach(dummy, zonep); 203 } 204 } 205 206 RWUNLOCK(&zt->rwlock, isc_rwlocktype_read); 207 208 return (result); 209} 210 211void 212dns_zt_attach(dns_zt_t *zt, dns_zt_t **ztp) { 213 REQUIRE(VALID_ZT(zt)); 214 REQUIRE(ztp != NULL && *ztp == NULL); 215 216 isc_refcount_increment(&zt->references); 217 218 *ztp = zt; 219} 220 221static isc_result_t 222flush(dns_zone_t *zone, void *uap) { 223 UNUSED(uap); 224 return (dns_zone_flush(zone)); 225} 226 227static void 228zt_destroy(dns_zt_t *zt) { 229 isc_refcount_destroy(&zt->references); 230 isc_refcount_destroy(&zt->loads_pending); 231 232 if (atomic_load_acquire(&zt->flush)) { 233 (void)dns_zt_apply(zt, isc_rwlocktype_none, false, NULL, flush, 234 NULL); 235 } 236 237 dns_rbt_destroy(&zt->table); 238 isc_rwlock_destroy(&zt->rwlock); 239 zt->magic = 0; 240 isc_mem_putanddetach(&zt->mctx, zt, sizeof(*zt)); 241} 242 243void 244dns_zt_detach(dns_zt_t **ztp) { 245 dns_zt_t *zt; 246 247 REQUIRE(ztp != NULL && VALID_ZT(*ztp)); 248 249 zt = *ztp; 250 *ztp = NULL; 251 252 if (isc_refcount_decrement(&zt->references) == 1) { 253 zt_destroy(zt); 254 } 255} 256 257void 258dns_zt_flush(dns_zt_t *zt) { 259 REQUIRE(VALID_ZT(zt)); 260 atomic_store_release(&zt->flush, true); 261} 262 263isc_result_t 264dns_zt_load(dns_zt_t *zt, bool stop, bool newonly) { 265 isc_result_t result; 266 struct zt_load_params params; 267 REQUIRE(VALID_ZT(zt)); 268 params.newonly = newonly; 269 result = dns_zt_apply(zt, isc_rwlocktype_read, stop, NULL, load, 270 ¶ms); 271 return (result); 272} 273 274static isc_result_t 275load(dns_zone_t *zone, void *paramsv) { 276 isc_result_t result; 277 struct zt_load_params *params = (struct zt_load_params *)paramsv; 278 result = dns_zone_load(zone, params->newonly); 279 if (result == DNS_R_CONTINUE || result == DNS_R_UPTODATE || 280 result == DNS_R_DYNAMIC) 281 { 282 result = ISC_R_SUCCESS; 283 } 284 return (result); 285} 286 287static void 288call_loaddone(dns_zt_t *zt) { 289 dns_zt_allloaded_t loaddone = zt->loaddone; 290 void *loaddone_arg = zt->loaddone_arg; 291 292 /* 293 * Set zt->loaddone, zt->loaddone_arg and zt->loadparams to NULL 294 * before calling loaddone. 295 */ 296 zt->loaddone = NULL; 297 zt->loaddone_arg = NULL; 298 299 isc_mem_put(zt->mctx, zt->loadparams, sizeof(struct zt_load_params)); 300 zt->loadparams = NULL; 301 302 /* 303 * Call the callback last. 304 */ 305 if (loaddone != NULL) { 306 loaddone(loaddone_arg); 307 } 308} 309 310isc_result_t 311dns_zt_asyncload(dns_zt_t *zt, bool newonly, dns_zt_allloaded_t alldone, 312 void *arg) { 313 isc_result_t result; 314 uint_fast32_t loads_pending; 315 316 REQUIRE(VALID_ZT(zt)); 317 318 /* 319 * Obtain a reference to zt->loads_pending so that asyncload can 320 * safely decrement both zt->references and zt->loads_pending 321 * without going to zero. 322 */ 323 loads_pending = isc_refcount_increment0(&zt->loads_pending); 324 INSIST(loads_pending == 0); 325 326 /* 327 * Only one dns_zt_asyncload call at a time should be active so 328 * these pointers should be NULL. They are set back to NULL 329 * before the zt->loaddone (alldone) is called in call_loaddone. 330 */ 331 INSIST(zt->loadparams == NULL); 332 INSIST(zt->loaddone == NULL); 333 INSIST(zt->loaddone_arg == NULL); 334 335 zt->loadparams = isc_mem_get(zt->mctx, sizeof(struct zt_load_params)); 336 zt->loadparams->dl = doneloading; 337 zt->loadparams->newonly = newonly; 338 zt->loaddone = alldone; 339 zt->loaddone_arg = arg; 340 341 result = dns_zt_apply(zt, isc_rwlocktype_read, false, NULL, asyncload, 342 zt); 343 344 /* 345 * Have all the loads completed? 346 */ 347 if (isc_refcount_decrement(&zt->loads_pending) == 1) { 348 call_loaddone(zt); 349 } 350 351 return (result); 352} 353 354/* 355 * Initiates asynchronous loading of zone 'zone'. 'callback' is a 356 * pointer to a function which will be used to inform the caller when 357 * the zone loading is complete. 358 */ 359static isc_result_t 360asyncload(dns_zone_t *zone, void *zt_) { 361 isc_result_t result; 362 struct dns_zt *zt = (dns_zt_t *)zt_; 363 REQUIRE(zone != NULL); 364 365 isc_refcount_increment(&zt->references); 366 isc_refcount_increment(&zt->loads_pending); 367 368 result = dns_zone_asyncload(zone, zt->loadparams->newonly, 369 *zt->loadparams->dl, zt); 370 if (result != ISC_R_SUCCESS) { 371 /* 372 * Caller is holding a reference to zt->loads_pending 373 * and zt->references so these can't decrement to zero. 374 */ 375 isc_refcount_decrement1(&zt->references); 376 isc_refcount_decrement1(&zt->loads_pending); 377 } 378 return (ISC_R_SUCCESS); 379} 380 381isc_result_t 382dns_zt_freezezones(dns_zt_t *zt, dns_view_t *view, bool freeze) { 383 isc_result_t result, tresult; 384 struct zt_freeze_params params = { view, freeze }; 385 386 REQUIRE(VALID_ZT(zt)); 387 388 result = dns_zt_apply(zt, isc_rwlocktype_read, false, &tresult, 389 freezezones, ¶ms); 390 if (tresult == ISC_R_NOTFOUND) { 391 tresult = ISC_R_SUCCESS; 392 } 393 return ((result == ISC_R_SUCCESS) ? tresult : result); 394} 395 396static isc_result_t 397freezezones(dns_zone_t *zone, void *uap) { 398 struct zt_freeze_params *params = uap; 399 bool frozen; 400 isc_result_t result = ISC_R_SUCCESS; 401 char classstr[DNS_RDATACLASS_FORMATSIZE]; 402 char zonename[DNS_NAME_FORMATSIZE]; 403 dns_zone_t *raw = NULL; 404 dns_view_t *view; 405 const char *vname; 406 const char *sep; 407 int level; 408 409 dns_zone_getraw(zone, &raw); 410 if (raw != NULL) { 411 zone = raw; 412 } 413 if (params->view != dns_zone_getview(zone)) { 414 if (raw != NULL) { 415 dns_zone_detach(&raw); 416 } 417 return (ISC_R_SUCCESS); 418 } 419 if (dns_zone_gettype(zone) != dns_zone_primary) { 420 if (raw != NULL) { 421 dns_zone_detach(&raw); 422 } 423 return (ISC_R_SUCCESS); 424 } 425 if (!dns_zone_isdynamic(zone, true)) { 426 if (raw != NULL) { 427 dns_zone_detach(&raw); 428 } 429 return (ISC_R_SUCCESS); 430 } 431 432 frozen = dns_zone_getupdatedisabled(zone); 433 if (params->freeze) { 434 if (frozen) { 435 result = DNS_R_FROZEN; 436 } 437 if (result == ISC_R_SUCCESS) { 438 result = dns_zone_flush(zone); 439 } 440 if (result == ISC_R_SUCCESS) { 441 dns_zone_setupdatedisabled(zone, params->freeze); 442 } 443 } else { 444 if (frozen) { 445 result = dns_zone_loadandthaw(zone); 446 if (result == DNS_R_CONTINUE || 447 result == DNS_R_UPTODATE) 448 { 449 result = ISC_R_SUCCESS; 450 } 451 } 452 } 453 view = dns_zone_getview(zone); 454 if (strcmp(view->name, "_bind") == 0 || strcmp(view->name, "_defaul" 455 "t") == 0) 456 { 457 vname = ""; 458 sep = ""; 459 } else { 460 vname = view->name; 461 sep = " "; 462 } 463 dns_rdataclass_format(dns_zone_getclass(zone), classstr, 464 sizeof(classstr)); 465 dns_name_format(dns_zone_getorigin(zone), zonename, sizeof(zonename)); 466 level = (result != ISC_R_SUCCESS) ? ISC_LOG_ERROR : ISC_LOG_DEBUG(1); 467 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE, 468 level, "%s zone '%s/%s'%s%s: %s", 469 params->freeze ? "freezing" : "thawing", zonename, 470 classstr, sep, vname, isc_result_totext(result)); 471 if (raw != NULL) { 472 dns_zone_detach(&raw); 473 } 474 return (result); 475} 476 477void 478dns_zt_setviewcommit(dns_zt_t *zt) { 479 dns_rbtnode_t *node; 480 dns_rbtnodechain_t chain; 481 isc_result_t result; 482 483 REQUIRE(VALID_ZT(zt)); 484 485 RWLOCK(&zt->rwlock, isc_rwlocktype_read); 486 dns_rbtnodechain_init(&chain); 487 488 result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL); 489 while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) { 490 result = dns_rbtnodechain_current(&chain, NULL, NULL, &node); 491 if (result == ISC_R_SUCCESS && node->data != NULL) { 492 dns_zone_setviewcommit(node->data); 493 } 494 495 result = dns_rbtnodechain_next(&chain, NULL, NULL); 496 } 497 498 dns_rbtnodechain_invalidate(&chain); 499 RWUNLOCK(&zt->rwlock, isc_rwlocktype_read); 500} 501 502void 503dns_zt_setviewrevert(dns_zt_t *zt) { 504 dns_rbtnode_t *node; 505 dns_rbtnodechain_t chain; 506 isc_result_t result; 507 508 REQUIRE(VALID_ZT(zt)); 509 510 dns_rbtnodechain_init(&chain); 511 512 result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL); 513 while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) { 514 result = dns_rbtnodechain_current(&chain, NULL, NULL, &node); 515 if (result == ISC_R_SUCCESS && node->data != NULL) { 516 dns_zone_setviewrevert(node->data); 517 } 518 519 result = dns_rbtnodechain_next(&chain, NULL, NULL); 520 } 521 522 dns_rbtnodechain_invalidate(&chain); 523} 524 525isc_result_t 526dns_zt_apply(dns_zt_t *zt, isc_rwlocktype_t lock, bool stop, isc_result_t *sub, 527 isc_result_t (*action)(dns_zone_t *, void *), void *uap) { 528 dns_rbtnode_t *node; 529 dns_rbtnodechain_t chain; 530 isc_result_t result, tresult = ISC_R_SUCCESS; 531 dns_zone_t *zone; 532 533 REQUIRE(VALID_ZT(zt)); 534 REQUIRE(action != NULL); 535 536 if (lock != isc_rwlocktype_none) { 537 RWLOCK(&zt->rwlock, lock); 538 } 539 540 dns_rbtnodechain_init(&chain); 541 result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL); 542 if (result == ISC_R_NOTFOUND) { 543 /* 544 * The tree is empty. 545 */ 546 tresult = result; 547 result = ISC_R_NOMORE; 548 } 549 while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) { 550 result = dns_rbtnodechain_current(&chain, NULL, NULL, &node); 551 if (result == ISC_R_SUCCESS) { 552 zone = node->data; 553 if (zone != NULL) { 554 result = (action)(zone, uap); 555 } 556 if (result != ISC_R_SUCCESS && stop) { 557 tresult = result; 558 goto cleanup; /* don't break */ 559 } else if (result != ISC_R_SUCCESS && 560 tresult == ISC_R_SUCCESS) 561 { 562 tresult = result; 563 } 564 } 565 result = dns_rbtnodechain_next(&chain, NULL, NULL); 566 } 567 if (result == ISC_R_NOMORE) { 568 result = ISC_R_SUCCESS; 569 } 570 571cleanup: 572 dns_rbtnodechain_invalidate(&chain); 573 if (sub != NULL) { 574 *sub = tresult; 575 } 576 577 if (lock != isc_rwlocktype_none) { 578 RWUNLOCK(&zt->rwlock, lock); 579 } 580 581 return (result); 582} 583 584/* 585 * Decrement the loads_pending counter; when counter reaches 586 * zero, call the loaddone callback that was initially set by 587 * dns_zt_asyncload(). 588 */ 589static isc_result_t 590doneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task) { 591 UNUSED(zone); 592 UNUSED(task); 593 594 REQUIRE(VALID_ZT(zt)); 595 596 if (isc_refcount_decrement(&zt->loads_pending) == 1) { 597 call_loaddone(zt); 598 } 599 600 if (isc_refcount_decrement(&zt->references) == 1) { 601 zt_destroy(zt); 602 } 603 604 return (ISC_R_SUCCESS); 605} 606 607/*** 608 *** Private 609 ***/ 610 611static void 612auto_detach(void *data, void *arg) { 613 dns_zone_t *zone = data; 614 615 UNUSED(arg); 616 dns_zone_detach(&zone); 617} 618