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