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