1 /* $NetBSD: catz.c,v 1.18 2026/06/19 20:09:59 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 #include <stdint.h> 21 #include <stdlib.h> 22 23 #include <isc/async.h> 24 #include <isc/hex.h> 25 #include <isc/loop.h> 26 #include <isc/md.h> 27 #include <isc/mem.h> 28 #include <isc/parseint.h> 29 #include <isc/result.h> 30 #include <isc/util.h> 31 #include <isc/work.h> 32 33 #include <dns/catz.h> 34 #include <dns/dbiterator.h> 35 #include <dns/rdatasetiter.h> 36 #include <dns/view.h> 37 #include <dns/zone.h> 38 39 #define DNS_CATZ_ZONE_MAGIC ISC_MAGIC('c', 'a', 't', 'z') 40 #define DNS_CATZ_ZONES_MAGIC ISC_MAGIC('c', 'a', 't', 's') 41 #define DNS_CATZ_ENTRY_MAGIC ISC_MAGIC('c', 'a', 't', 'e') 42 #define DNS_CATZ_COO_MAGIC ISC_MAGIC('c', 'a', 't', 'c') 43 44 #define DNS_CATZ_ZONE_VALID(catz) ISC_MAGIC_VALID(catz, DNS_CATZ_ZONE_MAGIC) 45 #define DNS_CATZ_ZONES_VALID(catzs) ISC_MAGIC_VALID(catzs, DNS_CATZ_ZONES_MAGIC) 46 #define DNS_CATZ_ENTRY_VALID(entry) ISC_MAGIC_VALID(entry, DNS_CATZ_ENTRY_MAGIC) 47 #define DNS_CATZ_COO_VALID(coo) ISC_MAGIC_VALID(coo, DNS_CATZ_COO_MAGIC) 48 49 #define DNS_CATZ_VERSION_UNDEFINED ((uint32_t)(-1)) 50 51 /*% 52 * Change of ownership permissions 53 */ 54 struct dns_catz_coo { 55 unsigned int magic; 56 dns_name_t name; 57 isc_refcount_t references; 58 }; 59 60 /*% 61 * Single member zone in a catalog 62 */ 63 struct dns_catz_entry { 64 unsigned int magic; 65 dns_name_t name; 66 dns_catz_options_t opts; 67 isc_refcount_t references; 68 }; 69 70 /*% 71 * Catalog zone 72 */ 73 struct dns_catz_zone { 74 unsigned int magic; 75 isc_loop_t *loop; 76 dns_name_t name; 77 dns_catz_zones_t *catzs; 78 dns_rdata_t soa; 79 uint32_t version; 80 /* key in entries is 'mhash', not domain name! */ 81 isc_ht_t *entries; 82 /* key in coos is domain name */ 83 isc_ht_t *coos; 84 85 /* 86 * defoptions are taken from named.conf 87 * zoneoptions are global options from zone 88 */ 89 dns_catz_options_t defoptions; 90 dns_catz_options_t zoneoptions; 91 isc_time_t lastupdated; 92 93 bool updatepending; /* there is an update pending */ 94 bool updaterunning; /* there is an update running */ 95 isc_result_t updateresult; /* result from the offloaded work */ 96 dns_db_t *db; /* zones database */ 97 dns_dbversion_t *dbversion; /* version we will be updating to */ 98 dns_db_t *updb; /* zones database we're working on */ 99 dns_dbversion_t *updbversion; /* version we're working on */ 100 101 isc_timer_t *updatetimer; 102 103 bool active; 104 bool broken; 105 106 isc_refcount_t references; 107 isc_mutex_t lock; 108 }; 109 110 static void 111 dns__catz_timer_cb(void *); 112 static void 113 dns__catz_timer_start(dns_catz_zone_t *catz); 114 static void 115 dns__catz_timer_stop(void *arg); 116 117 static void 118 dns__catz_update_cb(void *data); 119 static void 120 dns__catz_done_cb(void *data); 121 122 static isc_result_t 123 catz_process_zones_entry(dns_catz_zone_t *catz, dns_rdataset_t *value, 124 dns_label_t *mhash); 125 static isc_result_t 126 catz_process_zones_suboption(dns_catz_zone_t *catz, dns_rdataset_t *value, 127 dns_label_t *mhash, dns_name_t *name); 128 static void 129 catz_entry_add_or_mod(dns_catz_zone_t *catz, isc_ht_t *ht, unsigned char *key, 130 size_t keysize, dns_catz_entry_t *nentry, 131 dns_catz_entry_t *oentry, const char *msg, 132 const char *zname, const char *czname); 133 134 /*% 135 * Collection of catalog zones for a view 136 */ 137 struct dns_catz_zones { 138 unsigned int magic; 139 isc_ht_t *zones; 140 isc_mem_t *mctx; 141 isc_refcount_t references; 142 isc_mutex_t lock; 143 dns_catz_zonemodmethods_t *zmm; 144 isc_loopmgr_t *loopmgr; 145 dns_view_t *view; 146 atomic_bool shuttingdown; 147 }; 148 149 void 150 dns_catz_options_init(dns_catz_options_t *options) { 151 REQUIRE(options != NULL); 152 153 dns_ipkeylist_init(&options->masters); 154 155 options->allow_query = NULL; 156 options->allow_transfer = NULL; 157 158 options->allow_query = NULL; 159 options->allow_transfer = NULL; 160 161 options->in_memory = false; 162 options->min_update_interval = 5; 163 options->zonedir = NULL; 164 } 165 166 void 167 dns_catz_options_free(dns_catz_options_t *options, isc_mem_t *mctx) { 168 REQUIRE(options != NULL); 169 REQUIRE(mctx != NULL); 170 171 if (options->masters.count != 0) { 172 dns_ipkeylist_clear(mctx, &options->masters); 173 } 174 if (options->zonedir != NULL) { 175 isc_mem_free(mctx, options->zonedir); 176 options->zonedir = NULL; 177 } 178 if (options->allow_query != NULL) { 179 isc_buffer_free(&options->allow_query); 180 } 181 if (options->allow_transfer != NULL) { 182 isc_buffer_free(&options->allow_transfer); 183 } 184 } 185 186 void 187 dns_catz_options_copy(isc_mem_t *mctx, const dns_catz_options_t *src, 188 dns_catz_options_t *dst) { 189 REQUIRE(mctx != NULL); 190 REQUIRE(src != NULL); 191 REQUIRE(dst != NULL); 192 REQUIRE(dst->masters.count == 0); 193 REQUIRE(dst->allow_query == NULL); 194 REQUIRE(dst->allow_transfer == NULL); 195 196 if (src->masters.count != 0) { 197 dns_ipkeylist_copy(mctx, &src->masters, &dst->masters); 198 } 199 200 if (dst->zonedir != NULL) { 201 isc_mem_free(mctx, dst->zonedir); 202 dst->zonedir = NULL; 203 } 204 205 if (src->zonedir != NULL) { 206 dst->zonedir = isc_mem_strdup(mctx, src->zonedir); 207 } 208 209 if (src->allow_query != NULL) { 210 isc_buffer_dup(mctx, &dst->allow_query, src->allow_query); 211 } 212 213 if (src->allow_transfer != NULL) { 214 isc_buffer_dup(mctx, &dst->allow_transfer, src->allow_transfer); 215 } 216 } 217 218 void 219 dns_catz_options_setdefault(isc_mem_t *mctx, const dns_catz_options_t *defaults, 220 dns_catz_options_t *opts) { 221 REQUIRE(mctx != NULL); 222 REQUIRE(defaults != NULL); 223 REQUIRE(opts != NULL); 224 225 if (opts->masters.count == 0 && defaults->masters.count != 0) { 226 dns_ipkeylist_copy(mctx, &defaults->masters, &opts->masters); 227 } 228 229 if (defaults->zonedir != NULL) { 230 if (opts->zonedir != NULL) { 231 isc_mem_free(mctx, opts->zonedir); 232 } 233 opts->zonedir = isc_mem_strdup(mctx, defaults->zonedir); 234 } 235 236 if (opts->allow_query == NULL && defaults->allow_query != NULL) { 237 isc_buffer_dup(mctx, &opts->allow_query, defaults->allow_query); 238 } 239 if (opts->allow_transfer == NULL && defaults->allow_transfer != NULL) { 240 isc_buffer_dup(mctx, &opts->allow_transfer, 241 defaults->allow_transfer); 242 } 243 244 /* This option is always taken from config, so it's always 'default' */ 245 opts->in_memory = defaults->in_memory; 246 } 247 248 static dns_catz_coo_t * 249 catz_coo_new(isc_mem_t *mctx, const dns_name_t *domain) { 250 REQUIRE(mctx != NULL); 251 REQUIRE(domain != NULL); 252 253 dns_catz_coo_t *ncoo = isc_mem_get(mctx, sizeof(*ncoo)); 254 *ncoo = (dns_catz_coo_t){ 255 .magic = DNS_CATZ_COO_MAGIC, 256 }; 257 dns_name_init(&ncoo->name, NULL); 258 dns_name_dup(domain, mctx, &ncoo->name); 259 isc_refcount_init(&ncoo->references, 1); 260 261 return ncoo; 262 } 263 264 static void 265 catz_coo_detach(dns_catz_zone_t *catz, dns_catz_coo_t **coop) { 266 dns_catz_coo_t *coo; 267 268 REQUIRE(DNS_CATZ_ZONE_VALID(catz)); 269 REQUIRE(coop != NULL && DNS_CATZ_COO_VALID(*coop)); 270 coo = *coop; 271 *coop = NULL; 272 273 if (isc_refcount_decrement(&coo->references) == 1) { 274 isc_mem_t *mctx = catz->catzs->mctx; 275 coo->magic = 0; 276 isc_refcount_destroy(&coo->references); 277 if (dns_name_dynamic(&coo->name)) { 278 dns_name_free(&coo->name, mctx); 279 } 280 isc_mem_put(mctx, coo, sizeof(*coo)); 281 } 282 } 283 284 static void 285 catz_coo_add(dns_catz_zone_t *catz, dns_catz_entry_t *entry, 286 const dns_name_t *domain) { 287 REQUIRE(DNS_CATZ_ZONE_VALID(catz)); 288 REQUIRE(DNS_CATZ_ENTRY_VALID(entry)); 289 REQUIRE(domain != NULL); 290 291 /* We are write locked, so the add must succeed if not found */ 292 dns_catz_coo_t *coo = NULL; 293 isc_result_t result = isc_ht_find(catz->coos, entry->name.ndata, 294 entry->name.length, (void **)&coo); 295 if (result != ISC_R_SUCCESS) { 296 coo = catz_coo_new(catz->catzs->mctx, domain); 297 result = isc_ht_add(catz->coos, entry->name.ndata, 298 entry->name.length, coo); 299 } 300 INSIST(result == ISC_R_SUCCESS); 301 } 302 303 dns_catz_entry_t * 304 dns_catz_entry_new(isc_mem_t *mctx, const dns_name_t *domain) { 305 REQUIRE(mctx != NULL); 306 307 dns_catz_entry_t *nentry = isc_mem_get(mctx, sizeof(*nentry)); 308 *nentry = (dns_catz_entry_t){ 309 .magic = DNS_CATZ_ENTRY_MAGIC, 310 }; 311 312 dns_name_init(&nentry->name, NULL); 313 if (domain != NULL) { 314 dns_name_dup(domain, mctx, &nentry->name); 315 } 316 317 dns_catz_options_init(&nentry->opts); 318 isc_refcount_init(&nentry->references, 1); 319 320 return nentry; 321 } 322 323 dns_name_t * 324 dns_catz_entry_getname(dns_catz_entry_t *entry) { 325 REQUIRE(DNS_CATZ_ENTRY_VALID(entry)); 326 return &entry->name; 327 } 328 329 dns_catz_entry_t * 330 dns_catz_entry_copy(dns_catz_zone_t *catz, const dns_catz_entry_t *entry) { 331 REQUIRE(DNS_CATZ_ZONE_VALID(catz)); 332 REQUIRE(DNS_CATZ_ENTRY_VALID(entry)); 333 334 dns_catz_entry_t *nentry = dns_catz_entry_new(catz->catzs->mctx, 335 &entry->name); 336 337 dns_catz_options_copy(catz->catzs->mctx, &entry->opts, &nentry->opts); 338 339 return nentry; 340 } 341 342 void 343 dns_catz_entry_attach(dns_catz_entry_t *entry, dns_catz_entry_t **entryp) { 344 REQUIRE(DNS_CATZ_ENTRY_VALID(entry)); 345 REQUIRE(entryp != NULL && *entryp == NULL); 346 347 isc_refcount_increment(&entry->references); 348 *entryp = entry; 349 } 350 351 void 352 dns_catz_entry_detach(dns_catz_zone_t *catz, dns_catz_entry_t **entryp) { 353 dns_catz_entry_t *entry; 354 355 REQUIRE(DNS_CATZ_ZONE_VALID(catz)); 356 REQUIRE(entryp != NULL && DNS_CATZ_ENTRY_VALID(*entryp)); 357 entry = *entryp; 358 *entryp = NULL; 359 360 if (isc_refcount_decrement(&entry->references) == 1) { 361 isc_mem_t *mctx = catz->catzs->mctx; 362 entry->magic = 0; 363 isc_refcount_destroy(&entry->references); 364 dns_catz_options_free(&entry->opts, mctx); 365 if (dns_name_dynamic(&entry->name)) { 366 dns_name_free(&entry->name, mctx); 367 } 368 isc_mem_put(mctx, entry, sizeof(*entry)); 369 } 370 } 371 372 bool 373 dns_catz_entry_validate(const dns_catz_entry_t *entry) { 374 REQUIRE(DNS_CATZ_ENTRY_VALID(entry)); 375 UNUSED(entry); 376 377 return true; 378 } 379 380 bool 381 dns_catz_entry_cmp(const dns_catz_entry_t *ea, const dns_catz_entry_t *eb) { 382 isc_region_t ra, rb; 383 384 REQUIRE(DNS_CATZ_ENTRY_VALID(ea)); 385 REQUIRE(DNS_CATZ_ENTRY_VALID(eb)); 386 387 if (ea == eb) { 388 return true; 389 } 390 391 if (ea->opts.masters.count != eb->opts.masters.count) { 392 return false; 393 } 394 395 if (memcmp(ea->opts.masters.addrs, eb->opts.masters.addrs, 396 ea->opts.masters.count * sizeof(isc_sockaddr_t))) 397 { 398 return false; 399 } 400 401 for (size_t i = 0; i < eb->opts.masters.count; i++) { 402 if ((ea->opts.masters.keys[i] == NULL) != 403 (eb->opts.masters.keys[i] == NULL)) 404 { 405 return false; 406 } 407 if (ea->opts.masters.keys[i] == NULL) { 408 continue; 409 } 410 if (!dns_name_equal(ea->opts.masters.keys[i], 411 eb->opts.masters.keys[i])) 412 { 413 return false; 414 } 415 } 416 417 for (size_t i = 0; i < eb->opts.masters.count; i++) { 418 if ((ea->opts.masters.tlss[i] == NULL) != 419 (eb->opts.masters.tlss[i] == NULL)) 420 { 421 return false; 422 } 423 if (ea->opts.masters.tlss[i] == NULL) { 424 continue; 425 } 426 if (!dns_name_equal(ea->opts.masters.tlss[i], 427 eb->opts.masters.tlss[i])) 428 { 429 return false; 430 } 431 } 432 433 /* If one is NULL and the other isn't, the entries don't match */ 434 if ((ea->opts.allow_query == NULL) != (eb->opts.allow_query == NULL)) { 435 return false; 436 } 437 438 /* If one is non-NULL, then they both are */ 439 if (ea->opts.allow_query != NULL) { 440 isc_buffer_usedregion(ea->opts.allow_query, &ra); 441 isc_buffer_usedregion(eb->opts.allow_query, &rb); 442 if (isc_region_compare(&ra, &rb)) { 443 return false; 444 } 445 } 446 447 /* Repeat the above checks with allow_transfer */ 448 if ((ea->opts.allow_transfer == NULL) != 449 (eb->opts.allow_transfer == NULL)) 450 { 451 return false; 452 } 453 454 if (ea->opts.allow_transfer != NULL) { 455 isc_buffer_usedregion(ea->opts.allow_transfer, &ra); 456 isc_buffer_usedregion(eb->opts.allow_transfer, &rb); 457 if (isc_region_compare(&ra, &rb)) { 458 return false; 459 } 460 } 461 462 return true; 463 } 464 465 dns_name_t * 466 dns_catz_zone_getname(dns_catz_zone_t *catz) { 467 REQUIRE(DNS_CATZ_ZONE_VALID(catz)); 468 469 return &catz->name; 470 } 471 472 dns_catz_options_t * 473 dns_catz_zone_getdefoptions(dns_catz_zone_t *catz) { 474 REQUIRE(DNS_CATZ_ZONE_VALID(catz)); 475 476 return &catz->defoptions; 477 } 478 479 void 480 dns_catz_zone_resetdefoptions(dns_catz_zone_t *catz) { 481 REQUIRE(DNS_CATZ_ZONE_VALID(catz)); 482 483 dns_catz_options_free(&catz->defoptions, catz->catzs->mctx); 484 dns_catz_options_init(&catz->defoptions); 485 } 486 487 /*%< 488 * Merge 'newcatz' into 'catz', calling addzone/delzone/modzone 489 * (from catz->catzs->zmm) for appropriate member zones. 490 * 491 * Requires: 492 * \li 'catz' is a valid dns_catz_zone_t. 493 * \li 'newcatz' is a valid dns_catz_zone_t. 494 * 495 */ 496 static isc_result_t 497 dns__catz_zones_merge(dns_catz_zone_t *catz, dns_catz_zone_t *newcatz) { 498 isc_result_t result; 499 isc_ht_iter_t *iter1 = NULL, *iter2 = NULL; 500 isc_ht_iter_t *iteradd = NULL, *itermod = NULL; 501 isc_ht_t *toadd = NULL, *tomod = NULL; 502 bool delcur = false; 503 char czname[DNS_NAME_FORMATSIZE]; 504 char zname[DNS_NAME_FORMATSIZE]; 505 dns_catz_zoneop_fn_t addzone, modzone, delzone; 506 507 REQUIRE(DNS_CATZ_ZONE_VALID(catz)); 508 REQUIRE(DNS_CATZ_ZONE_VALID(newcatz)); 509 510 LOCK(&catz->lock); 511 512 /* TODO verify the new zone first! */ 513 514 addzone = catz->catzs->zmm->addzone; 515 modzone = catz->catzs->zmm->modzone; 516 delzone = catz->catzs->zmm->delzone; 517 518 /* Copy zoneoptions from newcatz into catz. */ 519 520 dns_catz_options_free(&catz->zoneoptions, catz->catzs->mctx); 521 dns_catz_options_copy(catz->catzs->mctx, &newcatz->zoneoptions, 522 &catz->zoneoptions); 523 dns_catz_options_setdefault(catz->catzs->mctx, &catz->defoptions, 524 &catz->zoneoptions); 525 526 dns_name_format(&catz->name, czname, DNS_NAME_FORMATSIZE); 527 528 isc_ht_init(&toadd, catz->catzs->mctx, 1, ISC_HT_CASE_INSENSITIVE); 529 isc_ht_init(&tomod, catz->catzs->mctx, 1, ISC_HT_CASE_INSENSITIVE); 530 isc_ht_iter_create(newcatz->entries, &iter1); 531 isc_ht_iter_create(catz->entries, &iter2); 532 533 /* 534 * We can create those iterators now, even though toadd and tomod are 535 * empty 536 */ 537 isc_ht_iter_create(toadd, &iteradd); 538 isc_ht_iter_create(tomod, &itermod); 539 540 /* 541 * First - walk the new zone and find all nodes that are not in the 542 * old zone, or are in both zones and are modified. 543 */ 544 for (result = isc_ht_iter_first(iter1); result == ISC_R_SUCCESS; 545 result = delcur ? isc_ht_iter_delcurrent_next(iter1) 546 : isc_ht_iter_next(iter1)) 547 { 548 isc_result_t find_result; 549 dns_catz_zone_t *parentcatz = NULL; 550 dns_catz_entry_t *nentry = NULL; 551 dns_catz_entry_t *oentry = NULL; 552 dns_zone_t *zone = NULL; 553 unsigned char *key = NULL; 554 size_t keysize; 555 delcur = false; 556 557 isc_ht_iter_current(iter1, (void **)&nentry); 558 isc_ht_iter_currentkey(iter1, &key, &keysize); 559 560 /* 561 * Spurious record that came from suboption without main 562 * record, removed. 563 * xxxwpk: make it a separate verification phase? 564 */ 565 if (dns_name_countlabels(&nentry->name) == 0) { 566 dns_catz_entry_detach(newcatz, &nentry); 567 delcur = true; 568 continue; 569 } 570 571 dns_name_format(&nentry->name, zname, DNS_NAME_FORMATSIZE); 572 573 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 574 DNS_LOGMODULE_MASTER, ISC_LOG_DEBUG(3), 575 "catz: iterating over '%s' from catalog '%s'", 576 zname, czname); 577 dns_catz_options_setdefault(catz->catzs->mctx, 578 &catz->zoneoptions, &nentry->opts); 579 580 /* Try to find the zone in the view */ 581 find_result = dns_view_findzone(catz->catzs->view, 582 dns_catz_entry_getname(nentry), 583 DNS_ZTFIND_EXACT, &zone); 584 if (find_result == ISC_R_SUCCESS) { 585 dns_catz_coo_t *coo = NULL; 586 char pczname[DNS_NAME_FORMATSIZE]; 587 bool parentcatz_locked = false; 588 589 /* 590 * Change of ownership (coo) processing, if required 591 */ 592 parentcatz = dns_zone_get_parentcatz(zone); 593 if (parentcatz != NULL && parentcatz != catz) { 594 UNLOCK(&catz->lock); 595 LOCK(&parentcatz->lock); 596 parentcatz_locked = true; 597 } 598 if (parentcatz_locked && 599 isc_ht_find(parentcatz->coos, nentry->name.ndata, 600 nentry->name.length, 601 (void **)&coo) == ISC_R_SUCCESS && 602 dns_name_equal(&coo->name, &catz->name)) 603 { 604 dns_name_format(&parentcatz->name, pczname, 605 DNS_NAME_FORMATSIZE); 606 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 607 DNS_LOGMODULE_MASTER, 608 ISC_LOG_DEBUG(3), 609 "catz: zone '%s' " 610 "change of ownership from " 611 "'%s' to '%s'", 612 zname, pczname, czname); 613 result = delzone(nentry, parentcatz, 614 parentcatz->catzs->view, 615 parentcatz->catzs->zmm->udata); 616 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 617 DNS_LOGMODULE_MASTER, 618 ISC_LOG_INFO, 619 "catz: deleting zone '%s' " 620 "from catalog '%s' - %s", 621 zname, pczname, 622 isc_result_totext(result)); 623 } 624 if (parentcatz_locked) { 625 UNLOCK(&parentcatz->lock); 626 LOCK(&catz->lock); 627 } 628 dns_zone_detach(&zone); 629 } 630 631 /* Try to find the zone in the old catalog zone */ 632 result = isc_ht_find(catz->entries, key, (uint32_t)keysize, 633 (void **)&oentry); 634 if (result != ISC_R_SUCCESS) { 635 if (find_result == ISC_R_SUCCESS && parentcatz == catz) 636 { 637 /* 638 * This means that the zone's unique label 639 * has been changed, in that case we must 640 * reset the zone's internal state by removing 641 * and re-adding it. 642 * 643 * Scheduling the addition now, the removal will 644 * be scheduled below, when walking the old 645 * zone for remaining entries, and then we will 646 * perform deletions earlier than additions and 647 * modifications. 648 */ 649 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 650 DNS_LOGMODULE_MASTER, 651 ISC_LOG_INFO, 652 "catz: zone '%s' unique label " 653 "has changed, reset state", 654 zname); 655 } 656 657 catz_entry_add_or_mod(catz, toadd, key, keysize, nentry, 658 NULL, "adding", zname, czname); 659 continue; 660 } 661 662 if (find_result != ISC_R_SUCCESS) { 663 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 664 DNS_LOGMODULE_MASTER, ISC_LOG_DEBUG(3), 665 "catz: zone '%s' was expected to exist " 666 "but can not be found, will be restored", 667 zname); 668 catz_entry_add_or_mod(catz, toadd, key, keysize, nentry, 669 oentry, "adding", zname, czname); 670 continue; 671 } 672 673 if (dns_catz_entry_cmp(oentry, nentry) != true) { 674 catz_entry_add_or_mod(catz, tomod, key, keysize, nentry, 675 oentry, "modifying", zname, 676 czname); 677 continue; 678 } 679 680 /* 681 * Delete the old entry so that it won't accidentally be 682 * removed as a non-existing entry below. 683 */ 684 dns_catz_entry_detach(catz, &oentry); 685 result = isc_ht_delete(catz->entries, key, (uint32_t)keysize); 686 RUNTIME_CHECK(result == ISC_R_SUCCESS); 687 } 688 RUNTIME_CHECK(result == ISC_R_NOMORE); 689 isc_ht_iter_destroy(&iter1); 690 691 /* 692 * Then - walk the old zone; only deleted entries should remain. 693 */ 694 for (result = isc_ht_iter_first(iter2); result == ISC_R_SUCCESS; 695 result = isc_ht_iter_delcurrent_next(iter2)) 696 { 697 dns_catz_entry_t *entry = NULL; 698 isc_ht_iter_current(iter2, (void **)&entry); 699 700 dns_name_format(&entry->name, zname, DNS_NAME_FORMATSIZE); 701 result = delzone(entry, catz, catz->catzs->view, 702 catz->catzs->zmm->udata); 703 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 704 DNS_LOGMODULE_MASTER, ISC_LOG_INFO, 705 "catz: deleting zone '%s' from catalog '%s' - %s", 706 zname, czname, isc_result_totext(result)); 707 dns_catz_entry_detach(catz, &entry); 708 } 709 RUNTIME_CHECK(result == ISC_R_NOMORE); 710 isc_ht_iter_destroy(&iter2); 711 /* At this moment catz->entries has to be empty. */ 712 INSIST(isc_ht_count(catz->entries) == 0); 713 isc_ht_destroy(&catz->entries); 714 715 for (result = isc_ht_iter_first(iteradd); result == ISC_R_SUCCESS; 716 result = isc_ht_iter_delcurrent_next(iteradd)) 717 { 718 dns_catz_entry_t *entry = NULL; 719 isc_ht_iter_current(iteradd, (void **)&entry); 720 721 dns_name_format(&entry->name, zname, DNS_NAME_FORMATSIZE); 722 result = addzone(entry, catz, catz->catzs->view, 723 catz->catzs->zmm->udata); 724 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 725 DNS_LOGMODULE_MASTER, ISC_LOG_INFO, 726 "catz: adding zone '%s' from catalog " 727 "'%s' - %s", 728 zname, czname, isc_result_totext(result)); 729 } 730 731 for (result = isc_ht_iter_first(itermod); result == ISC_R_SUCCESS; 732 result = isc_ht_iter_delcurrent_next(itermod)) 733 { 734 dns_catz_entry_t *entry = NULL; 735 isc_ht_iter_current(itermod, (void **)&entry); 736 737 dns_name_format(&entry->name, zname, DNS_NAME_FORMATSIZE); 738 result = modzone(entry, catz, catz->catzs->view, 739 catz->catzs->zmm->udata); 740 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 741 DNS_LOGMODULE_MASTER, ISC_LOG_INFO, 742 "catz: modifying zone '%s' from catalog " 743 "'%s' - %s", 744 zname, czname, isc_result_totext(result)); 745 } 746 747 catz->entries = newcatz->entries; 748 newcatz->entries = NULL; 749 750 /* 751 * We do not need to merge old coo (change of ownership) permission 752 * records with the new ones, just replace them. 753 */ 754 if (catz->coos != NULL && newcatz->coos != NULL) { 755 isc_ht_iter_t *iter = NULL; 756 757 isc_ht_iter_create(catz->coos, &iter); 758 for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS; 759 result = isc_ht_iter_delcurrent_next(iter)) 760 { 761 dns_catz_coo_t *coo = NULL; 762 763 isc_ht_iter_current(iter, (void **)&coo); 764 catz_coo_detach(catz, &coo); 765 } 766 INSIST(result == ISC_R_NOMORE); 767 isc_ht_iter_destroy(&iter); 768 769 /* The hashtable has to be empty now. */ 770 INSIST(isc_ht_count(catz->coos) == 0); 771 isc_ht_destroy(&catz->coos); 772 773 catz->coos = newcatz->coos; 774 newcatz->coos = NULL; 775 } 776 777 result = ISC_R_SUCCESS; 778 779 isc_ht_iter_destroy(&iteradd); 780 isc_ht_iter_destroy(&itermod); 781 isc_ht_destroy(&toadd); 782 isc_ht_destroy(&tomod); 783 784 UNLOCK(&catz->lock); 785 786 return result; 787 } 788 789 dns_catz_zones_t * 790 dns_catz_zones_new(isc_mem_t *mctx, isc_loopmgr_t *loopmgr, 791 dns_catz_zonemodmethods_t *zmm) { 792 REQUIRE(mctx != NULL); 793 REQUIRE(loopmgr != NULL); 794 REQUIRE(zmm != NULL); 795 796 dns_catz_zones_t *catzs = isc_mem_get(mctx, sizeof(*catzs)); 797 *catzs = (dns_catz_zones_t){ .loopmgr = loopmgr, 798 .zmm = zmm, 799 .magic = DNS_CATZ_ZONES_MAGIC }; 800 801 isc_mutex_init(&catzs->lock); 802 isc_refcount_init(&catzs->references, 1); 803 isc_ht_init(&catzs->zones, mctx, 4, ISC_HT_CASE_INSENSITIVE); 804 isc_mem_attach(mctx, &catzs->mctx); 805 806 return catzs; 807 } 808 809 void * 810 dns_catz_zones_get_udata(dns_catz_zones_t *catzs) { 811 REQUIRE(DNS_CATZ_ZONES_VALID(catzs)); 812 813 return catzs->zmm->udata; 814 } 815 816 void 817 dns_catz_catzs_set_view(dns_catz_zones_t *catzs, dns_view_t *view) { 818 REQUIRE(DNS_CATZ_ZONES_VALID(catzs)); 819 REQUIRE(DNS_VIEW_VALID(view)); 820 /* Either it's a new one or it's being reconfigured. */ 821 REQUIRE(catzs->view == NULL || !strcmp(catzs->view->name, view->name)); 822 823 if (catzs->view == NULL) { 824 dns_view_weakattach(view, &catzs->view); 825 } else if (catzs->view != view) { 826 dns_view_weakdetach(&catzs->view); 827 dns_view_weakattach(view, &catzs->view); 828 } 829 } 830 831 dns_catz_zone_t * 832 dns_catz_zone_new(dns_catz_zones_t *catzs, const dns_name_t *name) { 833 REQUIRE(DNS_CATZ_ZONES_VALID(catzs)); 834 REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC)); 835 836 dns_catz_zone_t *catz = isc_mem_get(catzs->mctx, sizeof(*catz)); 837 *catz = (dns_catz_zone_t){ .active = true, 838 .version = DNS_CATZ_VERSION_UNDEFINED, 839 .magic = DNS_CATZ_ZONE_MAGIC }; 840 841 dns_catz_zones_attach(catzs, &catz->catzs); 842 isc_mutex_init(&catz->lock); 843 isc_refcount_init(&catz->references, 1); 844 isc_ht_init(&catz->entries, catzs->mctx, 4, ISC_HT_CASE_INSENSITIVE); 845 isc_ht_init(&catz->coos, catzs->mctx, 4, ISC_HT_CASE_INSENSITIVE); 846 isc_time_settoepoch(&catz->lastupdated); 847 dns_catz_options_init(&catz->defoptions); 848 dns_catz_options_init(&catz->zoneoptions); 849 dns_name_init(&catz->name, NULL); 850 dns_name_dup(name, catzs->mctx, &catz->name); 851 852 return catz; 853 } 854 855 static void 856 dns__catz_timer_start(dns_catz_zone_t *catz) { 857 uint64_t tdiff; 858 isc_interval_t interval; 859 isc_time_t now; 860 861 REQUIRE(DNS_CATZ_ZONE_VALID(catz)); 862 863 now = isc_time_now(); 864 tdiff = isc_time_microdiff(&now, &catz->lastupdated) / 1000000; 865 if (tdiff < catz->defoptions.min_update_interval) { 866 uint64_t defer = catz->defoptions.min_update_interval - tdiff; 867 char dname[DNS_NAME_FORMATSIZE]; 868 869 dns_name_format(&catz->name, dname, DNS_NAME_FORMATSIZE); 870 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 871 DNS_LOGMODULE_MASTER, ISC_LOG_INFO, 872 "catz: %s: new zone version came " 873 "too soon, deferring update for " 874 "%" PRIu64 " seconds", 875 dname, defer); 876 isc_interval_set(&interval, (unsigned int)defer, 0); 877 } else { 878 isc_interval_set(&interval, 0, 0); 879 } 880 881 catz->loop = isc_loop(); 882 883 isc_timer_create(catz->loop, dns__catz_timer_cb, catz, 884 &catz->updatetimer); 885 isc_timer_start(catz->updatetimer, isc_timertype_once, &interval); 886 } 887 888 static void 889 dns__catz_timer_stop(void *arg) { 890 dns_catz_zone_t *catz = arg; 891 REQUIRE(DNS_CATZ_ZONE_VALID(catz)); 892 893 isc_timer_stop(catz->updatetimer); 894 isc_timer_destroy(&catz->updatetimer); 895 catz->loop = NULL; 896 897 dns_catz_zone_detach(&catz); 898 } 899 900 isc_result_t 901 dns_catz_zone_add(dns_catz_zones_t *catzs, const dns_name_t *name, 902 dns_catz_zone_t **catzp) { 903 REQUIRE(DNS_CATZ_ZONES_VALID(catzs)); 904 REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC)); 905 REQUIRE(catzp != NULL && *catzp == NULL); 906 907 dns_catz_zone_t *catz = NULL; 908 isc_result_t result; 909 char zname[DNS_NAME_FORMATSIZE]; 910 911 dns_name_format(name, zname, DNS_NAME_FORMATSIZE); 912 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER, 913 ISC_LOG_DEBUG(3), "catz: dns_catz_zone_add %s", zname); 914 915 LOCK(&catzs->lock); 916 917 /* 918 * This function is called only during a (re)configuration, while 919 * 'catzs->zones' can become NULL only during shutdown. 920 */ 921 INSIST(catzs->zones != NULL); 922 INSIST(!atomic_load(&catzs->shuttingdown)); 923 924 result = isc_ht_find(catzs->zones, name->ndata, name->length, 925 (void **)&catz); 926 switch (result) { 927 case ISC_R_SUCCESS: 928 INSIST(!catz->active); 929 catz->active = true; 930 result = ISC_R_EXISTS; 931 break; 932 case ISC_R_NOTFOUND: 933 catz = dns_catz_zone_new(catzs, name); 934 935 result = isc_ht_add(catzs->zones, catz->name.ndata, 936 catz->name.length, catz); 937 INSIST(result == ISC_R_SUCCESS); 938 break; 939 default: 940 UNREACHABLE(); 941 } 942 943 UNLOCK(&catzs->lock); 944 945 *catzp = catz; 946 947 return result; 948 } 949 950 dns_catz_zone_t * 951 dns_catz_zone_get(dns_catz_zones_t *catzs, const dns_name_t *name) { 952 isc_result_t result; 953 dns_catz_zone_t *found = NULL; 954 955 REQUIRE(DNS_CATZ_ZONES_VALID(catzs)); 956 REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC)); 957 958 LOCK(&catzs->lock); 959 if (catzs->zones == NULL) { 960 UNLOCK(&catzs->lock); 961 return NULL; 962 } 963 result = isc_ht_find(catzs->zones, name->ndata, name->length, 964 (void **)&found); 965 UNLOCK(&catzs->lock); 966 if (result != ISC_R_SUCCESS) { 967 return NULL; 968 } 969 970 return found; 971 } 972 973 static void 974 dns__catz_zone_shutdown(dns_catz_zone_t *catz) { 975 /* lock must be locked */ 976 if (catz->updatetimer != NULL) { 977 /* Don't wait for timer to trigger for shutdown */ 978 INSIST(catz->loop != NULL); 979 980 isc_async_run(catz->loop, dns__catz_timer_stop, catz); 981 } else { 982 dns_catz_zone_detach(&catz); 983 } 984 } 985 986 static void 987 dns__catz_zone_destroy(dns_catz_zone_t *catz) { 988 isc_mem_t *mctx = catz->catzs->mctx; 989 990 if (catz->entries != NULL) { 991 isc_ht_iter_t *iter = NULL; 992 isc_result_t result; 993 isc_ht_iter_create(catz->entries, &iter); 994 for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS; 995 result = isc_ht_iter_delcurrent_next(iter)) 996 { 997 dns_catz_entry_t *entry = NULL; 998 999 isc_ht_iter_current(iter, (void **)&entry); 1000 dns_catz_entry_detach(catz, &entry); 1001 } 1002 INSIST(result == ISC_R_NOMORE); 1003 isc_ht_iter_destroy(&iter); 1004 1005 /* The hashtable has to be empty now. */ 1006 INSIST(isc_ht_count(catz->entries) == 0); 1007 isc_ht_destroy(&catz->entries); 1008 } 1009 if (catz->coos != NULL) { 1010 isc_ht_iter_t *iter = NULL; 1011 isc_result_t result; 1012 isc_ht_iter_create(catz->coos, &iter); 1013 for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS; 1014 result = isc_ht_iter_delcurrent_next(iter)) 1015 { 1016 dns_catz_coo_t *coo = NULL; 1017 1018 isc_ht_iter_current(iter, (void **)&coo); 1019 catz_coo_detach(catz, &coo); 1020 } 1021 INSIST(result == ISC_R_NOMORE); 1022 isc_ht_iter_destroy(&iter); 1023 1024 /* The hashtable has to be empty now. */ 1025 INSIST(isc_ht_count(catz->coos) == 0); 1026 isc_ht_destroy(&catz->coos); 1027 } 1028 catz->magic = 0; 1029 isc_mutex_destroy(&catz->lock); 1030 1031 if (catz->updatetimer != NULL) { 1032 isc_timer_async_destroy(&catz->updatetimer); 1033 } 1034 1035 if (catz->db != NULL) { 1036 if (catz->dbversion != NULL) { 1037 dns_db_closeversion(catz->db, &catz->dbversion, false); 1038 } 1039 dns_db_updatenotify_unregister( 1040 catz->db, dns_catz_dbupdate_callback, catz->catzs); 1041 dns_db_detach(&catz->db); 1042 } 1043 1044 INSIST(!catz->updaterunning); 1045 1046 dns_name_free(&catz->name, mctx); 1047 dns_catz_options_free(&catz->defoptions, mctx); 1048 dns_catz_options_free(&catz->zoneoptions, mctx); 1049 1050 dns_catz_zones_detach(&catz->catzs); 1051 1052 isc_mem_put(mctx, catz, sizeof(*catz)); 1053 } 1054 1055 static void 1056 dns__catz_zones_destroy(dns_catz_zones_t *catzs) { 1057 REQUIRE(atomic_load(&catzs->shuttingdown)); 1058 REQUIRE(catzs->zones == NULL); 1059 1060 catzs->magic = 0; 1061 isc_mutex_destroy(&catzs->lock); 1062 if (catzs->view != NULL) { 1063 dns_view_weakdetach(&catzs->view); 1064 } 1065 isc_mem_putanddetach(&catzs->mctx, catzs, sizeof(*catzs)); 1066 } 1067 1068 void 1069 dns_catz_zones_shutdown(dns_catz_zones_t *catzs) { 1070 REQUIRE(DNS_CATZ_ZONES_VALID(catzs)); 1071 1072 if (!atomic_compare_exchange_strong(&catzs->shuttingdown, 1073 &(bool){ false }, true)) 1074 { 1075 return; 1076 } 1077 1078 LOCK(&catzs->lock); 1079 if (catzs->zones != NULL) { 1080 isc_ht_iter_t *iter = NULL; 1081 isc_result_t result; 1082 isc_ht_iter_create(catzs->zones, &iter); 1083 for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;) 1084 { 1085 dns_catz_zone_t *catz = NULL; 1086 isc_ht_iter_current(iter, (void **)&catz); 1087 result = isc_ht_iter_delcurrent_next(iter); 1088 dns__catz_zone_shutdown(catz); 1089 } 1090 INSIST(result == ISC_R_NOMORE); 1091 isc_ht_iter_destroy(&iter); 1092 INSIST(isc_ht_count(catzs->zones) == 0); 1093 isc_ht_destroy(&catzs->zones); 1094 } 1095 UNLOCK(&catzs->lock); 1096 } 1097 1098 #ifdef DNS_CATZ_TRACE 1099 ISC_REFCOUNT_TRACE_IMPL(dns_catz_zone, dns__catz_zone_destroy); 1100 ISC_REFCOUNT_TRACE_IMPL(dns_catz_zones, dns__catz_zones_destroy); 1101 #else 1102 ISC_REFCOUNT_IMPL(dns_catz_zone, dns__catz_zone_destroy); 1103 ISC_REFCOUNT_IMPL(dns_catz_zones, dns__catz_zones_destroy); 1104 #endif 1105 1106 typedef enum { 1107 CATZ_OPT_NONE, 1108 CATZ_OPT_ZONES, 1109 CATZ_OPT_COO, 1110 CATZ_OPT_VERSION, 1111 CATZ_OPT_CUSTOM_START, /* CATZ custom properties must go below this */ 1112 CATZ_OPT_EXT, 1113 CATZ_OPT_PRIMARIES, 1114 CATZ_OPT_ALLOW_QUERY, 1115 CATZ_OPT_ALLOW_TRANSFER, 1116 } catz_opt_t; 1117 1118 static bool 1119 catz_opt_cmp(const dns_label_t *option, const char *opt) { 1120 size_t len = strlen(opt); 1121 1122 if (option->length - 1 == len && 1123 memcmp(opt, option->base + 1, len) == 0) 1124 { 1125 return true; 1126 } else { 1127 return false; 1128 } 1129 } 1130 1131 static catz_opt_t 1132 catz_get_option(const dns_label_t *option) { 1133 if (catz_opt_cmp(option, "ext")) { 1134 return CATZ_OPT_EXT; 1135 } else if (catz_opt_cmp(option, "zones")) { 1136 return CATZ_OPT_ZONES; 1137 } else if (catz_opt_cmp(option, "masters") || 1138 catz_opt_cmp(option, "primaries")) 1139 { 1140 return CATZ_OPT_PRIMARIES; 1141 } else if (catz_opt_cmp(option, "allow-query")) { 1142 return CATZ_OPT_ALLOW_QUERY; 1143 } else if (catz_opt_cmp(option, "allow-transfer")) { 1144 return CATZ_OPT_ALLOW_TRANSFER; 1145 } else if (catz_opt_cmp(option, "coo")) { 1146 return CATZ_OPT_COO; 1147 } else if (catz_opt_cmp(option, "version")) { 1148 return CATZ_OPT_VERSION; 1149 } else { 1150 return CATZ_OPT_NONE; 1151 } 1152 } 1153 1154 static isc_result_t 1155 catz_process_zones(dns_catz_zone_t *catz, dns_rdataset_t *value, 1156 dns_name_t *name) { 1157 dns_label_t mhash; 1158 dns_name_t opt; 1159 1160 REQUIRE(DNS_CATZ_ZONE_VALID(catz)); 1161 REQUIRE(DNS_RDATASET_VALID(value)); 1162 REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC)); 1163 1164 if (name->labels == 0) { 1165 return ISC_R_FAILURE; 1166 } 1167 1168 dns_name_getlabel(name, name->labels - 1, &mhash); 1169 1170 if (name->labels == 1) { 1171 return catz_process_zones_entry(catz, value, &mhash); 1172 } else { 1173 dns_name_init(&opt, NULL); 1174 dns_name_split(name, 1, &opt, NULL); 1175 return catz_process_zones_suboption(catz, value, &mhash, &opt); 1176 } 1177 } 1178 1179 static isc_result_t 1180 catz_process_coo(dns_catz_zone_t *catz, dns_label_t *mhash, 1181 dns_rdataset_t *value) { 1182 isc_result_t result; 1183 dns_rdata_t rdata; 1184 dns_rdata_ptr_t ptr; 1185 dns_catz_entry_t *entry = NULL; 1186 1187 REQUIRE(DNS_CATZ_ZONE_VALID(catz)); 1188 REQUIRE(mhash != NULL); 1189 REQUIRE(DNS_RDATASET_VALID(value)); 1190 1191 /* Change of Ownership was introduced in version "2" of the schema. */ 1192 if (catz->version < 2) { 1193 return ISC_R_FAILURE; 1194 } 1195 1196 if (value->type != dns_rdatatype_ptr) { 1197 return ISC_R_FAILURE; 1198 } 1199 1200 if (dns_rdataset_count(value) != 1) { 1201 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 1202 DNS_LOGMODULE_MASTER, ISC_LOG_WARNING, 1203 "catz: 'coo' property PTR RRset contains " 1204 "more than one record, which is invalid"); 1205 catz->broken = true; 1206 return ISC_R_FAILURE; 1207 } 1208 1209 result = dns_rdataset_first(value); 1210 if (result != ISC_R_SUCCESS) { 1211 return result; 1212 } 1213 1214 dns_rdata_init(&rdata); 1215 dns_rdataset_current(value, &rdata); 1216 1217 result = dns_rdata_tostruct(&rdata, &ptr, NULL); 1218 if (result != ISC_R_SUCCESS) { 1219 return result; 1220 } 1221 1222 if (dns_name_countlabels(&ptr.ptr) == 0) { 1223 result = ISC_R_FAILURE; 1224 goto cleanup; 1225 } 1226 1227 result = isc_ht_find(catz->entries, mhash->base, mhash->length, 1228 (void **)&entry); 1229 if (result != ISC_R_SUCCESS) { 1230 /* The entry was not found .*/ 1231 goto cleanup; 1232 } 1233 1234 if (dns_name_countlabels(&entry->name) == 0) { 1235 result = ISC_R_FAILURE; 1236 goto cleanup; 1237 } 1238 1239 catz_coo_add(catz, entry, &ptr.ptr); 1240 1241 cleanup: 1242 dns_rdata_freestruct(&ptr); 1243 1244 return result; 1245 } 1246 1247 static isc_result_t 1248 catz_process_zones_entry(dns_catz_zone_t *catz, dns_rdataset_t *value, 1249 dns_label_t *mhash) { 1250 isc_result_t result; 1251 dns_rdata_t rdata; 1252 dns_rdata_ptr_t ptr; 1253 dns_catz_entry_t *entry = NULL; 1254 1255 if (value->type != dns_rdatatype_ptr) { 1256 return ISC_R_FAILURE; 1257 } 1258 1259 if (dns_rdataset_count(value) != 1) { 1260 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 1261 DNS_LOGMODULE_MASTER, ISC_LOG_WARNING, 1262 "catz: member zone PTR RRset contains " 1263 "more than one record, which is invalid"); 1264 catz->broken = true; 1265 return ISC_R_FAILURE; 1266 } 1267 1268 result = dns_rdataset_first(value); 1269 if (result != ISC_R_SUCCESS) { 1270 return result; 1271 } 1272 1273 dns_rdata_init(&rdata); 1274 dns_rdataset_current(value, &rdata); 1275 1276 result = dns_rdata_tostruct(&rdata, &ptr, NULL); 1277 if (result != ISC_R_SUCCESS) { 1278 return result; 1279 } 1280 1281 result = isc_ht_find(catz->entries, mhash->base, mhash->length, 1282 (void **)&entry); 1283 if (result == ISC_R_SUCCESS) { 1284 if (dns_name_countlabels(&entry->name) != 0) { 1285 /* We have a duplicate. */ 1286 dns_rdata_freestruct(&ptr); 1287 return ISC_R_FAILURE; 1288 } else { 1289 dns_name_dup(&ptr.ptr, catz->catzs->mctx, &entry->name); 1290 } 1291 } else { 1292 entry = dns_catz_entry_new(catz->catzs->mctx, &ptr.ptr); 1293 1294 result = isc_ht_add(catz->entries, mhash->base, mhash->length, 1295 entry); 1296 } 1297 INSIST(result == ISC_R_SUCCESS); 1298 1299 dns_rdata_freestruct(&ptr); 1300 1301 return ISC_R_SUCCESS; 1302 } 1303 1304 static isc_result_t 1305 catz_process_version(dns_catz_zone_t *catz, dns_rdataset_t *value) { 1306 isc_result_t result; 1307 dns_rdata_t rdata; 1308 dns_rdata_txt_t rdatatxt; 1309 dns_rdata_txt_string_t rdatastr; 1310 uint32_t tversion; 1311 char t[16]; 1312 1313 REQUIRE(DNS_CATZ_ZONE_VALID(catz)); 1314 REQUIRE(DNS_RDATASET_VALID(value)); 1315 1316 if (value->type != dns_rdatatype_txt) { 1317 return ISC_R_FAILURE; 1318 } 1319 1320 if (dns_rdataset_count(value) != 1) { 1321 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 1322 DNS_LOGMODULE_MASTER, ISC_LOG_WARNING, 1323 "catz: 'version' property TXT RRset contains " 1324 "more than one record, which is invalid"); 1325 catz->broken = true; 1326 return ISC_R_FAILURE; 1327 } 1328 1329 result = dns_rdataset_first(value); 1330 if (result != ISC_R_SUCCESS) { 1331 return result; 1332 } 1333 1334 dns_rdata_init(&rdata); 1335 dns_rdataset_current(value, &rdata); 1336 1337 result = dns_rdata_tostruct(&rdata, &rdatatxt, NULL); 1338 if (result != ISC_R_SUCCESS) { 1339 return result; 1340 } 1341 1342 result = dns_rdata_txt_first(&rdatatxt); 1343 if (result != ISC_R_SUCCESS) { 1344 goto cleanup; 1345 } 1346 1347 result = dns_rdata_txt_current(&rdatatxt, &rdatastr); 1348 if (result != ISC_R_SUCCESS) { 1349 goto cleanup; 1350 } 1351 1352 result = dns_rdata_txt_next(&rdatatxt); 1353 if (result != ISC_R_NOMORE) { 1354 result = ISC_R_FAILURE; 1355 goto cleanup; 1356 } 1357 if (rdatastr.length > 15) { 1358 result = ISC_R_BADNUMBER; 1359 goto cleanup; 1360 } 1361 memmove(t, rdatastr.data, rdatastr.length); 1362 t[rdatastr.length] = 0; 1363 result = isc_parse_uint32(&tversion, t, 10); 1364 if (result != ISC_R_SUCCESS) { 1365 goto cleanup; 1366 } 1367 catz->version = tversion; 1368 result = ISC_R_SUCCESS; 1369 1370 cleanup: 1371 dns_rdata_freestruct(&rdatatxt); 1372 if (result != ISC_R_SUCCESS) { 1373 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 1374 DNS_LOGMODULE_MASTER, ISC_LOG_WARNING, 1375 "catz: invalid record for the catalog " 1376 "zone version property"); 1377 catz->broken = true; 1378 } 1379 return result; 1380 } 1381 1382 static isc_result_t 1383 catz_process_primaries(dns_catz_zone_t *catz, dns_ipkeylist_t *ipkl, 1384 dns_rdataset_t *value, dns_name_t *name) { 1385 isc_result_t result; 1386 dns_rdata_t rdata; 1387 dns_rdata_in_a_t rdata_a; 1388 dns_rdata_in_aaaa_t rdata_aaaa; 1389 dns_rdata_txt_t rdata_txt; 1390 dns_rdata_txt_string_t rdatastr; 1391 dns_name_t *keyname = NULL; 1392 isc_mem_t *mctx; 1393 char keycbuf[DNS_NAME_FORMATSIZE]; 1394 isc_buffer_t keybuf; 1395 unsigned int rcount; 1396 1397 REQUIRE(DNS_CATZ_ZONE_VALID(catz)); 1398 REQUIRE(ipkl != NULL); 1399 REQUIRE(DNS_RDATASET_VALID(value)); 1400 REQUIRE(dns_rdataset_isassociated(value)); 1401 REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC)); 1402 1403 mctx = catz->catzs->mctx; 1404 memset(&rdata_a, 0, sizeof(rdata_a)); 1405 memset(&rdata_aaaa, 0, sizeof(rdata_aaaa)); 1406 memset(&rdata_txt, 0, sizeof(rdata_txt)); 1407 isc_buffer_init(&keybuf, keycbuf, sizeof(keycbuf)); 1408 1409 /* 1410 * We have three possibilities here: 1411 * - either empty name and IN A/IN AAAA record 1412 * - label and IN A/IN AAAA 1413 * - label and IN TXT - TSIG key name 1414 */ 1415 if (name->labels > 0) { 1416 isc_sockaddr_t sockaddr; 1417 size_t i; 1418 1419 /* 1420 * We're pre-preparing the data once, we'll put it into 1421 * the right spot in the primaries array once we find it. 1422 */ 1423 result = dns_rdataset_first(value); 1424 RUNTIME_CHECK(result == ISC_R_SUCCESS); 1425 dns_rdata_init(&rdata); 1426 dns_rdataset_current(value, &rdata); 1427 switch (value->type) { 1428 case dns_rdatatype_a: 1429 result = dns_rdata_tostruct(&rdata, &rdata_a, NULL); 1430 RUNTIME_CHECK(result == ISC_R_SUCCESS); 1431 isc_sockaddr_fromin(&sockaddr, &rdata_a.in_addr, 0); 1432 dns_rdata_freestruct(&rdata_a); 1433 break; 1434 case dns_rdatatype_aaaa: 1435 result = dns_rdata_tostruct(&rdata, &rdata_aaaa, NULL); 1436 RUNTIME_CHECK(result == ISC_R_SUCCESS); 1437 isc_sockaddr_fromin6(&sockaddr, &rdata_aaaa.in6_addr, 1438 0); 1439 dns_rdata_freestruct(&rdata_aaaa); 1440 break; 1441 case dns_rdatatype_txt: 1442 result = dns_rdata_tostruct(&rdata, &rdata_txt, NULL); 1443 RUNTIME_CHECK(result == ISC_R_SUCCESS); 1444 1445 result = dns_rdata_txt_first(&rdata_txt); 1446 if (result != ISC_R_SUCCESS) { 1447 dns_rdata_freestruct(&rdata_txt); 1448 return result; 1449 } 1450 1451 result = dns_rdata_txt_current(&rdata_txt, &rdatastr); 1452 if (result != ISC_R_SUCCESS) { 1453 dns_rdata_freestruct(&rdata_txt); 1454 return result; 1455 } 1456 1457 result = dns_rdata_txt_next(&rdata_txt); 1458 if (result != ISC_R_NOMORE) { 1459 dns_rdata_freestruct(&rdata_txt); 1460 return ISC_R_FAILURE; 1461 } 1462 1463 /* rdatastr.length < DNS_NAME_MAXTEXT */ 1464 keyname = isc_mem_get(mctx, sizeof(*keyname)); 1465 dns_name_init(keyname, 0); 1466 memmove(keycbuf, rdatastr.data, rdatastr.length); 1467 keycbuf[rdatastr.length] = 0; 1468 dns_rdata_freestruct(&rdata_txt); 1469 result = dns_name_fromstring(keyname, keycbuf, 1470 dns_rootname, 0, mctx); 1471 if (result != ISC_R_SUCCESS) { 1472 isc_mem_put(mctx, keyname, sizeof(*keyname)); 1473 return result; 1474 } 1475 break; 1476 default: 1477 return ISC_R_FAILURE; 1478 } 1479 1480 /* 1481 * We have to find the appropriate labeled record in 1482 * primaries if it exists. In the common case we'll 1483 * have no more than 3-4 records here, so no optimization. 1484 */ 1485 for (i = 0; i < ipkl->count; i++) { 1486 if (ipkl->labels[i] != NULL && 1487 !dns_name_compare(name, ipkl->labels[i])) 1488 { 1489 break; 1490 } 1491 } 1492 1493 if (i < ipkl->count) { /* we have this record already */ 1494 if (value->type == dns_rdatatype_txt) { 1495 if (ipkl->keys[i] != NULL) { 1496 if (dns_name_dynamic(ipkl->keys[i])) { 1497 dns_name_free(ipkl->keys[i], 1498 mctx); 1499 } 1500 isc_mem_put(mctx, ipkl->keys[i], 1501 sizeof(*ipkl->keys[i])); 1502 } 1503 ipkl->keys[i] = keyname; 1504 } else { /* A/AAAA */ 1505 memmove(&ipkl->addrs[i], &sockaddr, 1506 sizeof(sockaddr)); 1507 } 1508 } else { 1509 result = dns_ipkeylist_resize(mctx, ipkl, i + 1); 1510 if (result != ISC_R_SUCCESS) { 1511 return result; 1512 } 1513 1514 ipkl->labels[i] = isc_mem_get(mctx, 1515 sizeof(*ipkl->labels[0])); 1516 dns_name_init(ipkl->labels[i], NULL); 1517 dns_name_dup(name, mctx, ipkl->labels[i]); 1518 1519 if (value->type == dns_rdatatype_txt) { 1520 ipkl->keys[i] = keyname; 1521 } else { /* A/AAAA */ 1522 memmove(&ipkl->addrs[i], &sockaddr, 1523 sizeof(sockaddr)); 1524 } 1525 ipkl->count++; 1526 } 1527 return ISC_R_SUCCESS; 1528 } 1529 /* else - 'simple' case - without labels */ 1530 1531 if (value->type != dns_rdatatype_a && value->type != dns_rdatatype_aaaa) 1532 { 1533 return ISC_R_FAILURE; 1534 } 1535 1536 rcount = dns_rdataset_count(value) + ipkl->count; 1537 1538 result = dns_ipkeylist_resize(mctx, ipkl, rcount); 1539 if (result != ISC_R_SUCCESS) { 1540 return result; 1541 } 1542 1543 for (result = dns_rdataset_first(value); result == ISC_R_SUCCESS; 1544 result = dns_rdataset_next(value)) 1545 { 1546 dns_rdata_init(&rdata); 1547 dns_rdataset_current(value, &rdata); 1548 /* 1549 * port 0 == take the default 1550 */ 1551 if (value->type == dns_rdatatype_a) { 1552 result = dns_rdata_tostruct(&rdata, &rdata_a, NULL); 1553 RUNTIME_CHECK(result == ISC_R_SUCCESS); 1554 isc_sockaddr_fromin(&ipkl->addrs[ipkl->count], 1555 &rdata_a.in_addr, 0); 1556 dns_rdata_freestruct(&rdata_a); 1557 } else { 1558 result = dns_rdata_tostruct(&rdata, &rdata_aaaa, NULL); 1559 RUNTIME_CHECK(result == ISC_R_SUCCESS); 1560 isc_sockaddr_fromin6(&ipkl->addrs[ipkl->count], 1561 &rdata_aaaa.in6_addr, 0); 1562 dns_rdata_freestruct(&rdata_aaaa); 1563 } 1564 ipkl->keys[ipkl->count] = NULL; 1565 ipkl->labels[ipkl->count] = NULL; 1566 ipkl->count++; 1567 } 1568 return ISC_R_SUCCESS; 1569 } 1570 1571 static isc_result_t 1572 catz_process_apl(dns_catz_zone_t *catz, isc_buffer_t **aclbp, 1573 dns_rdataset_t *value) { 1574 REQUIRE(DNS_RDATASET_VALID(value)); 1575 REQUIRE(dns_rdataset_isassociated(value)); 1576 1577 if (value->type != dns_rdatatype_apl) { 1578 return ISC_R_FAILURE; 1579 } 1580 1581 REQUIRE(DNS_CATZ_ZONE_VALID(catz)); 1582 REQUIRE(aclbp != NULL); 1583 REQUIRE(*aclbp == NULL); 1584 1585 isc_result_t result = ISC_R_SUCCESS; 1586 dns_rdata_t rdata; 1587 dns_rdata_in_apl_t rdata_apl; 1588 dns_rdata_apl_ent_t apl_ent; 1589 isc_netaddr_t addr; 1590 isc_buffer_t *aclb = NULL; 1591 unsigned char buf[256]; /* larger than INET6_ADDRSTRLEN */ 1592 1593 if (dns_rdataset_count(value) > 1) { 1594 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 1595 DNS_LOGMODULE_MASTER, ISC_LOG_WARNING, 1596 "catz: more than one APL entry for member zone, " 1597 "result is undefined"); 1598 } 1599 result = dns_rdataset_first(value); 1600 RUNTIME_CHECK(result == ISC_R_SUCCESS); 1601 dns_rdata_init(&rdata); 1602 dns_rdataset_current(value, &rdata); 1603 result = dns_rdata_tostruct(&rdata, &rdata_apl, catz->catzs->mctx); 1604 if (result != ISC_R_SUCCESS) { 1605 return result; 1606 } 1607 isc_buffer_allocate(catz->catzs->mctx, &aclb, 16); 1608 for (result = dns_rdata_apl_first(&rdata_apl); result == ISC_R_SUCCESS; 1609 result = dns_rdata_apl_next(&rdata_apl)) 1610 { 1611 result = dns_rdata_apl_current(&rdata_apl, &apl_ent); 1612 RUNTIME_CHECK(result == ISC_R_SUCCESS); 1613 memset(buf, 0, sizeof(buf)); 1614 if (apl_ent.data != NULL && apl_ent.length > 0) { 1615 memmove(buf, apl_ent.data, apl_ent.length); 1616 } 1617 if (apl_ent.family == 1) { 1618 isc_netaddr_fromin(&addr, (struct in_addr *)buf); 1619 } else if (apl_ent.family == 2) { 1620 isc_netaddr_fromin6(&addr, (struct in6_addr *)buf); 1621 } else { 1622 continue; /* xxxwpk log it or simply ignore? */ 1623 } 1624 if (apl_ent.negative) { 1625 isc_buffer_putuint8(aclb, '!'); 1626 } 1627 isc_buffer_reserve(aclb, INET6_ADDRSTRLEN); 1628 result = isc_netaddr_totext(&addr, aclb); 1629 RUNTIME_CHECK(result == ISC_R_SUCCESS); 1630 if ((apl_ent.family == 1 && apl_ent.prefix < 32) || 1631 (apl_ent.family == 2 && apl_ent.prefix < 128)) 1632 { 1633 isc_buffer_putuint8(aclb, '/'); 1634 isc_buffer_printf(aclb, "%" PRId8, apl_ent.prefix); 1635 } 1636 isc_buffer_putstr(aclb, "; "); 1637 } 1638 if (result == ISC_R_NOMORE) { 1639 result = ISC_R_SUCCESS; 1640 } else { 1641 goto cleanup; 1642 } 1643 *aclbp = aclb; 1644 aclb = NULL; 1645 cleanup: 1646 if (aclb != NULL) { 1647 isc_buffer_free(&aclb); 1648 } 1649 dns_rdata_freestruct(&rdata_apl); 1650 return result; 1651 } 1652 1653 static isc_result_t 1654 catz_process_zones_suboption(dns_catz_zone_t *catz, dns_rdataset_t *value, 1655 dns_label_t *mhash, dns_name_t *name) { 1656 isc_result_t result; 1657 dns_catz_entry_t *entry = NULL; 1658 dns_label_t option; 1659 dns_name_t prefix; 1660 catz_opt_t opt; 1661 unsigned int suffix_labels = 1; 1662 1663 REQUIRE(DNS_CATZ_ZONE_VALID(catz)); 1664 REQUIRE(mhash != NULL); 1665 REQUIRE(DNS_RDATASET_VALID(value)); 1666 REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC)); 1667 1668 if (name->labels < 1) { 1669 return ISC_R_FAILURE; 1670 } 1671 dns_name_getlabel(name, name->labels - 1, &option); 1672 opt = catz_get_option(&option); 1673 1674 /* 1675 * The custom properties in version 2 schema must be placed under the 1676 * "ext" label. 1677 */ 1678 if (catz->version >= 2 && opt >= CATZ_OPT_CUSTOM_START) { 1679 if (opt != CATZ_OPT_EXT || name->labels < 2) { 1680 return ISC_R_FAILURE; 1681 } 1682 suffix_labels++; 1683 dns_name_getlabel(name, name->labels - 2, &option); 1684 opt = catz_get_option(&option); 1685 } 1686 1687 /* 1688 * We're adding this entry now, in case the option is invalid we'll get 1689 * rid of it in verification phase. 1690 */ 1691 result = isc_ht_find(catz->entries, mhash->base, mhash->length, 1692 (void **)&entry); 1693 if (result != ISC_R_SUCCESS) { 1694 entry = dns_catz_entry_new(catz->catzs->mctx, NULL); 1695 result = isc_ht_add(catz->entries, mhash->base, mhash->length, 1696 entry); 1697 } 1698 INSIST(result == ISC_R_SUCCESS); 1699 1700 dns_name_init(&prefix, NULL); 1701 dns_name_split(name, suffix_labels, &prefix, NULL); 1702 switch (opt) { 1703 case CATZ_OPT_COO: 1704 return catz_process_coo(catz, mhash, value); 1705 case CATZ_OPT_PRIMARIES: 1706 return catz_process_primaries(catz, &entry->opts.masters, value, 1707 &prefix); 1708 case CATZ_OPT_ALLOW_QUERY: 1709 if (prefix.labels != 0) { 1710 return ISC_R_FAILURE; 1711 } 1712 return catz_process_apl(catz, &entry->opts.allow_query, value); 1713 case CATZ_OPT_ALLOW_TRANSFER: 1714 if (prefix.labels != 0) { 1715 return ISC_R_FAILURE; 1716 } 1717 return catz_process_apl(catz, &entry->opts.allow_transfer, 1718 value); 1719 default: 1720 return ISC_R_FAILURE; 1721 } 1722 1723 return ISC_R_FAILURE; 1724 } 1725 1726 static void 1727 catz_entry_add_or_mod(dns_catz_zone_t *catz, isc_ht_t *ht, unsigned char *key, 1728 size_t keysize, dns_catz_entry_t *nentry, 1729 dns_catz_entry_t *oentry, const char *msg, 1730 const char *zname, const char *czname) { 1731 isc_result_t result = isc_ht_add(ht, key, (uint32_t)keysize, nentry); 1732 1733 if (result != ISC_R_SUCCESS) { 1734 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 1735 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR, 1736 "catz: error %s zone '%s' from catalog '%s' - %s", 1737 msg, zname, czname, isc_result_totext(result)); 1738 } 1739 if (oentry != NULL) { 1740 dns_catz_entry_detach(catz, &oentry); 1741 result = isc_ht_delete(catz->entries, key, (uint32_t)keysize); 1742 RUNTIME_CHECK(result == ISC_R_SUCCESS); 1743 } 1744 } 1745 1746 static isc_result_t 1747 catz_process_value(dns_catz_zone_t *catz, dns_name_t *name, 1748 dns_rdataset_t *rdataset) { 1749 dns_label_t option; 1750 dns_name_t prefix; 1751 catz_opt_t opt; 1752 unsigned int suffix_labels = 1; 1753 1754 REQUIRE(DNS_CATZ_ZONE_VALID(catz)); 1755 REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC)); 1756 REQUIRE(DNS_RDATASET_VALID(rdataset)); 1757 1758 if (name->labels < 1) { 1759 return ISC_R_FAILURE; 1760 } 1761 dns_name_getlabel(name, name->labels - 1, &option); 1762 opt = catz_get_option(&option); 1763 1764 /* 1765 * The custom properties in version 2 schema must be placed under the 1766 * "ext" label. 1767 */ 1768 if (catz->version >= 2 && opt >= CATZ_OPT_CUSTOM_START) { 1769 if (opt != CATZ_OPT_EXT || name->labels < 2) { 1770 return ISC_R_FAILURE; 1771 } 1772 suffix_labels++; 1773 dns_name_getlabel(name, name->labels - 2, &option); 1774 opt = catz_get_option(&option); 1775 } 1776 1777 dns_name_init(&prefix, NULL); 1778 dns_name_split(name, suffix_labels, &prefix, NULL); 1779 1780 switch (opt) { 1781 case CATZ_OPT_ZONES: 1782 return catz_process_zones(catz, rdataset, &prefix); 1783 case CATZ_OPT_PRIMARIES: 1784 return catz_process_primaries(catz, &catz->zoneoptions.masters, 1785 rdataset, &prefix); 1786 case CATZ_OPT_ALLOW_QUERY: 1787 if (prefix.labels != 0) { 1788 return ISC_R_FAILURE; 1789 } 1790 return catz_process_apl(catz, &catz->zoneoptions.allow_query, 1791 rdataset); 1792 case CATZ_OPT_ALLOW_TRANSFER: 1793 if (prefix.labels != 0) { 1794 return ISC_R_FAILURE; 1795 } 1796 return catz_process_apl(catz, &catz->zoneoptions.allow_transfer, 1797 rdataset); 1798 case CATZ_OPT_VERSION: 1799 if (prefix.labels != 0) { 1800 return ISC_R_FAILURE; 1801 } 1802 return catz_process_version(catz, rdataset); 1803 default: 1804 return ISC_R_FAILURE; 1805 } 1806 } 1807 1808 /*%< 1809 * Process a single rdataset from a catalog zone 'catz' update, src_name is the 1810 * record name. 1811 * 1812 * Requires: 1813 * \li 'catz' is a valid dns_catz_zone_t. 1814 * \li 'src_name' is a valid dns_name_t. 1815 * \li 'rdataset' is valid rdataset. 1816 */ 1817 static isc_result_t 1818 dns__catz_update_process(dns_catz_zone_t *catz, const dns_name_t *src_name, 1819 dns_rdataset_t *rdataset) { 1820 isc_result_t result; 1821 int order; 1822 unsigned int nlabels; 1823 dns_namereln_t nrres; 1824 dns_rdata_t rdata = DNS_RDATA_INIT; 1825 dns_rdata_soa_t soa; 1826 dns_name_t prefix; 1827 1828 REQUIRE(DNS_CATZ_ZONE_VALID(catz)); 1829 REQUIRE(ISC_MAGIC_VALID(src_name, DNS_NAME_MAGIC)); 1830 1831 if (rdataset->rdclass != dns_rdataclass_in) { 1832 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 1833 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR, 1834 "catz: RR found which has a non-IN class"); 1835 catz->broken = true; 1836 return ISC_R_FAILURE; 1837 } 1838 1839 nrres = dns_name_fullcompare(src_name, &catz->name, &order, &nlabels); 1840 if (nrres == dns_namereln_equal) { 1841 if (rdataset->type == dns_rdatatype_soa) { 1842 result = dns_rdataset_first(rdataset); 1843 if (result != ISC_R_SUCCESS) { 1844 return result; 1845 } 1846 1847 dns_rdataset_current(rdataset, &rdata); 1848 result = dns_rdata_tostruct(&rdata, &soa, NULL); 1849 RUNTIME_CHECK(result == ISC_R_SUCCESS); 1850 1851 /* 1852 * xxxwpk TODO do we want to save something from SOA? 1853 */ 1854 dns_rdata_freestruct(&soa); 1855 return result; 1856 } else if (rdataset->type == dns_rdatatype_ns) { 1857 return ISC_R_SUCCESS; 1858 } else { 1859 return ISC_R_UNEXPECTED; 1860 } 1861 } else if (nrres != dns_namereln_subdomain) { 1862 return ISC_R_UNEXPECTED; 1863 } 1864 1865 dns_name_init(&prefix, NULL); 1866 dns_name_split(src_name, catz->name.labels, &prefix, NULL); 1867 result = catz_process_value(catz, &prefix, rdataset); 1868 1869 return result; 1870 } 1871 1872 static isc_result_t 1873 digest2hex(unsigned char *digest, unsigned int digestlen, char *hash, 1874 size_t hashlen) { 1875 unsigned int i; 1876 for (i = 0; i < digestlen; i++) { 1877 size_t left = hashlen - i * 2; 1878 int ret = snprintf(hash + i * 2, left, "%02x", digest[i]); 1879 if (ret < 0 || (size_t)ret >= left) { 1880 return ISC_R_NOSPACE; 1881 } 1882 } 1883 return ISC_R_SUCCESS; 1884 } 1885 1886 isc_result_t 1887 dns_catz_generate_masterfilename(dns_catz_zone_t *catz, dns_catz_entry_t *entry, 1888 isc_buffer_t **buffer) { 1889 isc_buffer_t *tbuf = NULL; 1890 isc_region_t r; 1891 isc_result_t result; 1892 size_t rlen; 1893 bool special = false; 1894 1895 REQUIRE(DNS_CATZ_ZONE_VALID(catz)); 1896 REQUIRE(DNS_CATZ_ENTRY_VALID(entry)); 1897 REQUIRE(buffer != NULL && *buffer != NULL); 1898 1899 isc_buffer_allocate(catz->catzs->mctx, &tbuf, 1900 strlen(catz->catzs->view->name) + 1901 2 * DNS_NAME_FORMATSIZE + 2); 1902 1903 isc_buffer_putstr(tbuf, catz->catzs->view->name); 1904 isc_buffer_putstr(tbuf, "_"); 1905 result = dns_name_totext(&catz->name, DNS_NAME_OMITFINALDOT, tbuf); 1906 if (result != ISC_R_SUCCESS) { 1907 goto cleanup; 1908 } 1909 1910 isc_buffer_putstr(tbuf, "_"); 1911 result = dns_name_totext(&entry->name, DNS_NAME_OMITFINALDOT, tbuf); 1912 if (result != ISC_R_SUCCESS) { 1913 goto cleanup; 1914 } 1915 1916 /* 1917 * Search for slash and other special characters in the view and 1918 * zone names. Add a null terminator so we can use strpbrk(), then 1919 * remove it. 1920 */ 1921 isc_buffer_putuint8(tbuf, 0); 1922 if (strpbrk(isc_buffer_base(tbuf), "\\/:") != NULL) { 1923 special = true; 1924 } 1925 isc_buffer_subtract(tbuf, 1); 1926 1927 /* __catz__<digest>.db */ 1928 rlen = (isc_md_type_get_size(ISC_MD_SHA256) * 2 + 1) + 12; 1929 1930 /* optionally prepend with <zonedir>/ */ 1931 if (entry->opts.zonedir != NULL) { 1932 rlen += strlen(entry->opts.zonedir) + 1; 1933 } 1934 1935 result = isc_buffer_reserve(*buffer, (unsigned int)rlen); 1936 if (result != ISC_R_SUCCESS) { 1937 goto cleanup; 1938 } 1939 1940 if (entry->opts.zonedir != NULL) { 1941 isc_buffer_putstr(*buffer, entry->opts.zonedir); 1942 isc_buffer_putstr(*buffer, "/"); 1943 } 1944 1945 isc_buffer_usedregion(tbuf, &r); 1946 isc_buffer_putstr(*buffer, "__catz__"); 1947 if (special || tbuf->used > ISC_SHA256_DIGESTLENGTH * 2 + 1) { 1948 unsigned char digest[ISC_MAX_MD_SIZE]; 1949 unsigned int digestlen; 1950 1951 /* we can do that because digest string < 2 * DNS_NAME */ 1952 result = isc_md(ISC_MD_SHA256, r.base, r.length, digest, 1953 &digestlen); 1954 if (result != ISC_R_SUCCESS) { 1955 goto cleanup; 1956 } 1957 result = digest2hex(digest, digestlen, (char *)r.base, 1958 ISC_SHA256_DIGESTLENGTH * 2 + 1); 1959 if (result != ISC_R_SUCCESS) { 1960 goto cleanup; 1961 } 1962 isc_buffer_putstr(*buffer, (char *)r.base); 1963 } else { 1964 isc_buffer_copyregion(*buffer, &r); 1965 } 1966 1967 isc_buffer_putstr(*buffer, ".db"); 1968 result = ISC_R_SUCCESS; 1969 1970 cleanup: 1971 isc_buffer_free(&tbuf); 1972 return result; 1973 } 1974 1975 /* 1976 * We have to generate a text buffer with regular zone config: 1977 * zone "foo.bar" { 1978 * type secondary; 1979 * primaries { ip1 port port1; ip2 port port2; }; 1980 * } 1981 */ 1982 isc_result_t 1983 dns_catz_generate_zonecfg(dns_catz_zone_t *catz, dns_catz_entry_t *entry, 1984 isc_buffer_t **buf) { 1985 isc_buffer_t *buffer = NULL; 1986 isc_region_t region; 1987 isc_result_t result; 1988 uint32_t i; 1989 isc_netaddr_t netaddr; 1990 char pbuf[sizeof("65535")]; /* used for port number */ 1991 char namebuf[DNS_NAME_FORMATSIZE]; 1992 1993 REQUIRE(DNS_CATZ_ZONE_VALID(catz)); 1994 REQUIRE(DNS_CATZ_ENTRY_VALID(entry)); 1995 REQUIRE(buf != NULL && *buf == NULL); 1996 1997 /* 1998 * The buffer will be reallocated if something won't fit, 1999 * ISC_BUFFER_INCR seems like a good start. 2000 */ 2001 isc_buffer_allocate(catz->catzs->mctx, &buffer, ISC_BUFFER_INCR); 2002 2003 isc_buffer_putstr(buffer, "zone \""); 2004 dns_name_format(&entry->name, namebuf, sizeof(namebuf)); 2005 isc_buffer_putstr(buffer, namebuf); 2006 isc_buffer_putstr(buffer, "\" { type secondary; primaries"); 2007 2008 isc_buffer_putstr(buffer, " { "); 2009 for (i = 0; i < entry->opts.masters.count; i++) { 2010 /* 2011 * Every primary must have an IP address assigned. 2012 */ 2013 switch (entry->opts.masters.addrs[i].type.sa.sa_family) { 2014 case AF_INET: 2015 case AF_INET6: 2016 break; 2017 default: 2018 dns_name_format(&entry->name, namebuf, sizeof(namebuf)); 2019 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 2020 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR, 2021 "catz: zone '%s' uses an invalid primary " 2022 "(no IP address assigned)", 2023 namebuf); 2024 result = ISC_R_FAILURE; 2025 goto cleanup; 2026 } 2027 isc_netaddr_fromsockaddr(&netaddr, 2028 &entry->opts.masters.addrs[i]); 2029 isc_buffer_reserve(buffer, INET6_ADDRSTRLEN); 2030 result = isc_netaddr_totext(&netaddr, buffer); 2031 RUNTIME_CHECK(result == ISC_R_SUCCESS); 2032 2033 isc_buffer_putstr(buffer, " port "); 2034 snprintf(pbuf, sizeof(pbuf), "%u", 2035 isc_sockaddr_getport(&entry->opts.masters.addrs[i])); 2036 isc_buffer_putstr(buffer, pbuf); 2037 2038 if (entry->opts.masters.keys[i] != NULL) { 2039 isc_buffer_putstr(buffer, " key "); 2040 dns_name_format(entry->opts.masters.keys[i], namebuf, 2041 sizeof(namebuf)); 2042 isc_buffer_putstr(buffer, namebuf); 2043 } 2044 2045 if (entry->opts.masters.tlss[i] != NULL) { 2046 isc_buffer_putstr(buffer, " tls "); 2047 dns_name_format(entry->opts.masters.tlss[i], namebuf, 2048 sizeof(namebuf)); 2049 isc_buffer_putstr(buffer, namebuf); 2050 } 2051 isc_buffer_putstr(buffer, "; "); 2052 } 2053 isc_buffer_putstr(buffer, "}; "); 2054 if (!entry->opts.in_memory) { 2055 isc_buffer_putstr(buffer, "file \""); 2056 result = dns_catz_generate_masterfilename(catz, entry, &buffer); 2057 if (result != ISC_R_SUCCESS) { 2058 goto cleanup; 2059 } 2060 isc_buffer_putstr(buffer, "\"; "); 2061 } 2062 if (entry->opts.allow_query != NULL) { 2063 isc_buffer_putstr(buffer, "allow-query { "); 2064 isc_buffer_usedregion(entry->opts.allow_query, ®ion); 2065 isc_buffer_copyregion(buffer, ®ion); 2066 isc_buffer_putstr(buffer, "}; "); 2067 } 2068 if (entry->opts.allow_transfer != NULL) { 2069 isc_buffer_putstr(buffer, "allow-transfer { "); 2070 isc_buffer_usedregion(entry->opts.allow_transfer, ®ion); 2071 isc_buffer_copyregion(buffer, ®ion); 2072 isc_buffer_putstr(buffer, "}; "); 2073 } 2074 2075 isc_buffer_putstr(buffer, "};"); 2076 *buf = buffer; 2077 2078 return ISC_R_SUCCESS; 2079 2080 cleanup: 2081 isc_buffer_free(&buffer); 2082 return result; 2083 } 2084 2085 static void 2086 dns__catz_timer_cb(void *arg) { 2087 char domain[DNS_NAME_FORMATSIZE]; 2088 dns_catz_zone_t *catz = (dns_catz_zone_t *)arg; 2089 2090 REQUIRE(DNS_CATZ_ZONE_VALID(catz)); 2091 2092 if (atomic_load(&catz->catzs->shuttingdown)) { 2093 return; 2094 } 2095 2096 LOCK(&catz->catzs->lock); 2097 2098 INSIST(DNS_DB_VALID(catz->db)); 2099 INSIST(catz->dbversion != NULL); 2100 INSIST(catz->updb == NULL); 2101 INSIST(catz->updbversion == NULL); 2102 2103 catz->updatepending = false; 2104 catz->updaterunning = true; 2105 catz->updateresult = ISC_R_UNSET; 2106 2107 dns_name_format(&catz->name, domain, DNS_NAME_FORMATSIZE); 2108 2109 if (!catz->active) { 2110 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 2111 DNS_LOGMODULE_MASTER, ISC_LOG_INFO, 2112 "catz: %s: no longer active, reload is canceled", 2113 domain); 2114 catz->updaterunning = false; 2115 catz->updateresult = ISC_R_CANCELED; 2116 goto exit; 2117 } 2118 2119 dns_db_attach(catz->db, &catz->updb); 2120 catz->updbversion = catz->dbversion; 2121 catz->dbversion = NULL; 2122 2123 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER, 2124 ISC_LOG_INFO, "catz: %s: reload start", domain); 2125 2126 dns_catz_zone_ref(catz); 2127 isc_work_enqueue(catz->loop, dns__catz_update_cb, dns__catz_done_cb, 2128 catz); 2129 2130 exit: 2131 isc_timer_destroy(&catz->updatetimer); 2132 catz->loop = NULL; 2133 2134 catz->lastupdated = isc_time_now(); 2135 2136 UNLOCK(&catz->catzs->lock); 2137 } 2138 2139 isc_result_t 2140 dns_catz_dbupdate_callback(dns_db_t *db, void *fn_arg) { 2141 dns_catz_zones_t *catzs = NULL; 2142 dns_catz_zone_t *catz = NULL; 2143 isc_result_t result = ISC_R_SUCCESS; 2144 isc_region_t r; 2145 2146 REQUIRE(DNS_DB_VALID(db)); 2147 REQUIRE(DNS_CATZ_ZONES_VALID(fn_arg)); 2148 catzs = (dns_catz_zones_t *)fn_arg; 2149 2150 if (atomic_load(&catzs->shuttingdown)) { 2151 return ISC_R_SHUTTINGDOWN; 2152 } 2153 2154 dns_name_toregion(&db->origin, &r); 2155 2156 LOCK(&catzs->lock); 2157 if (catzs->zones == NULL) { 2158 result = ISC_R_SHUTTINGDOWN; 2159 goto cleanup; 2160 } 2161 result = isc_ht_find(catzs->zones, r.base, r.length, (void **)&catz); 2162 if (result != ISC_R_SUCCESS) { 2163 goto cleanup; 2164 } 2165 2166 /* New zone came as AXFR */ 2167 if (catz->db != NULL && catz->db != db) { 2168 /* Old db cleanup. */ 2169 if (catz->dbversion != NULL) { 2170 dns_db_closeversion(catz->db, &catz->dbversion, false); 2171 } 2172 dns_db_updatenotify_unregister( 2173 catz->db, dns_catz_dbupdate_callback, catz->catzs); 2174 dns_db_detach(&catz->db); 2175 } 2176 if (catz->db == NULL) { 2177 /* New db registration. */ 2178 dns_db_attach(db, &catz->db); 2179 dns_db_updatenotify_register(db, dns_catz_dbupdate_callback, 2180 catz->catzs); 2181 } 2182 2183 if (!catz->updatepending && !catz->updaterunning) { 2184 catz->updatepending = true; 2185 dns_db_currentversion(db, &catz->dbversion); 2186 dns__catz_timer_start(catz); 2187 } else { 2188 char dname[DNS_NAME_FORMATSIZE]; 2189 2190 catz->updatepending = true; 2191 dns_name_format(&catz->name, dname, DNS_NAME_FORMATSIZE); 2192 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 2193 DNS_LOGMODULE_MASTER, ISC_LOG_DEBUG(3), 2194 "catz: %s: update already queued or running", 2195 dname); 2196 if (catz->dbversion != NULL) { 2197 dns_db_closeversion(catz->db, &catz->dbversion, false); 2198 } 2199 dns_db_currentversion(catz->db, &catz->dbversion); 2200 } 2201 2202 cleanup: 2203 UNLOCK(&catzs->lock); 2204 2205 return result; 2206 } 2207 2208 void 2209 dns_catz_dbupdate_unregister(dns_db_t *db, dns_catz_zones_t *catzs) { 2210 REQUIRE(DNS_DB_VALID(db)); 2211 REQUIRE(DNS_CATZ_ZONES_VALID(catzs)); 2212 2213 dns_db_updatenotify_unregister(db, dns_catz_dbupdate_callback, catzs); 2214 } 2215 2216 void 2217 dns_catz_dbupdate_register(dns_db_t *db, dns_catz_zones_t *catzs) { 2218 REQUIRE(DNS_DB_VALID(db)); 2219 REQUIRE(DNS_CATZ_ZONES_VALID(catzs)); 2220 2221 dns_db_updatenotify_register(db, dns_catz_dbupdate_callback, catzs); 2222 } 2223 2224 static bool 2225 catz_rdatatype_is_processable(const dns_rdatatype_t type) { 2226 return !dns_rdatatype_isdnssec(type) && type != dns_rdatatype_cds && 2227 type != dns_rdatatype_cdnskey && type != dns_rdatatype_zonemd; 2228 } 2229 2230 /* 2231 * Process an updated database for a catalog zone. 2232 * It creates a new catz, iterates over database to fill it with content, and 2233 * then merges new catz into old catz. 2234 */ 2235 static void 2236 dns__catz_update_cb(void *data) { 2237 dns_catz_zone_t *catz = (dns_catz_zone_t *)data; 2238 dns_db_t *updb = NULL; 2239 dns_catz_zones_t *catzs = NULL; 2240 dns_catz_zone_t *oldcatz = NULL, *newcatz = NULL; 2241 isc_result_t result; 2242 isc_region_t r; 2243 dns_dbnode_t *node = NULL; 2244 const dns_dbnode_t *vers_node = NULL; 2245 dns_dbiterator_t *updbit = NULL; 2246 dns_fixedname_t fixname; 2247 dns_name_t *name = NULL; 2248 dns_rdatasetiter_t *rdsiter = NULL; 2249 dns_rdataset_t rdataset; 2250 char bname[DNS_NAME_FORMATSIZE]; 2251 char cname[DNS_NAME_FORMATSIZE]; 2252 bool is_vers_processed = false; 2253 bool is_active; 2254 uint32_t vers; 2255 uint32_t catz_vers; 2256 2257 REQUIRE(DNS_CATZ_ZONE_VALID(catz)); 2258 REQUIRE(DNS_DB_VALID(catz->updb)); 2259 REQUIRE(DNS_CATZ_ZONES_VALID(catz->catzs)); 2260 2261 updb = catz->updb; 2262 catzs = catz->catzs; 2263 2264 if (atomic_load(&catzs->shuttingdown)) { 2265 result = ISC_R_SHUTTINGDOWN; 2266 goto exit; 2267 } 2268 2269 dns_name_format(&updb->origin, bname, DNS_NAME_FORMATSIZE); 2270 2271 /* 2272 * Create a new catz in the same context as current catz. 2273 */ 2274 dns_name_toregion(&updb->origin, &r); 2275 LOCK(&catzs->lock); 2276 if (catzs->zones == NULL) { 2277 UNLOCK(&catzs->lock); 2278 result = ISC_R_SHUTTINGDOWN; 2279 goto exit; 2280 } 2281 result = isc_ht_find(catzs->zones, r.base, r.length, (void **)&oldcatz); 2282 is_active = (result == ISC_R_SUCCESS && oldcatz->active); 2283 UNLOCK(&catzs->lock); 2284 if (result != ISC_R_SUCCESS) { 2285 /* This can happen if we remove the zone in the meantime. */ 2286 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 2287 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR, 2288 "catz: zone '%s' not in config", bname); 2289 goto exit; 2290 } 2291 2292 if (!is_active) { 2293 /* This can happen during a reconfiguration. */ 2294 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 2295 DNS_LOGMODULE_MASTER, ISC_LOG_INFO, 2296 "catz: zone '%s' is no longer active", bname); 2297 result = ISC_R_CANCELED; 2298 goto exit; 2299 } 2300 2301 result = dns_db_getsoaserial(updb, oldcatz->updbversion, &vers); 2302 if (result != ISC_R_SUCCESS) { 2303 /* A zone without SOA record?!? */ 2304 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 2305 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR, 2306 "catz: zone '%s' has no SOA record (%s)", bname, 2307 isc_result_totext(result)); 2308 goto exit; 2309 } 2310 2311 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER, 2312 ISC_LOG_INFO, 2313 "catz: updating catalog zone '%s' with serial %" PRIu32, 2314 bname, vers); 2315 2316 result = dns_db_createiterator(updb, DNS_DB_NONSEC3, &updbit); 2317 if (result != ISC_R_SUCCESS) { 2318 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 2319 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR, 2320 "catz: failed to create DB iterator - %s", 2321 isc_result_totext(result)); 2322 goto exit; 2323 } 2324 2325 name = dns_fixedname_initname(&fixname); 2326 2327 /* 2328 * Take the version record to process first, because the other 2329 * records might be processed differently depending on the version of 2330 * the catalog zone's schema. 2331 */ 2332 result = dns_name_fromstring(name, "version", &updb->origin, 0, NULL); 2333 if (result != ISC_R_SUCCESS) { 2334 dns_dbiterator_destroy(&updbit); 2335 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 2336 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR, 2337 "catz: failed to create name from string - %s", 2338 isc_result_totext(result)); 2339 goto exit; 2340 } 2341 2342 result = dns_dbiterator_seek(updbit, name); 2343 if (result != ISC_R_SUCCESS) { 2344 dns_dbiterator_destroy(&updbit); 2345 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 2346 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR, 2347 "catz: zone '%s' has no 'version' record (%s) " 2348 "and will not be processed", 2349 bname, isc_result_totext(result)); 2350 goto exit; 2351 } 2352 2353 newcatz = dns_catz_zone_new(catzs, &updb->origin); 2354 name = dns_fixedname_initname(&fixname); 2355 2356 /* 2357 * Iterate over database to fill the new zone. 2358 */ 2359 while (result == ISC_R_SUCCESS) { 2360 if (atomic_load(&catzs->shuttingdown)) { 2361 result = ISC_R_SHUTTINGDOWN; 2362 break; 2363 } 2364 2365 result = dns_dbiterator_current(updbit, &node, name); 2366 if (result != ISC_R_SUCCESS) { 2367 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 2368 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR, 2369 "catz: failed to get db iterator - %s", 2370 isc_result_totext(result)); 2371 break; 2372 } 2373 2374 result = dns_dbiterator_pause(updbit); 2375 RUNTIME_CHECK(result == ISC_R_SUCCESS); 2376 2377 if (!is_vers_processed) { 2378 /* Keep the version node to skip it later in the loop */ 2379 vers_node = node; 2380 } else if (node == vers_node) { 2381 /* Skip the already processed version node */ 2382 dns_db_detachnode(updb, &node); 2383 result = dns_dbiterator_next(updbit); 2384 continue; 2385 } 2386 2387 result = dns_db_allrdatasets(updb, node, oldcatz->updbversion, 2388 0, 0, &rdsiter); 2389 if (result != ISC_R_SUCCESS) { 2390 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 2391 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR, 2392 "catz: failed to fetch rrdatasets - %s", 2393 isc_result_totext(result)); 2394 dns_db_detachnode(updb, &node); 2395 break; 2396 } 2397 2398 dns_rdataset_init(&rdataset); 2399 result = dns_rdatasetiter_first(rdsiter); 2400 while (result == ISC_R_SUCCESS) { 2401 dns_rdatasetiter_current(rdsiter, &rdataset); 2402 2403 /* 2404 * Skip processing DNSSEC-related and ZONEMD types, 2405 * because we are not interested in them in the context 2406 * of a catalog zone, and processing them will fail 2407 * and produce an unnecessary warning message. 2408 */ 2409 if (!catz_rdatatype_is_processable(rdataset.type)) { 2410 goto next; 2411 } 2412 2413 /* 2414 * Although newcatz->coos is accessed in 2415 * catz_process_coo() in the call-chain below, we don't 2416 * need to hold the newcatz->lock, because the newcatz 2417 * is still local to this thread and function and 2418 * newcatz->coos can't be accessed from the outside 2419 * until dns__catz_zones_merge() has been called. 2420 */ 2421 result = dns__catz_update_process(newcatz, name, 2422 &rdataset); 2423 if (result != ISC_R_SUCCESS) { 2424 char typebuf[DNS_RDATATYPE_FORMATSIZE]; 2425 char classbuf[DNS_RDATACLASS_FORMATSIZE]; 2426 2427 dns_name_format(name, cname, 2428 DNS_NAME_FORMATSIZE); 2429 dns_rdataclass_format(rdataset.rdclass, 2430 classbuf, 2431 sizeof(classbuf)); 2432 dns_rdatatype_format(rdataset.type, typebuf, 2433 sizeof(typebuf)); 2434 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 2435 DNS_LOGMODULE_MASTER, 2436 ISC_LOG_WARNING, 2437 "catz: invalid record in catalog " 2438 "zone - %s %s %s (%s) - ignoring", 2439 cname, classbuf, typebuf, 2440 isc_result_totext(result)); 2441 } 2442 next: 2443 dns_rdataset_disassociate(&rdataset); 2444 result = dns_rdatasetiter_next(rdsiter); 2445 } 2446 2447 dns_rdatasetiter_destroy(&rdsiter); 2448 2449 dns_db_detachnode(updb, &node); 2450 2451 if (!is_vers_processed) { 2452 is_vers_processed = true; 2453 result = dns_dbiterator_first(updbit); 2454 } else { 2455 result = dns_dbiterator_next(updbit); 2456 } 2457 } 2458 2459 dns_dbiterator_destroy(&updbit); 2460 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER, 2461 ISC_LOG_DEBUG(3), 2462 "catz: update_from_db: iteration finished: %s", 2463 isc_result_totext(result)); 2464 2465 /* 2466 * Check catalog zone version compatibilites. 2467 */ 2468 catz_vers = (newcatz->version == DNS_CATZ_VERSION_UNDEFINED) 2469 ? oldcatz->version 2470 : newcatz->version; 2471 if (catz_vers == DNS_CATZ_VERSION_UNDEFINED) { 2472 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 2473 DNS_LOGMODULE_MASTER, ISC_LOG_WARNING, 2474 "catz: zone '%s' version is not set", bname); 2475 newcatz->broken = true; 2476 } else if (catz_vers != 1 && catz_vers != 2) { 2477 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 2478 DNS_LOGMODULE_MASTER, ISC_LOG_WARNING, 2479 "catz: zone '%s' unsupported version " 2480 "'%" PRIu32 "'", 2481 bname, catz_vers); 2482 newcatz->broken = true; 2483 } else { 2484 oldcatz->version = catz_vers; 2485 } 2486 2487 if (newcatz->broken) { 2488 dns_name_format(name, cname, DNS_NAME_FORMATSIZE); 2489 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 2490 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR, 2491 "catz: new catalog zone '%s' is broken and " 2492 "will not be processed", 2493 bname); 2494 dns_catz_zone_detach(&newcatz); 2495 result = ISC_R_FAILURE; 2496 goto exit; 2497 } 2498 2499 /* 2500 * Finally merge new zone into old zone. 2501 */ 2502 result = dns__catz_zones_merge(oldcatz, newcatz); 2503 dns_catz_zone_detach(&newcatz); 2504 if (result != ISC_R_SUCCESS) { 2505 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 2506 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR, 2507 "catz: failed merging zones: %s", 2508 isc_result_totext(result)); 2509 2510 goto exit; 2511 } 2512 2513 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER, 2514 ISC_LOG_DEBUG(3), 2515 "catz: update_from_db: new zone merged"); 2516 2517 exit: 2518 catz->updateresult = result; 2519 } 2520 2521 static void 2522 dns__catz_done_cb(void *data) { 2523 dns_catz_zone_t *catz = (dns_catz_zone_t *)data; 2524 char dname[DNS_NAME_FORMATSIZE]; 2525 2526 REQUIRE(DNS_CATZ_ZONE_VALID(catz)); 2527 2528 LOCK(&catz->catzs->lock); 2529 catz->updaterunning = false; 2530 2531 dns_name_format(&catz->name, dname, DNS_NAME_FORMATSIZE); 2532 2533 if (catz->updatepending && !atomic_load(&catz->catzs->shuttingdown)) { 2534 /* Restart the timer */ 2535 dns__catz_timer_start(catz); 2536 } 2537 2538 dns_db_closeversion(catz->updb, &catz->updbversion, false); 2539 dns_db_detach(&catz->updb); 2540 2541 UNLOCK(&catz->catzs->lock); 2542 2543 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER, 2544 ISC_LOG_INFO, "catz: %s: reload done: %s", dname, 2545 isc_result_totext(catz->updateresult)); 2546 2547 dns_catz_zone_unref(catz); 2548 } 2549 2550 void 2551 dns_catz_prereconfig(dns_catz_zones_t *catzs) { 2552 isc_result_t result; 2553 isc_ht_iter_t *iter = NULL; 2554 2555 REQUIRE(DNS_CATZ_ZONES_VALID(catzs)); 2556 2557 LOCK(&catzs->lock); 2558 isc_ht_iter_create(catzs->zones, &iter); 2559 for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS; 2560 result = isc_ht_iter_next(iter)) 2561 { 2562 dns_catz_zone_t *catz = NULL; 2563 isc_ht_iter_current(iter, (void **)&catz); 2564 catz->active = false; 2565 } 2566 UNLOCK(&catzs->lock); 2567 INSIST(result == ISC_R_NOMORE); 2568 isc_ht_iter_destroy(&iter); 2569 } 2570 2571 void 2572 dns_catz_postreconfig(dns_catz_zones_t *catzs) { 2573 isc_result_t result; 2574 dns_catz_zone_t *newcatz = NULL; 2575 isc_ht_iter_t *iter = NULL; 2576 2577 REQUIRE(DNS_CATZ_ZONES_VALID(catzs)); 2578 2579 LOCK(&catzs->lock); 2580 isc_ht_iter_create(catzs->zones, &iter); 2581 for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;) { 2582 dns_catz_zone_t *catz = NULL; 2583 2584 isc_ht_iter_current(iter, (void **)&catz); 2585 if (!catz->active) { 2586 char cname[DNS_NAME_FORMATSIZE]; 2587 dns_name_format(&catz->name, cname, 2588 DNS_NAME_FORMATSIZE); 2589 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, 2590 DNS_LOGMODULE_MASTER, ISC_LOG_WARNING, 2591 "catz: removing catalog zone %s", cname); 2592 2593 /* 2594 * Merge the old zone with an empty one to remove 2595 * all members. 2596 */ 2597 newcatz = dns_catz_zone_new(catzs, &catz->name); 2598 dns__catz_zones_merge(catz, newcatz); 2599 dns_catz_zone_detach(&newcatz); 2600 2601 /* Make sure that we have an empty catalog zone. */ 2602 INSIST(isc_ht_count(catz->entries) == 0); 2603 result = isc_ht_iter_delcurrent_next(iter); 2604 dns_catz_zone_detach(&catz); 2605 } else { 2606 result = isc_ht_iter_next(iter); 2607 } 2608 } 2609 UNLOCK(&catzs->lock); 2610 RUNTIME_CHECK(result == ISC_R_NOMORE); 2611 isc_ht_iter_destroy(&iter); 2612 } 2613 2614 void 2615 dns_catz_zone_prereconfig(dns_catz_zone_t *catz) { 2616 LOCK(&catz->lock); 2617 } 2618 2619 void 2620 dns_catz_zone_postreconfig(dns_catz_zone_t *catz) { 2621 UNLOCK(&catz->lock); 2622 } 2623 2624 void 2625 dns_catz_zone_for_each_entry2(dns_catz_zone_t *catz, dns_catz_entry_cb2 cb, 2626 void *arg1, void *arg2) { 2627 REQUIRE(DNS_CATZ_ZONE_VALID(catz)); 2628 2629 isc_ht_iter_t *iter = NULL; 2630 isc_result_t result; 2631 2632 LOCK(&catz->catzs->lock); 2633 isc_ht_iter_create(catz->entries, &iter); 2634 for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS; 2635 result = isc_ht_iter_next(iter)) 2636 { 2637 dns_catz_entry_t *entry = NULL; 2638 2639 isc_ht_iter_current(iter, (void **)&entry); 2640 cb(entry, arg1, arg2); 2641 } 2642 isc_ht_iter_destroy(&iter); 2643 UNLOCK(&catz->catzs->lock); 2644 } 2645