1/* $NetBSD: zt.c,v 1.1 2024/02/18 20:57:34 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/string.h> 26#include <isc/task.h> 27#include <isc/util.h> 28 29#include <dns/log.h> 30#include <dns/name.h> 31#include <dns/rbt.h> 32#include <dns/rdataclass.h> 33#include <dns/result.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 if (atomic_load_acquire(&zt->flush)) { 230 (void)dns_zt_apply(zt, isc_rwlocktype_none, false, NULL, flush, 231 NULL); 232 } 233 dns_rbt_destroy(&zt->table); 234 isc_rwlock_destroy(&zt->rwlock); 235 zt->magic = 0; 236 isc_mem_putanddetach(&zt->mctx, zt, sizeof(*zt)); 237} 238 239static void 240zt_flushanddetach(dns_zt_t **ztp, bool need_flush) { 241 dns_zt_t *zt; 242 243 REQUIRE(ztp != NULL && VALID_ZT(*ztp)); 244 245 zt = *ztp; 246 *ztp = NULL; 247 248 if (need_flush) { 249 atomic_store_release(&zt->flush, true); 250 } 251 252 if (isc_refcount_decrement(&zt->references) == 1) { 253 zt_destroy(zt); 254 } 255} 256 257void 258dns_zt_flushanddetach(dns_zt_t **ztp) { 259 zt_flushanddetach(ztp, true); 260} 261 262void 263dns_zt_detach(dns_zt_t **ztp) { 264 zt_flushanddetach(ztp, false); 265} 266 267isc_result_t 268dns_zt_load(dns_zt_t *zt, bool stop, bool newonly) { 269 isc_result_t result; 270 struct zt_load_params params; 271 REQUIRE(VALID_ZT(zt)); 272 params.newonly = newonly; 273 result = dns_zt_apply(zt, isc_rwlocktype_read, stop, NULL, load, 274 ¶ms); 275 return (result); 276} 277 278static isc_result_t 279load(dns_zone_t *zone, void *paramsv) { 280 isc_result_t result; 281 struct zt_load_params *params = (struct zt_load_params *)paramsv; 282 result = dns_zone_load(zone, params->newonly); 283 if (result == DNS_R_CONTINUE || result == DNS_R_UPTODATE || 284 result == DNS_R_DYNAMIC) 285 { 286 result = ISC_R_SUCCESS; 287 } 288 return (result); 289} 290 291static void 292call_loaddone(dns_zt_t *zt) { 293 dns_zt_allloaded_t loaddone = zt->loaddone; 294 void *loaddone_arg = zt->loaddone_arg; 295 296 /* 297 * Set zt->loaddone, zt->loaddone_arg and zt->loadparams to NULL 298 * before calling loaddone. 299 */ 300 zt->loaddone = NULL; 301 zt->loaddone_arg = NULL; 302 303 isc_mem_put(zt->mctx, zt->loadparams, sizeof(struct zt_load_params)); 304 zt->loadparams = NULL; 305 306 /* 307 * Call the callback last. 308 */ 309 if (loaddone != NULL) { 310 loaddone(loaddone_arg); 311 } 312} 313 314isc_result_t 315dns_zt_asyncload(dns_zt_t *zt, bool newonly, dns_zt_allloaded_t alldone, 316 void *arg) { 317 isc_result_t result; 318 uint_fast32_t loads_pending; 319 320 REQUIRE(VALID_ZT(zt)); 321 322 /* 323 * Obtain a reference to zt->loads_pending so that asyncload can 324 * safely decrement both zt->references and zt->loads_pending 325 * without going to zero. 326 */ 327 loads_pending = isc_refcount_increment0(&zt->loads_pending); 328 INSIST(loads_pending == 0); 329 330 /* 331 * Only one dns_zt_asyncload call at a time should be active so 332 * these pointers should be NULL. They are set back to NULL 333 * before the zt->loaddone (alldone) is called in call_loaddone. 334 */ 335 INSIST(zt->loadparams == NULL); 336 INSIST(zt->loaddone == NULL); 337 INSIST(zt->loaddone_arg == NULL); 338 339 zt->loadparams = isc_mem_get(zt->mctx, sizeof(struct zt_load_params)); 340 zt->loadparams->dl = doneloading; 341 zt->loadparams->newonly = newonly; 342 zt->loaddone = alldone; 343 zt->loaddone_arg = arg; 344 345 result = dns_zt_apply(zt, isc_rwlocktype_read, false, NULL, asyncload, 346 zt); 347 348 /* 349 * Have all the loads completed? 350 */ 351 if (isc_refcount_decrement(&zt->loads_pending) == 1) { 352 call_loaddone(zt); 353 } 354 355 return (result); 356} 357 358/* 359 * Initiates asynchronous loading of zone 'zone'. 'callback' is a 360 * pointer to a function which will be used to inform the caller when 361 * the zone loading is complete. 362 */ 363static isc_result_t 364asyncload(dns_zone_t *zone, void *zt_) { 365 isc_result_t result; 366 struct dns_zt *zt = (dns_zt_t *)zt_; 367 REQUIRE(zone != NULL); 368 369 isc_refcount_increment(&zt->references); 370 isc_refcount_increment(&zt->loads_pending); 371 372 result = dns_zone_asyncload(zone, zt->loadparams->newonly, 373 *zt->loadparams->dl, zt); 374 if (result != ISC_R_SUCCESS) { 375 /* 376 * Caller is holding a reference to zt->loads_pending 377 * and zt->references so these can't decrement to zero. 378 */ 379 isc_refcount_decrement1(&zt->references); 380 isc_refcount_decrement1(&zt->loads_pending); 381 } 382 return (ISC_R_SUCCESS); 383} 384 385isc_result_t 386dns_zt_freezezones(dns_zt_t *zt, dns_view_t *view, bool freeze) { 387 isc_result_t result, tresult; 388 struct zt_freeze_params params = { view, freeze }; 389 390 REQUIRE(VALID_ZT(zt)); 391 392 result = dns_zt_apply(zt, isc_rwlocktype_read, false, &tresult, 393 freezezones, ¶ms); 394 if (tresult == ISC_R_NOTFOUND) { 395 tresult = ISC_R_SUCCESS; 396 } 397 return ((result == ISC_R_SUCCESS) ? tresult : result); 398} 399 400static isc_result_t 401freezezones(dns_zone_t *zone, void *uap) { 402 struct zt_freeze_params *params = uap; 403 bool frozen; 404 isc_result_t result = ISC_R_SUCCESS; 405 char classstr[DNS_RDATACLASS_FORMATSIZE]; 406 char zonename[DNS_NAME_FORMATSIZE]; 407 dns_zone_t *raw = NULL; 408 dns_view_t *view; 409 const char *vname; 410 const char *sep; 411 int level; 412 413 dns_zone_getraw(zone, &raw); 414 if (raw != NULL) { 415 zone = raw; 416 } 417 if (params->view != dns_zone_getview(zone)) { 418 if (raw != NULL) { 419 dns_zone_detach(&raw); 420 } 421 return (ISC_R_SUCCESS); 422 } 423 if (dns_zone_gettype(zone) != dns_zone_primary) { 424 if (raw != NULL) { 425 dns_zone_detach(&raw); 426 } 427 return (ISC_R_SUCCESS); 428 } 429 if (!dns_zone_isdynamic(zone, true)) { 430 if (raw != NULL) { 431 dns_zone_detach(&raw); 432 } 433 return (ISC_R_SUCCESS); 434 } 435 436 frozen = dns_zone_getupdatedisabled(zone); 437 if (params->freeze) { 438 if (frozen) { 439 result = DNS_R_FROZEN; 440 } 441 if (result == ISC_R_SUCCESS) { 442 result = dns_zone_flush(zone); 443 } 444 if (result == ISC_R_SUCCESS) { 445 dns_zone_setupdatedisabled(zone, params->freeze); 446 } 447 } else { 448 if (frozen) { 449 result = dns_zone_loadandthaw(zone); 450 if (result == DNS_R_CONTINUE || 451 result == DNS_R_UPTODATE) 452 { 453 result = ISC_R_SUCCESS; 454 } 455 } 456 } 457 view = dns_zone_getview(zone); 458 if (strcmp(view->name, "_bind") == 0 || strcmp(view->name, "_defaul" 459 "t") == 0) 460 { 461 vname = ""; 462 sep = ""; 463 } else { 464 vname = view->name; 465 sep = " "; 466 } 467 dns_rdataclass_format(dns_zone_getclass(zone), classstr, 468 sizeof(classstr)); 469 dns_name_format(dns_zone_getorigin(zone), zonename, sizeof(zonename)); 470 level = (result != ISC_R_SUCCESS) ? ISC_LOG_ERROR : ISC_LOG_DEBUG(1); 471 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE, 472 level, "%s zone '%s/%s'%s%s: %s", 473 params->freeze ? "freezing" : "thawing", zonename, 474 classstr, sep, vname, isc_result_totext(result)); 475 if (raw != NULL) { 476 dns_zone_detach(&raw); 477 } 478 return (result); 479} 480 481void 482dns_zt_setviewcommit(dns_zt_t *zt) { 483 dns_rbtnode_t *node; 484 dns_rbtnodechain_t chain; 485 isc_result_t result; 486 487 REQUIRE(VALID_ZT(zt)); 488 489 dns_rbtnodechain_init(&chain); 490 491 result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL); 492 while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) { 493 result = dns_rbtnodechain_current(&chain, NULL, NULL, &node); 494 if (result == ISC_R_SUCCESS && node->data != NULL) { 495 dns_zone_setviewcommit(node->data); 496 } 497 498 result = dns_rbtnodechain_next(&chain, NULL, NULL); 499 } 500 501 dns_rbtnodechain_invalidate(&chain); 502} 503 504void 505dns_zt_setviewrevert(dns_zt_t *zt) { 506 dns_rbtnode_t *node; 507 dns_rbtnodechain_t chain; 508 isc_result_t result; 509 510 REQUIRE(VALID_ZT(zt)); 511 512 dns_rbtnodechain_init(&chain); 513 514 result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL); 515 while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) { 516 result = dns_rbtnodechain_current(&chain, NULL, NULL, &node); 517 if (result == ISC_R_SUCCESS && node->data != NULL) { 518 dns_zone_setviewrevert(node->data); 519 } 520 521 result = dns_rbtnodechain_next(&chain, NULL, NULL); 522 } 523 524 dns_rbtnodechain_invalidate(&chain); 525} 526 527isc_result_t 528dns_zt_apply(dns_zt_t *zt, isc_rwlocktype_t lock, bool stop, isc_result_t *sub, 529 isc_result_t (*action)(dns_zone_t *, void *), void *uap) { 530 dns_rbtnode_t *node; 531 dns_rbtnodechain_t chain; 532 isc_result_t result, tresult = ISC_R_SUCCESS; 533 dns_zone_t *zone; 534 535 REQUIRE(VALID_ZT(zt)); 536 REQUIRE(action != NULL); 537 538 if (lock != isc_rwlocktype_none) { 539 RWLOCK(&zt->rwlock, lock); 540 } 541 542 dns_rbtnodechain_init(&chain); 543 result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL); 544 if (result == ISC_R_NOTFOUND) { 545 /* 546 * The tree is empty. 547 */ 548 tresult = result; 549 result = ISC_R_NOMORE; 550 } 551 while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) { 552 result = dns_rbtnodechain_current(&chain, NULL, NULL, &node); 553 if (result == ISC_R_SUCCESS) { 554 zone = node->data; 555 if (zone != NULL) { 556 result = (action)(zone, uap); 557 } 558 if (result != ISC_R_SUCCESS && stop) { 559 tresult = result; 560 goto cleanup; /* don't break */ 561 } else if (result != ISC_R_SUCCESS && 562 tresult == ISC_R_SUCCESS) 563 { 564 tresult = result; 565 } 566 } 567 result = dns_rbtnodechain_next(&chain, NULL, NULL); 568 } 569 if (result == ISC_R_NOMORE) { 570 result = ISC_R_SUCCESS; 571 } 572 573cleanup: 574 dns_rbtnodechain_invalidate(&chain); 575 if (sub != NULL) { 576 *sub = tresult; 577 } 578 579 if (lock != isc_rwlocktype_none) { 580 RWUNLOCK(&zt->rwlock, lock); 581 } 582 583 return (result); 584} 585 586/* 587 * Decrement the loads_pending counter; when counter reaches 588 * zero, call the loaddone callback that was initially set by 589 * dns_zt_asyncload(). 590 */ 591static isc_result_t 592doneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task) { 593 UNUSED(zone); 594 UNUSED(task); 595 596 REQUIRE(VALID_ZT(zt)); 597 598 if (isc_refcount_decrement(&zt->loads_pending) == 1) { 599 call_loaddone(zt); 600 } 601 602 if (isc_refcount_decrement(&zt->references) == 1) { 603 zt_destroy(zt); 604 } 605 606 return (ISC_R_SUCCESS); 607} 608 609/*** 610 *** Private 611 ***/ 612 613static void 614auto_detach(void *data, void *arg) { 615 dns_zone_t *zone = data; 616 617 UNUSED(arg); 618 dns_zone_detach(&zone); 619} 620