1 /* $NetBSD: zt.c,v 1.5 2020/05/24 19:46:23 christos Exp $ */ 2 3 /* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * This Source Code Form is subject to the terms of the Mozilla Public 7 * License, v. 2.0. If a copy of the MPL was not distributed with this 8 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * See the COPYRIGHT file distributed with this work for additional 11 * information regarding copyright ownership. 12 */ 13 14 /*! \file */ 15 16 #include <inttypes.h> 17 #include <stdbool.h> 18 19 #include <isc/atomic.h> 20 #include <isc/file.h> 21 #include <isc/magic.h> 22 #include <isc/mem.h> 23 #include <isc/string.h> 24 #include <isc/task.h> 25 #include <isc/util.h> 26 27 #include <dns/log.h> 28 #include <dns/name.h> 29 #include <dns/rbt.h> 30 #include <dns/rdataclass.h> 31 #include <dns/result.h> 32 #include <dns/view.h> 33 #include <dns/zone.h> 34 #include <dns/zt.h> 35 36 struct zt_load_params { 37 dns_zt_zoneloaded_t dl; 38 bool newonly; 39 }; 40 41 struct dns_zt { 42 /* Unlocked. */ 43 unsigned int magic; 44 isc_mem_t *mctx; 45 dns_rdataclass_t rdclass; 46 isc_rwlock_t rwlock; 47 dns_zt_allloaded_t loaddone; 48 void *loaddone_arg; 49 struct zt_load_params *loadparams; 50 51 /* Atomic */ 52 atomic_bool flush; 53 isc_refcount_t references; 54 isc_refcount_t loads_pending; 55 56 /* Locked by lock. */ 57 dns_rbt_t *table; 58 }; 59 60 #define ZTMAGIC ISC_MAGIC('Z', 'T', 'b', 'l') 61 #define VALID_ZT(zt) ISC_MAGIC_VALID(zt, ZTMAGIC) 62 63 static void 64 auto_detach(void *, void *); 65 66 static isc_result_t 67 load(dns_zone_t *zone, void *uap); 68 69 static isc_result_t 70 asyncload(dns_zone_t *zone, void *callback); 71 72 static isc_result_t 73 freezezones(dns_zone_t *zone, void *uap); 74 75 static isc_result_t 76 doneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task); 77 78 isc_result_t 79 dns_zt_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, dns_zt_t **ztp) { 80 dns_zt_t *zt; 81 isc_result_t result; 82 83 REQUIRE(ztp != NULL && *ztp == NULL); 84 85 zt = isc_mem_get(mctx, sizeof(*zt)); 86 87 zt->table = NULL; 88 result = dns_rbt_create(mctx, auto_detach, zt, &zt->table); 89 if (result != ISC_R_SUCCESS) { 90 goto cleanup_zt; 91 } 92 93 result = isc_rwlock_init(&zt->rwlock, 0, 0); 94 if (result != ISC_R_SUCCESS) { 95 goto cleanup_rbt; 96 } 97 98 zt->mctx = NULL; 99 isc_mem_attach(mctx, &zt->mctx); 100 isc_refcount_init(&zt->references, 1); 101 atomic_init(&zt->flush, false); 102 zt->rdclass = rdclass; 103 zt->magic = ZTMAGIC; 104 zt->loaddone = NULL; 105 zt->loaddone_arg = NULL; 106 zt->loadparams = NULL; 107 isc_refcount_init(&zt->loads_pending, 0); 108 *ztp = zt; 109 110 return (ISC_R_SUCCESS); 111 112 cleanup_rbt: 113 dns_rbt_destroy(&zt->table); 114 115 cleanup_zt: 116 isc_mem_put(mctx, zt, sizeof(*zt)); 117 118 return (result); 119 } 120 121 isc_result_t 122 dns_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 143 isc_result_t 144 dns_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 161 isc_result_t 162 dns_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 211 void 212 dns_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 221 static isc_result_t 222 flush(dns_zone_t *zone, void *uap) { 223 UNUSED(uap); 224 return (dns_zone_flush(zone)); 225 } 226 227 static void 228 zt_destroy(dns_zt_t *zt) { 229 if (atomic_load_acquire(&zt->flush)) { 230 (void)dns_zt_apply(zt, false, NULL, flush, NULL); 231 } 232 dns_rbt_destroy(&zt->table); 233 isc_rwlock_destroy(&zt->rwlock); 234 zt->magic = 0; 235 isc_mem_putanddetach(&zt->mctx, zt, sizeof(*zt)); 236 } 237 238 static void 239 zt_flushanddetach(dns_zt_t **ztp, bool need_flush) { 240 dns_zt_t *zt; 241 242 REQUIRE(ztp != NULL && VALID_ZT(*ztp)); 243 244 zt = *ztp; 245 *ztp = NULL; 246 247 if (need_flush) { 248 atomic_store_release(&zt->flush, true); 249 } 250 251 if (isc_refcount_decrement(&zt->references) == 1) { 252 zt_destroy(zt); 253 } 254 } 255 256 void 257 dns_zt_flushanddetach(dns_zt_t **ztp) { 258 zt_flushanddetach(ztp, true); 259 } 260 261 void 262 dns_zt_detach(dns_zt_t **ztp) { 263 zt_flushanddetach(ztp, false); 264 } 265 266 isc_result_t 267 dns_zt_load(dns_zt_t *zt, bool stop, bool newonly) { 268 isc_result_t result; 269 struct zt_load_params params; 270 REQUIRE(VALID_ZT(zt)); 271 params.newonly = newonly; 272 RWLOCK(&zt->rwlock, isc_rwlocktype_read); 273 result = dns_zt_apply(zt, stop, NULL, load, ¶ms); 274 RWUNLOCK(&zt->rwlock, isc_rwlocktype_read); 275 return (result); 276 } 277 278 static isc_result_t 279 load(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 291 static void 292 call_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 314 isc_result_t 315 dns_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 RWLOCK(&zt->rwlock, isc_rwlocktype_read); 346 result = dns_zt_apply(zt, false, NULL, asyncload, zt); 347 RWUNLOCK(&zt->rwlock, isc_rwlocktype_read); 348 349 /* 350 * Have all the loads completed? 351 */ 352 if (isc_refcount_decrement(&zt->loads_pending) == 1) { 353 call_loaddone(zt); 354 } 355 356 return (result); 357 } 358 359 /* 360 * Initiates asynchronous loading of zone 'zone'. 'callback' is a 361 * pointer to a function which will be used to inform the caller when 362 * the zone loading is complete. 363 */ 364 static isc_result_t 365 asyncload(dns_zone_t *zone, void *zt_) { 366 isc_result_t result; 367 struct dns_zt *zt = (dns_zt_t *)zt_; 368 REQUIRE(zone != NULL); 369 370 isc_refcount_increment(&zt->references); 371 isc_refcount_increment(&zt->loads_pending); 372 373 result = dns_zone_asyncload(zone, zt->loadparams->newonly, 374 *zt->loadparams->dl, zt); 375 if (result != ISC_R_SUCCESS) { 376 /* 377 * Caller is holding a reference to zt->loads_pending 378 * and zt->references so these can't decrement to zero. 379 */ 380 INSIST(isc_refcount_decrement(&zt->loads_pending) > 1); 381 INSIST(isc_refcount_decrement(&zt->references) > 1); 382 } 383 return (ISC_R_SUCCESS); 384 } 385 386 isc_result_t 387 dns_zt_freezezones(dns_zt_t *zt, bool freeze) { 388 isc_result_t result, tresult; 389 390 REQUIRE(VALID_ZT(zt)); 391 392 RWLOCK(&zt->rwlock, isc_rwlocktype_read); 393 result = dns_zt_apply(zt, false, &tresult, freezezones, &freeze); 394 RWUNLOCK(&zt->rwlock, isc_rwlocktype_read); 395 if (tresult == ISC_R_NOTFOUND) { 396 tresult = ISC_R_SUCCESS; 397 } 398 return ((result == ISC_R_SUCCESS) ? tresult : result); 399 } 400 401 static isc_result_t 402 freezezones(dns_zone_t *zone, void *uap) { 403 bool freeze = *(bool *)uap; 404 bool frozen; 405 isc_result_t result = ISC_R_SUCCESS; 406 char classstr[DNS_RDATACLASS_FORMATSIZE]; 407 char zonename[DNS_NAME_FORMATSIZE]; 408 dns_zone_t *raw = NULL; 409 dns_view_t *view; 410 const char *vname; 411 const char *sep; 412 int level; 413 414 dns_zone_getraw(zone, &raw); 415 if (raw != NULL) { 416 zone = raw; 417 } 418 if (dns_zone_gettype(zone) != dns_zone_master) { 419 if (raw != NULL) { 420 dns_zone_detach(&raw); 421 } 422 return (ISC_R_SUCCESS); 423 } 424 if (!dns_zone_isdynamic(zone, true)) { 425 if (raw != NULL) { 426 dns_zone_detach(&raw); 427 } 428 return (ISC_R_SUCCESS); 429 } 430 431 frozen = dns_zone_getupdatedisabled(zone); 432 if (freeze) { 433 if (frozen) { 434 result = DNS_R_FROZEN; 435 } 436 if (result == ISC_R_SUCCESS) { 437 result = dns_zone_flush(zone); 438 } 439 if (result == ISC_R_SUCCESS) { 440 dns_zone_setupdatedisabled(zone, freeze); 441 } 442 } else { 443 if (frozen) { 444 result = dns_zone_loadandthaw(zone); 445 if (result == DNS_R_CONTINUE || 446 result == DNS_R_UPTODATE) { 447 result = ISC_R_SUCCESS; 448 } 449 } 450 } 451 view = dns_zone_getview(zone); 452 if (strcmp(view->name, "_bind") == 0 || strcmp(view->name, "_defaul" 453 "t") == 0) 454 { 455 vname = ""; 456 sep = ""; 457 } else { 458 vname = view->name; 459 sep = " "; 460 } 461 dns_rdataclass_format(dns_zone_getclass(zone), classstr, 462 sizeof(classstr)); 463 dns_name_format(dns_zone_getorigin(zone), zonename, sizeof(zonename)); 464 level = (result != ISC_R_SUCCESS) ? ISC_LOG_ERROR : ISC_LOG_DEBUG(1); 465 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE, 466 level, "%s zone '%s/%s'%s%s: %s", 467 freeze ? "freezing" : "thawing", zonename, classstr, sep, 468 vname, isc_result_totext(result)); 469 if (raw != NULL) { 470 dns_zone_detach(&raw); 471 } 472 return (result); 473 } 474 475 void 476 dns_zt_setviewcommit(dns_zt_t *zt) { 477 dns_rbtnode_t *node; 478 dns_rbtnodechain_t chain; 479 isc_result_t result; 480 481 REQUIRE(VALID_ZT(zt)); 482 483 dns_rbtnodechain_init(&chain); 484 485 result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL); 486 while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) { 487 result = dns_rbtnodechain_current(&chain, NULL, NULL, &node); 488 if (result == ISC_R_SUCCESS && node->data != NULL) { 489 dns_zone_setviewcommit(node->data); 490 } 491 492 result = dns_rbtnodechain_next(&chain, NULL, NULL); 493 } 494 495 dns_rbtnodechain_invalidate(&chain); 496 } 497 498 void 499 dns_zt_setviewrevert(dns_zt_t *zt) { 500 dns_rbtnode_t *node; 501 dns_rbtnodechain_t chain; 502 isc_result_t result; 503 504 REQUIRE(VALID_ZT(zt)); 505 506 dns_rbtnodechain_init(&chain); 507 508 result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL); 509 while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) { 510 result = dns_rbtnodechain_current(&chain, NULL, NULL, &node); 511 if (result == ISC_R_SUCCESS && node->data != NULL) { 512 dns_zone_setviewrevert(node->data); 513 } 514 515 result = dns_rbtnodechain_next(&chain, NULL, NULL); 516 } 517 518 dns_rbtnodechain_invalidate(&chain); 519 } 520 521 isc_result_t 522 dns_zt_apply(dns_zt_t *zt, bool stop, isc_result_t *sub, 523 isc_result_t (*action)(dns_zone_t *, void *), void *uap) { 524 dns_rbtnode_t *node; 525 dns_rbtnodechain_t chain; 526 isc_result_t result, tresult = ISC_R_SUCCESS; 527 dns_zone_t *zone; 528 529 REQUIRE(VALID_ZT(zt)); 530 REQUIRE(action != NULL); 531 532 dns_rbtnodechain_init(&chain); 533 result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL); 534 if (result == ISC_R_NOTFOUND) { 535 /* 536 * The tree is empty. 537 */ 538 tresult = result; 539 result = ISC_R_NOMORE; 540 } 541 while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) { 542 result = dns_rbtnodechain_current(&chain, NULL, NULL, &node); 543 if (result == ISC_R_SUCCESS) { 544 zone = node->data; 545 if (zone != NULL) { 546 result = (action)(zone, uap); 547 } 548 if (result != ISC_R_SUCCESS && stop) { 549 tresult = result; 550 goto cleanup; /* don't break */ 551 } else if (result != ISC_R_SUCCESS && 552 tresult == ISC_R_SUCCESS) { 553 tresult = result; 554 } 555 } 556 result = dns_rbtnodechain_next(&chain, NULL, NULL); 557 } 558 if (result == ISC_R_NOMORE) { 559 result = ISC_R_SUCCESS; 560 } 561 562 cleanup: 563 dns_rbtnodechain_invalidate(&chain); 564 if (sub != NULL) { 565 *sub = tresult; 566 } 567 568 return (result); 569 } 570 571 /* 572 * Decrement the loads_pending counter; when counter reaches 573 * zero, call the loaddone callback that was initially set by 574 * dns_zt_asyncload(). 575 */ 576 static isc_result_t 577 doneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task) { 578 UNUSED(zone); 579 UNUSED(task); 580 581 REQUIRE(VALID_ZT(zt)); 582 583 if (isc_refcount_decrement(&zt->loads_pending) == 1) { 584 call_loaddone(zt); 585 } 586 587 if (isc_refcount_decrement(&zt->references) == 1) { 588 zt_destroy(zt); 589 } 590 591 return (ISC_R_SUCCESS); 592 } 593 594 /*** 595 *** Private 596 ***/ 597 598 static void 599 auto_detach(void *data, void *arg) { 600 dns_zone_t *zone = data; 601 602 UNUSED(arg); 603 dns_zone_detach(&zone); 604 } 605