1/* $NetBSD: zt.c,v 1.5 2012/12/04 23:38:43 spz Exp $ */ 2 3/* 4 * Copyright (C) 2004-2007, 2011, 2012 Internet Systems Consortium, Inc. ("ISC") 5 * Copyright (C) 1999-2002 Internet Software Consortium. 6 * 7 * Permission to use, copy, modify, and/or distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 17 * PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20/* Id */ 21 22/*! \file */ 23 24#include <config.h> 25 26#include <isc/file.h> 27#include <isc/magic.h> 28#include <isc/mem.h> 29#include <isc/string.h> 30#include <isc/task.h> 31#include <isc/util.h> 32 33#include <dns/log.h> 34#include <dns/name.h> 35#include <dns/rbt.h> 36#include <dns/rdataclass.h> 37#include <dns/result.h> 38#include <dns/view.h> 39#include <dns/zone.h> 40#include <dns/zt.h> 41 42struct dns_zt { 43 /* Unlocked. */ 44 unsigned int magic; 45 isc_mem_t *mctx; 46 dns_rdataclass_t rdclass; 47 isc_rwlock_t rwlock; 48 dns_zt_allloaded_t loaddone; 49 void * loaddone_arg; 50 /* Locked by lock. */ 51 isc_boolean_t flush; 52 isc_uint32_t references; 53 unsigned int loads_pending; 54 dns_rbt_t *table; 55}; 56 57#define ZTMAGIC ISC_MAGIC('Z', 'T', 'b', 'l') 58#define VALID_ZT(zt) ISC_MAGIC_VALID(zt, ZTMAGIC) 59 60static void 61auto_detach(void *, void *); 62 63static isc_result_t 64load(dns_zone_t *zone, void *uap); 65 66static isc_result_t 67asyncload(dns_zone_t *zone, void *callback); 68 69static isc_result_t 70loadnew(dns_zone_t *zone, void *uap); 71 72static isc_result_t 73freezezones(dns_zone_t *zone, void *uap); 74 75static isc_result_t 76doneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task); 77 78isc_result_t 79dns_zt_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, dns_zt_t **ztp) 80{ 81 dns_zt_t *zt; 82 isc_result_t result; 83 84 REQUIRE(ztp != NULL && *ztp == NULL); 85 86 zt = isc_mem_get(mctx, sizeof(*zt)); 87 if (zt == NULL) 88 return (ISC_R_NOMEMORY); 89 90 zt->table = NULL; 91 result = dns_rbt_create(mctx, auto_detach, zt, &zt->table); 92 if (result != ISC_R_SUCCESS) 93 goto cleanup_zt; 94 95 result = isc_rwlock_init(&zt->rwlock, 0, 0); 96 if (result != ISC_R_SUCCESS) 97 goto cleanup_rbt; 98 99 zt->mctx = NULL; 100 isc_mem_attach(mctx, &zt->mctx); 101 zt->references = 1; 102 zt->flush = ISC_FALSE; 103 zt->rdclass = rdclass; 104 zt->magic = ZTMAGIC; 105 zt->loaddone = NULL; 106 zt->loaddone_arg = NULL; 107 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 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 RWUNLOCK(&zt->rwlock, isc_rwlocktype_write); 138 139 return (result); 140} 141 142isc_result_t 143dns_zt_unmount(dns_zt_t *zt, dns_zone_t *zone) { 144 isc_result_t result; 145 dns_name_t *name; 146 147 REQUIRE(VALID_ZT(zt)); 148 149 name = dns_zone_getorigin(zone); 150 151 RWLOCK(&zt->rwlock, isc_rwlocktype_write); 152 153 result = dns_rbt_deletename(zt->table, name, ISC_FALSE); 154 155 RWUNLOCK(&zt->rwlock, isc_rwlocktype_write); 156 157 return (result); 158} 159 160isc_result_t 161dns_zt_find(dns_zt_t *zt, dns_name_t *name, unsigned int options, 162 dns_name_t *foundname, dns_zone_t **zonep) 163{ 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 RWLOCK(&zt->rwlock, isc_rwlocktype_read); 174 175 result = dns_rbt_findname(zt->table, name, rbtoptions, foundname, 176 (void **) (void*)&dummy); 177 if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) 178 dns_zone_attach(dummy, zonep); 179 180 RWUNLOCK(&zt->rwlock, isc_rwlocktype_read); 181 182 return (result); 183} 184 185void 186dns_zt_attach(dns_zt_t *zt, dns_zt_t **ztp) { 187 188 REQUIRE(VALID_ZT(zt)); 189 REQUIRE(ztp != NULL && *ztp == NULL); 190 191 RWLOCK(&zt->rwlock, isc_rwlocktype_write); 192 193 INSIST(zt->references > 0); 194 zt->references++; 195 INSIST(zt->references != 0); 196 197 RWUNLOCK(&zt->rwlock, isc_rwlocktype_write); 198 199 *ztp = zt; 200} 201 202static isc_result_t 203flush(dns_zone_t *zone, void *uap) { 204 UNUSED(uap); 205 return (dns_zone_flush(zone)); 206} 207 208static void 209zt_destroy(dns_zt_t *zt) { 210 if (zt->flush) 211 (void)dns_zt_apply(zt, ISC_FALSE, flush, NULL); 212 dns_rbt_destroy(&zt->table); 213 isc_rwlock_destroy(&zt->rwlock); 214 zt->magic = 0; 215 isc_mem_putanddetach(&zt->mctx, zt, sizeof(*zt)); 216} 217 218static void 219zt_flushanddetach(dns_zt_t **ztp, isc_boolean_t need_flush) { 220 isc_boolean_t destroy = ISC_FALSE; 221 dns_zt_t *zt; 222 223 REQUIRE(ztp != NULL && VALID_ZT(*ztp)); 224 225 zt = *ztp; 226 227 RWLOCK(&zt->rwlock, isc_rwlocktype_write); 228 229 INSIST(zt->references > 0); 230 zt->references--; 231 if (zt->references == 0) 232 destroy = ISC_TRUE; 233 if (need_flush) 234 zt->flush = ISC_TRUE; 235 236 RWUNLOCK(&zt->rwlock, isc_rwlocktype_write); 237 238 if (destroy) 239 zt_destroy(zt); 240 241 *ztp = NULL; 242} 243 244void 245dns_zt_flushanddetach(dns_zt_t **ztp) { 246 zt_flushanddetach(ztp, ISC_TRUE); 247} 248 249void 250dns_zt_detach(dns_zt_t **ztp) { 251 zt_flushanddetach(ztp, ISC_FALSE); 252} 253 254isc_result_t 255dns_zt_load(dns_zt_t *zt, isc_boolean_t stop) { 256 isc_result_t result; 257 258 REQUIRE(VALID_ZT(zt)); 259 260 RWLOCK(&zt->rwlock, isc_rwlocktype_read); 261 result = dns_zt_apply(zt, stop, load, NULL); 262 RWUNLOCK(&zt->rwlock, isc_rwlocktype_read); 263 return (result); 264} 265 266static isc_result_t 267load(dns_zone_t *zone, void *uap) { 268 isc_result_t result; 269 UNUSED(uap); 270 271 result = dns_zone_load(zone); 272 if (result == DNS_R_CONTINUE || result == DNS_R_UPTODATE) 273 result = ISC_R_SUCCESS; 274 275 return (result); 276} 277 278isc_result_t 279dns_zt_asyncload(dns_zt_t *zt, dns_zt_allloaded_t alldone, void *arg) { 280 isc_result_t result; 281 static dns_zt_zoneloaded_t dl = doneloading; 282 int pending; 283 284 REQUIRE(VALID_ZT(zt)); 285 286 RWLOCK(&zt->rwlock, isc_rwlocktype_write); 287 288 INSIST(zt->loads_pending == 0); 289 result = dns_zt_apply2(zt, ISC_FALSE, NULL, asyncload, &dl); 290 291 pending = zt->loads_pending; 292 if (pending != 0) { 293 zt->loaddone = alldone; 294 zt->loaddone_arg = arg; 295 } 296 297 RWUNLOCK(&zt->rwlock, isc_rwlocktype_write); 298 299 if (pending == 0) 300 alldone(arg); 301 302 return (result); 303} 304 305/* 306 * Initiates asynchronous loading of zone 'zone'. 'callback' is a 307 * pointer to a function which will be used to inform the caller when 308 * the zone loading is complete. 309 */ 310static isc_result_t 311asyncload(dns_zone_t *zone, void *callback) { 312 isc_result_t result; 313 dns_zt_zoneloaded_t *loaded = callback; 314 dns_zt_t *zt; 315 316 REQUIRE(zone != NULL); 317 zt = dns_zone_getview(zone)->zonetable; 318 INSIST(VALID_ZT(zt)); 319 320 result = dns_zone_asyncload(zone, *loaded, zt); 321 if (result == ISC_R_SUCCESS) { 322 INSIST(zt->references > 0); 323 zt->references++; 324 INSIST(zt->references != 0); 325 zt->loads_pending++; 326 } 327 return (ISC_R_SUCCESS); 328} 329 330isc_result_t 331dns_zt_loadnew(dns_zt_t *zt, isc_boolean_t stop) { 332 isc_result_t result; 333 334 REQUIRE(VALID_ZT(zt)); 335 336 RWLOCK(&zt->rwlock, isc_rwlocktype_read); 337 result = dns_zt_apply(zt, stop, loadnew, NULL); 338 RWUNLOCK(&zt->rwlock, isc_rwlocktype_read); 339 return (result); 340} 341 342static isc_result_t 343loadnew(dns_zone_t *zone, void *uap) { 344 isc_result_t result; 345 UNUSED(uap); 346 347 result = dns_zone_loadnew(zone); 348 if (result == DNS_R_CONTINUE || result == DNS_R_UPTODATE || 349 result == DNS_R_DYNAMIC) 350 result = ISC_R_SUCCESS; 351 return (result); 352} 353 354isc_result_t 355dns_zt_freezezones(dns_zt_t *zt, isc_boolean_t freeze) { 356 isc_result_t result, tresult; 357 358 REQUIRE(VALID_ZT(zt)); 359 360 RWLOCK(&zt->rwlock, isc_rwlocktype_read); 361 result = dns_zt_apply2(zt, ISC_FALSE, &tresult, freezezones, &freeze); 362 RWUNLOCK(&zt->rwlock, isc_rwlocktype_read); 363 if (tresult == ISC_R_NOTFOUND) 364 tresult = ISC_R_SUCCESS; 365 return ((result == ISC_R_SUCCESS) ? tresult : result); 366} 367 368static isc_result_t 369freezezones(dns_zone_t *zone, void *uap) { 370 isc_boolean_t freeze = *(isc_boolean_t *)uap; 371 isc_boolean_t frozen; 372 isc_result_t result = ISC_R_SUCCESS; 373 char classstr[DNS_RDATACLASS_FORMATSIZE]; 374 char zonename[DNS_NAME_FORMATSIZE]; 375 dns_zone_t *raw = NULL; 376 dns_view_t *view; 377 const char *vname; 378 const char *sep; 379 int level; 380 381 dns_zone_getraw(zone, &raw); 382 if (raw != NULL) 383 zone = raw; 384 if (dns_zone_gettype(zone) != dns_zone_master) { 385 if (raw != NULL) 386 dns_zone_detach(&raw); 387 return (ISC_R_SUCCESS); 388 } 389 if (!dns_zone_isdynamic(zone, ISC_TRUE)) { 390 if (raw != NULL) 391 dns_zone_detach(&raw); 392 return (ISC_R_SUCCESS); 393 } 394 395 frozen = dns_zone_getupdatedisabled(zone); 396 if (freeze) { 397 if (frozen) 398 result = DNS_R_FROZEN; 399 if (result == ISC_R_SUCCESS) 400 result = dns_zone_flush(zone); 401 } else { 402 if (frozen) { 403 result = dns_zone_load(zone); 404 if (result == DNS_R_CONTINUE || 405 result == DNS_R_UPTODATE) 406 result = ISC_R_SUCCESS; 407 } 408 } 409 if (result == ISC_R_SUCCESS) 410 dns_zone_setupdatedisabled(zone, freeze); 411 view = dns_zone_getview(zone); 412 if (strcmp(view->name, "_bind") == 0 || 413 strcmp(view->name, "_default") == 0) 414 { 415 vname = ""; 416 sep = ""; 417 } else { 418 vname = view->name; 419 sep = " "; 420 } 421 dns_rdataclass_format(dns_zone_getclass(zone), classstr, 422 sizeof(classstr)); 423 dns_name_format(dns_zone_getorigin(zone), zonename, sizeof(zonename)); 424 level = (result != ISC_R_SUCCESS) ? ISC_LOG_ERROR : ISC_LOG_DEBUG(1); 425 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE, 426 level, "%s zone '%s/%s'%s%s: %s", 427 freeze ? "freezing" : "thawing", 428 zonename, classstr, sep, vname, 429 isc_result_totext(result)); 430 if (raw != NULL) 431 dns_zone_detach(&raw); 432 return (result); 433} 434 435isc_result_t 436dns_zt_apply(dns_zt_t *zt, isc_boolean_t stop, 437 isc_result_t (*action)(dns_zone_t *, void *), void *uap) 438{ 439 return (dns_zt_apply2(zt, stop, NULL, action, uap)); 440} 441 442isc_result_t 443dns_zt_apply2(dns_zt_t *zt, isc_boolean_t stop, isc_result_t *sub, 444 isc_result_t (*action)(dns_zone_t *, void *), void *uap) 445{ 446 dns_rbtnode_t *node; 447 dns_rbtnodechain_t chain; 448 isc_result_t result, tresult = ISC_R_SUCCESS; 449 dns_zone_t *zone; 450 451 REQUIRE(VALID_ZT(zt)); 452 REQUIRE(action != NULL); 453 454 dns_rbtnodechain_init(&chain, zt->mctx); 455 result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL); 456 if (result == ISC_R_NOTFOUND) { 457 /* 458 * The tree is empty. 459 */ 460 tresult = result; 461 result = ISC_R_NOMORE; 462 } 463 while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) { 464 result = dns_rbtnodechain_current(&chain, NULL, NULL, 465 &node); 466 if (result == ISC_R_SUCCESS) { 467 zone = node->data; 468 if (zone != NULL) 469 result = (action)(zone, uap); 470 if (result != ISC_R_SUCCESS && stop) { 471 tresult = result; 472 goto cleanup; /* don't break */ 473 } else if (result != ISC_R_SUCCESS && 474 tresult == ISC_R_SUCCESS) 475 tresult = result; 476 } 477 result = dns_rbtnodechain_next(&chain, NULL, NULL); 478 } 479 if (result == ISC_R_NOMORE) 480 result = ISC_R_SUCCESS; 481 482 cleanup: 483 dns_rbtnodechain_invalidate(&chain); 484 if (sub != NULL) 485 *sub = tresult; 486 487 return (result); 488} 489 490/* 491 * Decrement the loads_pending counter; when counter reaches 492 * zero, call the loaddone callback that was initially set by 493 * dns_zt_asyncload(). 494 */ 495static isc_result_t 496doneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task) { 497 isc_boolean_t destroy = ISC_FALSE; 498 dns_zt_allloaded_t alldone = NULL; 499 void *arg = NULL; 500 501 UNUSED(zone); 502 UNUSED(task); 503 504 REQUIRE(VALID_ZT(zt)); 505 506 RWLOCK(&zt->rwlock, isc_rwlocktype_write); 507 INSIST(zt->loads_pending != 0); 508 INSIST(zt->references != 0); 509 zt->references--; 510 if (zt->references == 0) 511 destroy = ISC_TRUE; 512 zt->loads_pending--; 513 if (zt->loads_pending == 0) { 514 alldone = zt->loaddone; 515 arg = zt->loaddone_arg; 516 zt->loaddone = NULL; 517 zt->loaddone_arg = NULL; 518 } 519 RWUNLOCK(&zt->rwlock, isc_rwlocktype_write); 520 521 if (alldone != NULL) 522 alldone(arg); 523 524 if (destroy) 525 zt_destroy(zt); 526 527 return (ISC_R_SUCCESS); 528} 529 530/*** 531 *** Private 532 ***/ 533 534static void 535auto_detach(void *data, void *arg) { 536 dns_zone_t *zone = data; 537 538 UNUSED(arg); 539 540 dns_zone_detach(&zone); 541} 542