Home | History | Annotate | Line # | Download | only in dns
catz.c revision 1.9
      1 /*	$NetBSD: catz.c,v 1.9 2023/01/25 21:43:30 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 
     21 #include <isc/hex.h>
     22 #include <isc/md.h>
     23 #include <isc/mem.h>
     24 #include <isc/parseint.h>
     25 #include <isc/print.h>
     26 #include <isc/result.h>
     27 #include <isc/task.h>
     28 #include <isc/util.h>
     29 
     30 #include <dns/catz.h>
     31 #include <dns/dbiterator.h>
     32 #include <dns/events.h>
     33 #include <dns/rdatasetiter.h>
     34 #include <dns/view.h>
     35 #include <dns/zone.h>
     36 
     37 #define DNS_CATZ_ZONE_MAGIC  ISC_MAGIC('c', 'a', 't', 'z')
     38 #define DNS_CATZ_ZONES_MAGIC ISC_MAGIC('c', 'a', 't', 's')
     39 #define DNS_CATZ_ENTRY_MAGIC ISC_MAGIC('c', 'a', 't', 'e')
     40 
     41 #define DNS_CATZ_ZONE_VALID(catz)   ISC_MAGIC_VALID(catz, DNS_CATZ_ZONE_MAGIC)
     42 #define DNS_CATZ_ZONES_VALID(catzs) ISC_MAGIC_VALID(catzs, DNS_CATZ_ZONES_MAGIC)
     43 #define DNS_CATZ_ENTRY_VALID(entry) ISC_MAGIC_VALID(entry, DNS_CATZ_ENTRY_MAGIC)
     44 
     45 /*%
     46  * Single member zone in a catalog
     47  */
     48 struct dns_catz_entry {
     49 	unsigned int magic;
     50 	dns_name_t name;
     51 	dns_catz_options_t opts;
     52 	isc_refcount_t refs;
     53 };
     54 
     55 /*%
     56  * Catalog zone
     57  */
     58 struct dns_catz_zone {
     59 	unsigned int magic;
     60 	dns_name_t name;
     61 	dns_catz_zones_t *catzs;
     62 	dns_rdata_t soa;
     63 	/* key in entries is 'mhash', not domain name! */
     64 	isc_ht_t *entries;
     65 	/*
     66 	 * defoptions are taken from named.conf
     67 	 * zoneoptions are global options from zone
     68 	 */
     69 	dns_catz_options_t defoptions;
     70 	dns_catz_options_t zoneoptions;
     71 	isc_time_t lastupdated;
     72 	bool updatepending;
     73 	uint32_t version;
     74 
     75 	dns_db_t *db;
     76 	dns_dbversion_t *dbversion;
     77 
     78 	isc_timer_t *updatetimer;
     79 	isc_event_t updateevent;
     80 
     81 	bool active;
     82 	bool db_registered;
     83 
     84 	isc_refcount_t refs;
     85 };
     86 
     87 static isc_result_t
     88 catz_process_zones_entry(dns_catz_zone_t *zone, dns_rdataset_t *value,
     89 			 dns_label_t *mhash);
     90 static isc_result_t
     91 catz_process_zones_suboption(dns_catz_zone_t *zone, dns_rdataset_t *value,
     92 			     dns_label_t *mhash, dns_name_t *name);
     93 static void
     94 catz_entry_add_or_mod(dns_catz_zone_t *target, isc_ht_t *ht, unsigned char *key,
     95 		      size_t keysize, dns_catz_entry_t *nentry,
     96 		      dns_catz_entry_t *oentry, const char *msg,
     97 		      const char *zname, const char *czname);
     98 
     99 /*%
    100  * Collection of catalog zones for a view
    101  */
    102 struct dns_catz_zones {
    103 	unsigned int magic;
    104 	isc_ht_t *zones;
    105 	isc_mem_t *mctx;
    106 	isc_refcount_t refs;
    107 	isc_mutex_t lock;
    108 	dns_catz_zonemodmethods_t *zmm;
    109 	isc_taskmgr_t *taskmgr;
    110 	isc_timermgr_t *timermgr;
    111 	dns_view_t *view;
    112 	isc_task_t *updater;
    113 };
    114 
    115 void
    116 dns_catz_options_init(dns_catz_options_t *options) {
    117 	REQUIRE(options != NULL);
    118 
    119 	dns_ipkeylist_init(&options->masters);
    120 
    121 	options->allow_query = NULL;
    122 	options->allow_transfer = NULL;
    123 
    124 	options->allow_query = NULL;
    125 	options->allow_transfer = NULL;
    126 
    127 	options->in_memory = false;
    128 	options->min_update_interval = 5;
    129 	options->zonedir = NULL;
    130 }
    131 
    132 void
    133 dns_catz_options_free(dns_catz_options_t *options, isc_mem_t *mctx) {
    134 	REQUIRE(options != NULL);
    135 	REQUIRE(mctx != NULL);
    136 
    137 	if (options->masters.count != 0) {
    138 		dns_ipkeylist_clear(mctx, &options->masters);
    139 	}
    140 	if (options->zonedir != NULL) {
    141 		isc_mem_free(mctx, options->zonedir);
    142 		options->zonedir = NULL;
    143 	}
    144 	if (options->allow_query != NULL) {
    145 		isc_buffer_free(&options->allow_query);
    146 	}
    147 	if (options->allow_transfer != NULL) {
    148 		isc_buffer_free(&options->allow_transfer);
    149 	}
    150 }
    151 
    152 isc_result_t
    153 dns_catz_options_copy(isc_mem_t *mctx, const dns_catz_options_t *src,
    154 		      dns_catz_options_t *dst) {
    155 	REQUIRE(mctx != NULL);
    156 	REQUIRE(src != NULL);
    157 	REQUIRE(dst != NULL);
    158 	REQUIRE(dst->masters.count == 0);
    159 	REQUIRE(dst->allow_query == NULL);
    160 	REQUIRE(dst->allow_transfer == NULL);
    161 
    162 	if (src->masters.count != 0) {
    163 		dns_ipkeylist_copy(mctx, &src->masters, &dst->masters);
    164 	}
    165 
    166 	if (dst->zonedir != NULL) {
    167 		isc_mem_free(mctx, dst->zonedir);
    168 		dst->zonedir = NULL;
    169 	}
    170 
    171 	if (src->zonedir != NULL) {
    172 		dst->zonedir = isc_mem_strdup(mctx, src->zonedir);
    173 	}
    174 
    175 	if (src->allow_query != NULL) {
    176 		isc_buffer_dup(mctx, &dst->allow_query, src->allow_query);
    177 	}
    178 
    179 	if (src->allow_transfer != NULL) {
    180 		isc_buffer_dup(mctx, &dst->allow_transfer, src->allow_transfer);
    181 	}
    182 
    183 	return (ISC_R_SUCCESS);
    184 }
    185 
    186 isc_result_t
    187 dns_catz_options_setdefault(isc_mem_t *mctx, const dns_catz_options_t *defaults,
    188 			    dns_catz_options_t *opts) {
    189 	REQUIRE(mctx != NULL);
    190 	REQUIRE(defaults != NULL);
    191 	REQUIRE(opts != NULL);
    192 
    193 	if (opts->masters.count == 0 && defaults->masters.count != 0) {
    194 		dns_ipkeylist_copy(mctx, &defaults->masters, &opts->masters);
    195 	}
    196 
    197 	if (defaults->zonedir != NULL) {
    198 		opts->zonedir = isc_mem_strdup(mctx, defaults->zonedir);
    199 	}
    200 
    201 	if (opts->allow_query == NULL && defaults->allow_query != NULL) {
    202 		isc_buffer_dup(mctx, &opts->allow_query, defaults->allow_query);
    203 	}
    204 	if (opts->allow_transfer == NULL && defaults->allow_transfer != NULL) {
    205 		isc_buffer_dup(mctx, &opts->allow_transfer,
    206 			       defaults->allow_transfer);
    207 	}
    208 
    209 	/* This option is always taken from config, so it's always 'default' */
    210 	opts->in_memory = defaults->in_memory;
    211 	return (ISC_R_SUCCESS);
    212 }
    213 
    214 isc_result_t
    215 dns_catz_entry_new(isc_mem_t *mctx, const dns_name_t *domain,
    216 		   dns_catz_entry_t **nentryp) {
    217 	dns_catz_entry_t *nentry;
    218 
    219 	REQUIRE(mctx != NULL);
    220 	REQUIRE(nentryp != NULL && *nentryp == NULL);
    221 
    222 	nentry = isc_mem_get(mctx, sizeof(dns_catz_entry_t));
    223 
    224 	dns_name_init(&nentry->name, NULL);
    225 	if (domain != NULL) {
    226 		dns_name_dup(domain, mctx, &nentry->name);
    227 	}
    228 
    229 	dns_catz_options_init(&nentry->opts);
    230 	isc_refcount_init(&nentry->refs, 1);
    231 	nentry->magic = DNS_CATZ_ENTRY_MAGIC;
    232 	*nentryp = nentry;
    233 	return (ISC_R_SUCCESS);
    234 }
    235 
    236 dns_name_t *
    237 dns_catz_entry_getname(dns_catz_entry_t *entry) {
    238 	REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
    239 	return (&entry->name);
    240 }
    241 
    242 isc_result_t
    243 dns_catz_entry_copy(dns_catz_zone_t *zone, const dns_catz_entry_t *entry,
    244 		    dns_catz_entry_t **nentryp) {
    245 	isc_result_t result;
    246 	dns_catz_entry_t *nentry = NULL;
    247 
    248 	REQUIRE(DNS_CATZ_ZONE_VALID(zone));
    249 	REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
    250 	REQUIRE(nentryp != NULL && *nentryp == NULL);
    251 
    252 	result = dns_catz_entry_new(zone->catzs->mctx, &entry->name, &nentry);
    253 	if (result != ISC_R_SUCCESS) {
    254 		return (result);
    255 	}
    256 
    257 	result = dns_catz_options_copy(zone->catzs->mctx, &entry->opts,
    258 				       &nentry->opts);
    259 	if (result != ISC_R_SUCCESS) {
    260 		dns_catz_entry_detach(zone, &nentry);
    261 	}
    262 
    263 	*nentryp = nentry;
    264 	return (result);
    265 }
    266 
    267 void
    268 dns_catz_entry_attach(dns_catz_entry_t *entry, dns_catz_entry_t **entryp) {
    269 	REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
    270 	REQUIRE(entryp != NULL && *entryp == NULL);
    271 
    272 	isc_refcount_increment(&entry->refs);
    273 	*entryp = entry;
    274 }
    275 
    276 void
    277 dns_catz_entry_detach(dns_catz_zone_t *zone, dns_catz_entry_t **entryp) {
    278 	dns_catz_entry_t *entry;
    279 
    280 	REQUIRE(DNS_CATZ_ZONE_VALID(zone));
    281 	REQUIRE(entryp != NULL);
    282 	entry = *entryp;
    283 	*entryp = NULL;
    284 	REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
    285 
    286 	if (isc_refcount_decrement(&entry->refs) == 1) {
    287 		isc_mem_t *mctx = zone->catzs->mctx;
    288 		entry->magic = 0;
    289 		isc_refcount_destroy(&entry->refs);
    290 		dns_catz_options_free(&entry->opts, mctx);
    291 		if (dns_name_dynamic(&entry->name)) {
    292 			dns_name_free(&entry->name, mctx);
    293 		}
    294 		isc_mem_put(mctx, entry, sizeof(dns_catz_entry_t));
    295 	}
    296 }
    297 
    298 bool
    299 dns_catz_entry_validate(const dns_catz_entry_t *entry) {
    300 	REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
    301 	UNUSED(entry);
    302 
    303 	return (true);
    304 }
    305 
    306 bool
    307 dns_catz_entry_cmp(const dns_catz_entry_t *ea, const dns_catz_entry_t *eb) {
    308 	isc_region_t ra, rb;
    309 
    310 	REQUIRE(DNS_CATZ_ENTRY_VALID(ea));
    311 	REQUIRE(DNS_CATZ_ENTRY_VALID(eb));
    312 
    313 	if (ea == eb) {
    314 		return (true);
    315 	}
    316 
    317 	if (ea->opts.masters.count != eb->opts.masters.count) {
    318 		return (false);
    319 	}
    320 
    321 	if (memcmp(ea->opts.masters.addrs, eb->opts.masters.addrs,
    322 		   ea->opts.masters.count * sizeof(isc_sockaddr_t)))
    323 	{
    324 		return (false);
    325 	}
    326 
    327 	for (size_t i = 0; i < eb->opts.masters.count; i++) {
    328 		if ((ea->opts.masters.keys[i] == NULL) !=
    329 		    (eb->opts.masters.keys[i] == NULL))
    330 		{
    331 			return (false);
    332 		}
    333 		if (ea->opts.masters.keys[i] == NULL) {
    334 			continue;
    335 		}
    336 		if (!dns_name_equal(ea->opts.masters.keys[i],
    337 				    eb->opts.masters.keys[i]))
    338 		{
    339 			return (false);
    340 		}
    341 	}
    342 
    343 	/* If one is NULL and the other isn't, the entries don't match */
    344 	if ((ea->opts.allow_query == NULL) != (eb->opts.allow_query == NULL)) {
    345 		return (false);
    346 	}
    347 
    348 	/* If one is non-NULL, then they both are */
    349 	if (ea->opts.allow_query != NULL) {
    350 		isc_buffer_usedregion(ea->opts.allow_query, &ra);
    351 		isc_buffer_usedregion(eb->opts.allow_query, &rb);
    352 		if (isc_region_compare(&ra, &rb)) {
    353 			return (false);
    354 		}
    355 	}
    356 
    357 	/* Repeat the above checks with allow_transfer */
    358 	if ((ea->opts.allow_transfer == NULL) !=
    359 	    (eb->opts.allow_transfer == NULL))
    360 	{
    361 		return (false);
    362 	}
    363 
    364 	if (ea->opts.allow_transfer != NULL) {
    365 		isc_buffer_usedregion(ea->opts.allow_transfer, &ra);
    366 		isc_buffer_usedregion(eb->opts.allow_transfer, &rb);
    367 		if (isc_region_compare(&ra, &rb)) {
    368 			return (false);
    369 		}
    370 	}
    371 
    372 	/* xxxwpk TODO compare dscps! */
    373 	return (true);
    374 }
    375 
    376 dns_name_t *
    377 dns_catz_zone_getname(dns_catz_zone_t *zone) {
    378 	REQUIRE(DNS_CATZ_ZONE_VALID(zone));
    379 
    380 	return (&zone->name);
    381 }
    382 
    383 dns_catz_options_t *
    384 dns_catz_zone_getdefoptions(dns_catz_zone_t *zone) {
    385 	REQUIRE(DNS_CATZ_ZONE_VALID(zone));
    386 
    387 	return (&zone->defoptions);
    388 }
    389 
    390 void
    391 dns_catz_zone_resetdefoptions(dns_catz_zone_t *zone) {
    392 	REQUIRE(DNS_CATZ_ZONE_VALID(zone));
    393 
    394 	dns_catz_options_free(&zone->defoptions, zone->catzs->mctx);
    395 	dns_catz_options_init(&zone->defoptions);
    396 }
    397 
    398 isc_result_t
    399 dns_catz_zones_merge(dns_catz_zone_t *target, dns_catz_zone_t *newzone) {
    400 	isc_result_t result;
    401 	isc_ht_iter_t *iter1 = NULL, *iter2 = NULL;
    402 	isc_ht_iter_t *iteradd = NULL, *itermod = NULL;
    403 	isc_ht_t *toadd = NULL, *tomod = NULL;
    404 	bool delcur = false;
    405 	char czname[DNS_NAME_FORMATSIZE];
    406 	char zname[DNS_NAME_FORMATSIZE];
    407 	dns_catz_zoneop_fn_t addzone, modzone, delzone;
    408 
    409 	REQUIRE(DNS_CATZ_ZONE_VALID(newzone));
    410 	REQUIRE(DNS_CATZ_ZONE_VALID(target));
    411 
    412 	/* TODO verify the new zone first! */
    413 
    414 	addzone = target->catzs->zmm->addzone;
    415 	modzone = target->catzs->zmm->modzone;
    416 	delzone = target->catzs->zmm->delzone;
    417 
    418 	/* Copy zoneoptions from newzone into target. */
    419 
    420 	dns_catz_options_free(&target->zoneoptions, target->catzs->mctx);
    421 	dns_catz_options_copy(target->catzs->mctx, &newzone->zoneoptions,
    422 			      &target->zoneoptions);
    423 	dns_catz_options_setdefault(target->catzs->mctx, &target->defoptions,
    424 				    &target->zoneoptions);
    425 
    426 	dns_name_format(&target->name, czname, DNS_NAME_FORMATSIZE);
    427 
    428 	isc_ht_init(&toadd, target->catzs->mctx, 16);
    429 
    430 	isc_ht_init(&tomod, target->catzs->mctx, 16);
    431 
    432 	isc_ht_iter_create(newzone->entries, &iter1);
    433 
    434 	isc_ht_iter_create(target->entries, &iter2);
    435 
    436 	/*
    437 	 * We can create those iterators now, even though toadd and tomod are
    438 	 * empty
    439 	 */
    440 	isc_ht_iter_create(toadd, &iteradd);
    441 
    442 	isc_ht_iter_create(tomod, &itermod);
    443 
    444 	/*
    445 	 * First - walk the new zone and find all nodes that are not in the
    446 	 * old zone, or are in both zones and are modified.
    447 	 */
    448 	for (result = isc_ht_iter_first(iter1); result == ISC_R_SUCCESS;
    449 	     result = delcur ? isc_ht_iter_delcurrent_next(iter1)
    450 			     : isc_ht_iter_next(iter1))
    451 	{
    452 		dns_catz_entry_t *nentry = NULL;
    453 		dns_catz_entry_t *oentry = NULL;
    454 		dns_zone_t *zone = NULL;
    455 		unsigned char *key = NULL;
    456 		size_t keysize;
    457 		delcur = false;
    458 
    459 		isc_ht_iter_current(iter1, (void **)&nentry);
    460 		isc_ht_iter_currentkey(iter1, &key, &keysize);
    461 
    462 		/*
    463 		 * Spurious record that came from suboption without main
    464 		 * record, removed.
    465 		 * xxxwpk: make it a separate verification phase?
    466 		 */
    467 		if (dns_name_countlabels(&nentry->name) == 0) {
    468 			dns_catz_entry_detach(newzone, &nentry);
    469 			delcur = true;
    470 			continue;
    471 		}
    472 
    473 		dns_name_format(&nentry->name, zname, DNS_NAME_FORMATSIZE);
    474 
    475 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
    476 			      DNS_LOGMODULE_MASTER, ISC_LOG_DEBUG(3),
    477 			      "catz: iterating over '%s' from catalog '%s'",
    478 			      zname, czname);
    479 		dns_catz_options_setdefault(target->catzs->mctx,
    480 					    &target->zoneoptions,
    481 					    &nentry->opts);
    482 
    483 		result = isc_ht_find(target->entries, key, (uint32_t)keysize,
    484 				     (void **)&oentry);
    485 		if (result != ISC_R_SUCCESS) {
    486 			catz_entry_add_or_mod(target, toadd, key, keysize,
    487 					      nentry, NULL, "adding", zname,
    488 					      czname);
    489 			continue;
    490 		}
    491 
    492 		result = dns_zt_find(target->catzs->view->zonetable,
    493 				     dns_catz_entry_getname(nentry), 0, NULL,
    494 				     &zone);
    495 		if (result != ISC_R_SUCCESS) {
    496 			isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
    497 				      DNS_LOGMODULE_MASTER, ISC_LOG_DEBUG(3),
    498 				      "catz: zone '%s' was expected to exist "
    499 				      "but can not be found, will be restored",
    500 				      zname);
    501 			catz_entry_add_or_mod(target, toadd, key, keysize,
    502 					      nentry, oentry, "adding", zname,
    503 					      czname);
    504 			continue;
    505 		}
    506 		dns_zone_detach(&zone);
    507 
    508 		if (dns_catz_entry_cmp(oentry, nentry) != true) {
    509 			catz_entry_add_or_mod(target, tomod, key, keysize,
    510 					      nentry, oentry, "modifying",
    511 					      zname, czname);
    512 			continue;
    513 		}
    514 
    515 		/*
    516 		 * Delete the old entry so that it won't accidentally be
    517 		 * removed as a non-existing entry below.
    518 		 */
    519 		dns_catz_entry_detach(target, &oentry);
    520 		result = isc_ht_delete(target->entries, key, (uint32_t)keysize);
    521 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
    522 	}
    523 	RUNTIME_CHECK(result == ISC_R_NOMORE);
    524 	isc_ht_iter_destroy(&iter1);
    525 
    526 	/*
    527 	 * Then - walk the old zone; only deleted entries should remain.
    528 	 */
    529 	for (result = isc_ht_iter_first(iter2); result == ISC_R_SUCCESS;
    530 	     result = isc_ht_iter_delcurrent_next(iter2))
    531 	{
    532 		dns_catz_entry_t *entry = NULL;
    533 		isc_ht_iter_current(iter2, (void **)&entry);
    534 
    535 		dns_name_format(&entry->name, zname, DNS_NAME_FORMATSIZE);
    536 		result = delzone(entry, target, target->catzs->view,
    537 				 target->catzs->taskmgr,
    538 				 target->catzs->zmm->udata);
    539 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
    540 			      DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
    541 			      "catz: deleting zone '%s' from catalog '%s' - %s",
    542 			      zname, czname, isc_result_totext(result));
    543 		dns_catz_entry_detach(target, &entry);
    544 	}
    545 	RUNTIME_CHECK(result == ISC_R_NOMORE);
    546 	isc_ht_iter_destroy(&iter2);
    547 	/* At this moment target->entries has to be be empty. */
    548 	INSIST(isc_ht_count(target->entries) == 0);
    549 	isc_ht_destroy(&target->entries);
    550 
    551 	for (result = isc_ht_iter_first(iteradd); result == ISC_R_SUCCESS;
    552 	     result = isc_ht_iter_delcurrent_next(iteradd))
    553 	{
    554 		dns_catz_entry_t *entry = NULL;
    555 		isc_ht_iter_current(iteradd, (void **)&entry);
    556 
    557 		dns_name_format(&entry->name, zname, DNS_NAME_FORMATSIZE);
    558 		result = addzone(entry, target, target->catzs->view,
    559 				 target->catzs->taskmgr,
    560 				 target->catzs->zmm->udata);
    561 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
    562 			      DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
    563 			      "catz: adding zone '%s' from catalog "
    564 			      "'%s' - %s",
    565 			      zname, czname, isc_result_totext(result));
    566 	}
    567 
    568 	for (result = isc_ht_iter_first(itermod); result == ISC_R_SUCCESS;
    569 	     result = isc_ht_iter_delcurrent_next(itermod))
    570 	{
    571 		dns_catz_entry_t *entry = NULL;
    572 		isc_ht_iter_current(itermod, (void **)&entry);
    573 
    574 		dns_name_format(&entry->name, zname, DNS_NAME_FORMATSIZE);
    575 		result = modzone(entry, target, target->catzs->view,
    576 				 target->catzs->taskmgr,
    577 				 target->catzs->zmm->udata);
    578 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
    579 			      DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
    580 			      "catz: modifying zone '%s' from catalog "
    581 			      "'%s' - %s",
    582 			      zname, czname, isc_result_totext(result));
    583 	}
    584 
    585 	target->entries = newzone->entries;
    586 	newzone->entries = NULL;
    587 
    588 	result = ISC_R_SUCCESS;
    589 
    590 	isc_ht_iter_destroy(&iteradd);
    591 	isc_ht_iter_destroy(&itermod);
    592 	isc_ht_destroy(&toadd);
    593 	isc_ht_destroy(&tomod);
    594 
    595 	return (result);
    596 }
    597 
    598 isc_result_t
    599 dns_catz_new_zones(dns_catz_zones_t **catzsp, dns_catz_zonemodmethods_t *zmm,
    600 		   isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
    601 		   isc_timermgr_t *timermgr) {
    602 	dns_catz_zones_t *new_zones;
    603 	isc_result_t result;
    604 
    605 	REQUIRE(catzsp != NULL && *catzsp == NULL);
    606 	REQUIRE(zmm != NULL);
    607 
    608 	new_zones = isc_mem_get(mctx, sizeof(*new_zones));
    609 	memset(new_zones, 0, sizeof(*new_zones));
    610 
    611 	isc_mutex_init(&new_zones->lock);
    612 
    613 	isc_refcount_init(&new_zones->refs, 1);
    614 
    615 	isc_ht_init(&new_zones->zones, mctx, 4);
    616 
    617 	isc_mem_attach(mctx, &new_zones->mctx);
    618 	new_zones->zmm = zmm;
    619 	new_zones->timermgr = timermgr;
    620 	new_zones->taskmgr = taskmgr;
    621 
    622 	result = isc_task_create(taskmgr, 0, &new_zones->updater);
    623 	if (result != ISC_R_SUCCESS) {
    624 		goto cleanup_ht;
    625 	}
    626 	new_zones->magic = DNS_CATZ_ZONES_MAGIC;
    627 
    628 	*catzsp = new_zones;
    629 	return (ISC_R_SUCCESS);
    630 
    631 cleanup_ht:
    632 	isc_ht_destroy(&new_zones->zones);
    633 	isc_refcount_destroy(&new_zones->refs);
    634 	isc_mutex_destroy(&new_zones->lock);
    635 	isc_mem_put(mctx, new_zones, sizeof(*new_zones));
    636 
    637 	return (result);
    638 }
    639 
    640 void
    641 dns_catz_catzs_set_view(dns_catz_zones_t *catzs, dns_view_t *view) {
    642 	REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
    643 	REQUIRE(DNS_VIEW_VALID(view));
    644 	/* Either it's a new one or it's being reconfigured. */
    645 	REQUIRE(catzs->view == NULL || !strcmp(catzs->view->name, view->name));
    646 
    647 	catzs->view = view;
    648 }
    649 
    650 isc_result_t
    651 dns_catz_new_zone(dns_catz_zones_t *catzs, dns_catz_zone_t **zonep,
    652 		  const dns_name_t *name) {
    653 	isc_result_t result;
    654 	dns_catz_zone_t *new_zone;
    655 
    656 	REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
    657 	REQUIRE(zonep != NULL && *zonep == NULL);
    658 	REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
    659 
    660 	new_zone = isc_mem_get(catzs->mctx, sizeof(*new_zone));
    661 
    662 	memset(new_zone, 0, sizeof(*new_zone));
    663 
    664 	dns_name_init(&new_zone->name, NULL);
    665 	dns_name_dup(name, catzs->mctx, &new_zone->name);
    666 
    667 	isc_ht_init(&new_zone->entries, catzs->mctx, 16);
    668 
    669 	new_zone->updatetimer = NULL;
    670 	result = isc_timer_create(catzs->timermgr, isc_timertype_inactive, NULL,
    671 				  NULL, catzs->updater,
    672 				  dns_catz_update_taskaction, new_zone,
    673 				  &new_zone->updatetimer);
    674 	if (result != ISC_R_SUCCESS) {
    675 		goto cleanup_ht;
    676 	}
    677 
    678 	isc_time_settoepoch(&new_zone->lastupdated);
    679 	new_zone->updatepending = false;
    680 	new_zone->db = NULL;
    681 	new_zone->dbversion = NULL;
    682 	new_zone->catzs = catzs;
    683 	dns_catz_options_init(&new_zone->defoptions);
    684 	dns_catz_options_init(&new_zone->zoneoptions);
    685 	new_zone->active = true;
    686 	new_zone->db_registered = false;
    687 	new_zone->version = (uint32_t)(-1);
    688 	isc_refcount_init(&new_zone->refs, 1);
    689 	new_zone->magic = DNS_CATZ_ZONE_MAGIC;
    690 
    691 	*zonep = new_zone;
    692 
    693 	return (ISC_R_SUCCESS);
    694 
    695 cleanup_ht:
    696 	isc_ht_destroy(&new_zone->entries);
    697 	dns_name_free(&new_zone->name, catzs->mctx);
    698 	isc_mem_put(catzs->mctx, new_zone, sizeof(*new_zone));
    699 
    700 	return (result);
    701 }
    702 
    703 isc_result_t
    704 dns_catz_add_zone(dns_catz_zones_t *catzs, const dns_name_t *name,
    705 		  dns_catz_zone_t **zonep) {
    706 	dns_catz_zone_t *new_zone = NULL;
    707 	isc_result_t result, tresult;
    708 	char zname[DNS_NAME_FORMATSIZE];
    709 
    710 	REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
    711 	REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
    712 	REQUIRE(zonep != NULL && *zonep == NULL);
    713 
    714 	dns_name_format(name, zname, DNS_NAME_FORMATSIZE);
    715 	isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER,
    716 		      ISC_LOG_DEBUG(3), "catz: dns_catz_add_zone %s", zname);
    717 
    718 	LOCK(&catzs->lock);
    719 
    720 	result = dns_catz_new_zone(catzs, &new_zone, name);
    721 	if (result != ISC_R_SUCCESS) {
    722 		goto cleanup;
    723 	}
    724 
    725 	result = isc_ht_add(catzs->zones, new_zone->name.ndata,
    726 			    new_zone->name.length, new_zone);
    727 	if (result != ISC_R_SUCCESS) {
    728 		dns_catz_zone_detach(&new_zone);
    729 		if (result != ISC_R_EXISTS) {
    730 			goto cleanup;
    731 		}
    732 	}
    733 
    734 	if (result == ISC_R_EXISTS) {
    735 		tresult = isc_ht_find(catzs->zones, name->ndata, name->length,
    736 				      (void **)&new_zone);
    737 		INSIST(tresult == ISC_R_SUCCESS && !new_zone->active);
    738 		new_zone->active = true;
    739 	}
    740 
    741 	*zonep = new_zone;
    742 
    743 cleanup:
    744 	UNLOCK(&catzs->lock);
    745 
    746 	return (result);
    747 }
    748 
    749 dns_catz_zone_t *
    750 dns_catz_get_zone(dns_catz_zones_t *catzs, const dns_name_t *name) {
    751 	isc_result_t result;
    752 	dns_catz_zone_t *found = NULL;
    753 
    754 	REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
    755 	REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
    756 
    757 	result = isc_ht_find(catzs->zones, name->ndata, name->length,
    758 			     (void **)&found);
    759 	if (result != ISC_R_SUCCESS) {
    760 		return (NULL);
    761 	}
    762 
    763 	return (found);
    764 }
    765 
    766 void
    767 dns_catz_catzs_attach(dns_catz_zones_t *catzs, dns_catz_zones_t **catzsp) {
    768 	REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
    769 	REQUIRE(catzsp != NULL && *catzsp == NULL);
    770 
    771 	isc_refcount_increment(&catzs->refs);
    772 	*catzsp = catzs;
    773 }
    774 
    775 void
    776 dns_catz_zone_attach(dns_catz_zone_t *zone, dns_catz_zone_t **zonep) {
    777 	REQUIRE(zonep != NULL && *zonep == NULL);
    778 
    779 	isc_refcount_increment(&zone->refs);
    780 	*zonep = zone;
    781 }
    782 
    783 void
    784 dns_catz_zone_detach(dns_catz_zone_t **zonep) {
    785 	REQUIRE(zonep != NULL && *zonep != NULL);
    786 	dns_catz_zone_t *zone = *zonep;
    787 	*zonep = NULL;
    788 
    789 	if (isc_refcount_decrement(&zone->refs) == 1) {
    790 		isc_mem_t *mctx = zone->catzs->mctx;
    791 		isc_refcount_destroy(&zone->refs);
    792 		if (zone->entries != NULL) {
    793 			isc_ht_iter_t *iter = NULL;
    794 			isc_result_t result;
    795 			isc_ht_iter_create(zone->entries, &iter);
    796 			for (result = isc_ht_iter_first(iter);
    797 			     result == ISC_R_SUCCESS;
    798 			     result = isc_ht_iter_delcurrent_next(iter))
    799 			{
    800 				dns_catz_entry_t *entry = NULL;
    801 
    802 				isc_ht_iter_current(iter, (void **)&entry);
    803 				dns_catz_entry_detach(zone, &entry);
    804 			}
    805 			INSIST(result == ISC_R_NOMORE);
    806 			isc_ht_iter_destroy(&iter);
    807 
    808 			/* The hashtable has to be empty now. */
    809 			INSIST(isc_ht_count(zone->entries) == 0);
    810 			isc_ht_destroy(&zone->entries);
    811 		}
    812 		zone->magic = 0;
    813 		isc_timer_detach(&zone->updatetimer);
    814 		if (zone->db_registered) {
    815 			dns_db_updatenotify_unregister(
    816 				zone->db, dns_catz_dbupdate_callback,
    817 				zone->catzs);
    818 		}
    819 		if (zone->dbversion) {
    820 			dns_db_closeversion(zone->db, &zone->dbversion, false);
    821 		}
    822 		if (zone->db != NULL) {
    823 			dns_db_detach(&zone->db);
    824 		}
    825 
    826 		dns_name_free(&zone->name, mctx);
    827 		dns_catz_options_free(&zone->defoptions, mctx);
    828 		dns_catz_options_free(&zone->zoneoptions, mctx);
    829 
    830 		zone->catzs = NULL;
    831 		isc_mem_put(mctx, zone, sizeof(dns_catz_zone_t));
    832 	}
    833 }
    834 
    835 void
    836 dns_catz_catzs_detach(dns_catz_zones_t **catzsp) {
    837 	dns_catz_zones_t *catzs;
    838 
    839 	REQUIRE(catzsp != NULL && DNS_CATZ_ZONES_VALID(*catzsp));
    840 
    841 	catzs = *catzsp;
    842 	*catzsp = NULL;
    843 
    844 	if (isc_refcount_decrement(&catzs->refs) == 1) {
    845 		catzs->magic = 0;
    846 		isc_task_destroy(&catzs->updater);
    847 		isc_mutex_destroy(&catzs->lock);
    848 		if (catzs->zones != NULL) {
    849 			isc_ht_iter_t *iter = NULL;
    850 			isc_result_t result;
    851 			isc_ht_iter_create(catzs->zones, &iter);
    852 			for (result = isc_ht_iter_first(iter);
    853 			     result == ISC_R_SUCCESS;)
    854 			{
    855 				dns_catz_zone_t *zone = NULL;
    856 				isc_ht_iter_current(iter, (void **)&zone);
    857 				result = isc_ht_iter_delcurrent_next(iter);
    858 				dns_catz_zone_detach(&zone);
    859 			}
    860 			INSIST(result == ISC_R_NOMORE);
    861 			isc_ht_iter_destroy(&iter);
    862 			INSIST(isc_ht_count(catzs->zones) == 0);
    863 			isc_ht_destroy(&catzs->zones);
    864 		}
    865 		isc_refcount_destroy(&catzs->refs);
    866 		isc_mem_putanddetach(&catzs->mctx, catzs, sizeof(*catzs));
    867 	}
    868 }
    869 
    870 typedef enum {
    871 	CATZ_OPT_NONE,
    872 	CATZ_OPT_ZONES,
    873 	CATZ_OPT_MASTERS,
    874 	CATZ_OPT_ALLOW_QUERY,
    875 	CATZ_OPT_ALLOW_TRANSFER,
    876 	CATZ_OPT_VERSION,
    877 } catz_opt_t;
    878 
    879 static bool
    880 catz_opt_cmp(const dns_label_t *option, const char *opt) {
    881 	unsigned int l = strlen(opt);
    882 	if (option->length - 1 == l &&
    883 	    memcmp(opt, option->base + 1, l - 1) == 0)
    884 	{
    885 		return (true);
    886 	} else {
    887 		return (false);
    888 	}
    889 }
    890 
    891 static catz_opt_t
    892 catz_get_option(const dns_label_t *option) {
    893 	if (catz_opt_cmp(option, "zones")) {
    894 		return (CATZ_OPT_ZONES);
    895 	} else if (catz_opt_cmp(option, "masters")) {
    896 		return (CATZ_OPT_MASTERS);
    897 	} else if (catz_opt_cmp(option, "allow-query")) {
    898 		return (CATZ_OPT_ALLOW_QUERY);
    899 	} else if (catz_opt_cmp(option, "allow-transfer")) {
    900 		return (CATZ_OPT_ALLOW_TRANSFER);
    901 	} else if (catz_opt_cmp(option, "version")) {
    902 		return (CATZ_OPT_VERSION);
    903 	} else {
    904 		return (CATZ_OPT_NONE);
    905 	}
    906 }
    907 
    908 static isc_result_t
    909 catz_process_zones(dns_catz_zone_t *zone, dns_rdataset_t *value,
    910 		   dns_name_t *name) {
    911 	dns_label_t mhash;
    912 	dns_name_t opt;
    913 
    914 	REQUIRE(DNS_CATZ_ZONE_VALID(zone));
    915 	REQUIRE(DNS_RDATASET_VALID(value));
    916 	REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
    917 
    918 	if (value->rdclass != dns_rdataclass_in) {
    919 		return (ISC_R_FAILURE);
    920 	}
    921 
    922 	if (name->labels == 0) {
    923 		return (ISC_R_FAILURE);
    924 	}
    925 
    926 	dns_name_getlabel(name, name->labels - 1, &mhash);
    927 
    928 	if (name->labels == 1) {
    929 		return (catz_process_zones_entry(zone, value, &mhash));
    930 	} else {
    931 		dns_name_init(&opt, NULL);
    932 		dns_name_split(name, 1, &opt, NULL);
    933 		return (catz_process_zones_suboption(zone, value, &mhash,
    934 						     &opt));
    935 	}
    936 }
    937 
    938 static isc_result_t
    939 catz_process_zones_entry(dns_catz_zone_t *zone, dns_rdataset_t *value,
    940 			 dns_label_t *mhash) {
    941 	isc_result_t result;
    942 	dns_rdata_t rdata;
    943 	dns_rdata_ptr_t ptr;
    944 	dns_catz_entry_t *entry = NULL;
    945 
    946 	/*
    947 	 * We only take -first- value, as mhash must be
    948 	 * different.
    949 	 */
    950 	if (value->type != dns_rdatatype_ptr) {
    951 		return (ISC_R_FAILURE);
    952 	}
    953 
    954 	result = dns_rdataset_first(value);
    955 	if (result != ISC_R_SUCCESS) {
    956 		return (ISC_R_FAILURE);
    957 	}
    958 
    959 	dns_rdata_init(&rdata);
    960 	dns_rdataset_current(value, &rdata);
    961 
    962 	result = dns_rdata_tostruct(&rdata, &ptr, NULL);
    963 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
    964 
    965 	result = isc_ht_find(zone->entries, mhash->base, mhash->length,
    966 			     (void **)&entry);
    967 	if (result == ISC_R_SUCCESS) {
    968 		if (dns_name_countlabels(&entry->name) != 0) {
    969 			/* We have a duplicate. */
    970 			dns_rdata_freestruct(&ptr);
    971 			return (ISC_R_FAILURE);
    972 		} else {
    973 			dns_name_dup(&ptr.ptr, zone->catzs->mctx, &entry->name);
    974 		}
    975 	} else {
    976 		result = dns_catz_entry_new(zone->catzs->mctx, &ptr.ptr,
    977 					    &entry);
    978 		if (result != ISC_R_SUCCESS) {
    979 			dns_rdata_freestruct(&ptr);
    980 			return (result);
    981 		}
    982 
    983 		result = isc_ht_add(zone->entries, mhash->base, mhash->length,
    984 				    entry);
    985 		if (result != ISC_R_SUCCESS) {
    986 			dns_rdata_freestruct(&ptr);
    987 			dns_catz_entry_detach(zone, &entry);
    988 			return (result);
    989 		}
    990 	}
    991 
    992 	dns_rdata_freestruct(&ptr);
    993 
    994 	return (ISC_R_SUCCESS);
    995 }
    996 
    997 static isc_result_t
    998 catz_process_version(dns_catz_zone_t *zone, dns_rdataset_t *value) {
    999 	isc_result_t result;
   1000 	dns_rdata_t rdata;
   1001 	dns_rdata_txt_t rdatatxt;
   1002 	dns_rdata_txt_string_t rdatastr;
   1003 	uint32_t tversion;
   1004 	char t[16];
   1005 
   1006 	REQUIRE(DNS_CATZ_ZONE_VALID(zone));
   1007 	REQUIRE(DNS_RDATASET_VALID(value));
   1008 
   1009 	if (value->rdclass != dns_rdataclass_in ||
   1010 	    value->type != dns_rdatatype_txt)
   1011 	{
   1012 		return (ISC_R_FAILURE);
   1013 	}
   1014 
   1015 	result = dns_rdataset_first(value);
   1016 	if (result != ISC_R_SUCCESS) {
   1017 		return (result);
   1018 	}
   1019 
   1020 	dns_rdata_init(&rdata);
   1021 	dns_rdataset_current(value, &rdata);
   1022 
   1023 	result = dns_rdata_tostruct(&rdata, &rdatatxt, NULL);
   1024 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
   1025 
   1026 	result = dns_rdata_txt_first(&rdatatxt);
   1027 	if (result != ISC_R_SUCCESS) {
   1028 		goto cleanup;
   1029 	}
   1030 
   1031 	result = dns_rdata_txt_current(&rdatatxt, &rdatastr);
   1032 	if (result != ISC_R_SUCCESS) {
   1033 		goto cleanup;
   1034 	}
   1035 
   1036 	result = dns_rdata_txt_next(&rdatatxt);
   1037 	if (result != ISC_R_NOMORE) {
   1038 		result = ISC_R_FAILURE;
   1039 		goto cleanup;
   1040 	}
   1041 	if (rdatastr.length > 15) {
   1042 		result = ISC_R_BADNUMBER;
   1043 		goto cleanup;
   1044 	}
   1045 	memmove(t, rdatastr.data, rdatastr.length);
   1046 	t[rdatastr.length] = 0;
   1047 	result = isc_parse_uint32(&tversion, t, 10);
   1048 	if (result != ISC_R_SUCCESS) {
   1049 		goto cleanup;
   1050 	}
   1051 	zone->version = tversion;
   1052 	result = ISC_R_SUCCESS;
   1053 
   1054 cleanup:
   1055 	dns_rdata_freestruct(&rdatatxt);
   1056 	return (result);
   1057 }
   1058 
   1059 static isc_result_t
   1060 catz_process_masters(dns_catz_zone_t *zone, dns_ipkeylist_t *ipkl,
   1061 		     dns_rdataset_t *value, dns_name_t *name) {
   1062 	isc_result_t result;
   1063 	dns_rdata_t rdata;
   1064 	dns_rdata_in_a_t rdata_a;
   1065 	dns_rdata_in_aaaa_t rdata_aaaa;
   1066 	dns_rdata_txt_t rdata_txt;
   1067 	dns_rdata_txt_string_t rdatastr;
   1068 	dns_name_t *keyname = NULL;
   1069 	isc_mem_t *mctx;
   1070 	char keycbuf[DNS_NAME_FORMATSIZE];
   1071 	isc_buffer_t keybuf;
   1072 	unsigned int rcount;
   1073 	unsigned int i;
   1074 
   1075 	REQUIRE(DNS_CATZ_ZONE_VALID(zone));
   1076 	REQUIRE(ipkl != NULL);
   1077 	REQUIRE(DNS_RDATASET_VALID(value));
   1078 	REQUIRE(dns_rdataset_isassociated(value));
   1079 	REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
   1080 
   1081 	mctx = zone->catzs->mctx;
   1082 	memset(&rdata_a, 0, sizeof(rdata_a));
   1083 	memset(&rdata_aaaa, 0, sizeof(rdata_aaaa));
   1084 	memset(&rdata_txt, 0, sizeof(rdata_txt));
   1085 	isc_buffer_init(&keybuf, keycbuf, sizeof(keycbuf));
   1086 
   1087 	/*
   1088 	 * We have three possibilities here:
   1089 	 * - either empty name and IN A/IN AAAA record
   1090 	 * - label and IN A/IN AAAA
   1091 	 * - label and IN TXT - TSIG key name
   1092 	 */
   1093 	if (value->rdclass != dns_rdataclass_in) {
   1094 		return (ISC_R_FAILURE);
   1095 	}
   1096 
   1097 	if (name->labels > 0) {
   1098 		isc_sockaddr_t sockaddr;
   1099 
   1100 		/*
   1101 		 * We're pre-preparing the data once, we'll put it into
   1102 		 * the right spot in the masters array once we find it.
   1103 		 */
   1104 		result = dns_rdataset_first(value);
   1105 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
   1106 		dns_rdata_init(&rdata);
   1107 		dns_rdataset_current(value, &rdata);
   1108 		switch (value->type) {
   1109 		case dns_rdatatype_a:
   1110 			result = dns_rdata_tostruct(&rdata, &rdata_a, NULL);
   1111 			RUNTIME_CHECK(result == ISC_R_SUCCESS);
   1112 			isc_sockaddr_fromin(&sockaddr, &rdata_a.in_addr, 0);
   1113 			break;
   1114 		case dns_rdatatype_aaaa:
   1115 			result = dns_rdata_tostruct(&rdata, &rdata_aaaa, NULL);
   1116 			RUNTIME_CHECK(result == ISC_R_SUCCESS);
   1117 			isc_sockaddr_fromin6(&sockaddr, &rdata_aaaa.in6_addr,
   1118 					     0);
   1119 			break;
   1120 		case dns_rdatatype_txt:
   1121 			result = dns_rdata_tostruct(&rdata, &rdata_txt, NULL);
   1122 			RUNTIME_CHECK(result == ISC_R_SUCCESS);
   1123 
   1124 			result = dns_rdata_txt_first(&rdata_txt);
   1125 			if (result != ISC_R_SUCCESS) {
   1126 				return (result);
   1127 			}
   1128 
   1129 			result = dns_rdata_txt_current(&rdata_txt, &rdatastr);
   1130 			if (result != ISC_R_SUCCESS) {
   1131 				return (result);
   1132 			}
   1133 
   1134 			result = dns_rdata_txt_next(&rdata_txt);
   1135 			if (result != ISC_R_NOMORE) {
   1136 				return (ISC_R_FAILURE);
   1137 			}
   1138 
   1139 			/* rdatastr.length < DNS_NAME_MAXTEXT */
   1140 			keyname = isc_mem_get(mctx, sizeof(dns_name_t));
   1141 			dns_name_init(keyname, 0);
   1142 			memmove(keycbuf, rdatastr.data, rdatastr.length);
   1143 			keycbuf[rdatastr.length] = 0;
   1144 			result = dns_name_fromstring(keyname, keycbuf, 0, mctx);
   1145 			if (result != ISC_R_SUCCESS) {
   1146 				dns_name_free(keyname, mctx);
   1147 				isc_mem_put(mctx, keyname, sizeof(dns_name_t));
   1148 				return (result);
   1149 			}
   1150 			break;
   1151 		default:
   1152 			return (ISC_R_FAILURE);
   1153 		}
   1154 
   1155 		/*
   1156 		 * We have to find the appropriate labeled record in masters
   1157 		 * if it exists.
   1158 		 * In common case we'll have no more than 3-4 records here so
   1159 		 * no optimization.
   1160 		 */
   1161 		for (i = 0; i < ipkl->count; i++) {
   1162 			if (ipkl->labels[i] != NULL &&
   1163 			    !dns_name_compare(name, ipkl->labels[i]))
   1164 			{
   1165 				break;
   1166 			}
   1167 		}
   1168 
   1169 		if (i < ipkl->count) { /* we have this record already */
   1170 			if (value->type == dns_rdatatype_txt) {
   1171 				ipkl->keys[i] = keyname;
   1172 			} else { /* A/AAAA */
   1173 				memmove(&ipkl->addrs[i], &sockaddr,
   1174 					sizeof(isc_sockaddr_t));
   1175 			}
   1176 		} else {
   1177 			result = dns_ipkeylist_resize(mctx, ipkl, i + 1);
   1178 			if (result != ISC_R_SUCCESS) {
   1179 				return (result);
   1180 			}
   1181 
   1182 			ipkl->labels[i] = isc_mem_get(mctx, sizeof(dns_name_t));
   1183 			dns_name_init(ipkl->labels[i], NULL);
   1184 			dns_name_dup(name, mctx, ipkl->labels[i]);
   1185 
   1186 			if (value->type == dns_rdatatype_txt) {
   1187 				ipkl->keys[i] = keyname;
   1188 			} else { /* A/AAAA */
   1189 				memmove(&ipkl->addrs[i], &sockaddr,
   1190 					sizeof(isc_sockaddr_t));
   1191 			}
   1192 			ipkl->count++;
   1193 		}
   1194 		return (ISC_R_SUCCESS);
   1195 	}
   1196 	/* else - 'simple' case - without labels */
   1197 
   1198 	if (value->type != dns_rdatatype_a && value->type != dns_rdatatype_aaaa)
   1199 	{
   1200 		return (ISC_R_FAILURE);
   1201 	}
   1202 
   1203 	rcount = dns_rdataset_count(value) + ipkl->count;
   1204 
   1205 	result = dns_ipkeylist_resize(mctx, ipkl, rcount);
   1206 	if (result != ISC_R_SUCCESS) {
   1207 		return (result);
   1208 	}
   1209 
   1210 	for (result = dns_rdataset_first(value); result == ISC_R_SUCCESS;
   1211 	     result = dns_rdataset_next(value))
   1212 	{
   1213 		dns_rdata_init(&rdata);
   1214 		dns_rdataset_current(value, &rdata);
   1215 		/*
   1216 		 * port 0 == take the default
   1217 		 */
   1218 		if (value->type == dns_rdatatype_a) {
   1219 			result = dns_rdata_tostruct(&rdata, &rdata_a, NULL);
   1220 			RUNTIME_CHECK(result == ISC_R_SUCCESS);
   1221 			isc_sockaddr_fromin(&ipkl->addrs[ipkl->count],
   1222 					    &rdata_a.in_addr, 0);
   1223 		} else {
   1224 			result = dns_rdata_tostruct(&rdata, &rdata_aaaa, NULL);
   1225 			RUNTIME_CHECK(result == ISC_R_SUCCESS);
   1226 			isc_sockaddr_fromin6(&ipkl->addrs[ipkl->count],
   1227 					     &rdata_aaaa.in6_addr, 0);
   1228 		}
   1229 		ipkl->keys[ipkl->count] = NULL;
   1230 		ipkl->labels[ipkl->count] = NULL;
   1231 		ipkl->count++;
   1232 		dns_rdata_freestruct(&rdata_a);
   1233 	}
   1234 	return (ISC_R_SUCCESS);
   1235 }
   1236 
   1237 static isc_result_t
   1238 catz_process_apl(dns_catz_zone_t *zone, isc_buffer_t **aclbp,
   1239 		 dns_rdataset_t *value) {
   1240 	isc_result_t result = ISC_R_SUCCESS;
   1241 	dns_rdata_t rdata;
   1242 	dns_rdata_in_apl_t rdata_apl;
   1243 	dns_rdata_apl_ent_t apl_ent;
   1244 	isc_netaddr_t addr;
   1245 	isc_buffer_t *aclb = NULL;
   1246 	unsigned char buf[256]; /* larger than INET6_ADDRSTRLEN */
   1247 
   1248 	REQUIRE(DNS_CATZ_ZONE_VALID(zone));
   1249 	REQUIRE(aclbp != NULL);
   1250 	REQUIRE(*aclbp == NULL);
   1251 	REQUIRE(DNS_RDATASET_VALID(value));
   1252 	REQUIRE(dns_rdataset_isassociated(value));
   1253 
   1254 	if (value->rdclass != dns_rdataclass_in ||
   1255 	    value->type != dns_rdatatype_apl)
   1256 	{
   1257 		return (ISC_R_FAILURE);
   1258 	}
   1259 
   1260 	if (dns_rdataset_count(value) > 1) {
   1261 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
   1262 			      DNS_LOGMODULE_MASTER, ISC_LOG_WARNING,
   1263 			      "catz: more than one APL entry for member zone, "
   1264 			      "result is undefined");
   1265 	}
   1266 	result = dns_rdataset_first(value);
   1267 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
   1268 	dns_rdata_init(&rdata);
   1269 	dns_rdataset_current(value, &rdata);
   1270 	result = dns_rdata_tostruct(&rdata, &rdata_apl, zone->catzs->mctx);
   1271 	if (result != ISC_R_SUCCESS) {
   1272 		return (result);
   1273 	}
   1274 	isc_buffer_allocate(zone->catzs->mctx, &aclb, 16);
   1275 	isc_buffer_setautorealloc(aclb, true);
   1276 	for (result = dns_rdata_apl_first(&rdata_apl); result == ISC_R_SUCCESS;
   1277 	     result = dns_rdata_apl_next(&rdata_apl))
   1278 	{
   1279 		result = dns_rdata_apl_current(&rdata_apl, &apl_ent);
   1280 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
   1281 		memset(buf, 0, sizeof(buf));
   1282 		if (apl_ent.data != NULL && apl_ent.length > 0) {
   1283 			memmove(buf, apl_ent.data, apl_ent.length);
   1284 		}
   1285 		if (apl_ent.family == 1) {
   1286 			isc_netaddr_fromin(&addr, (struct in_addr *)buf);
   1287 		} else if (apl_ent.family == 2) {
   1288 			isc_netaddr_fromin6(&addr, (struct in6_addr *)buf);
   1289 		} else {
   1290 			continue; /* xxxwpk log it or simply ignore? */
   1291 		}
   1292 		if (apl_ent.negative) {
   1293 			isc_buffer_putuint8(aclb, '!');
   1294 		}
   1295 		isc_buffer_reserve(&aclb, INET6_ADDRSTRLEN);
   1296 		result = isc_netaddr_totext(&addr, aclb);
   1297 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
   1298 		if ((apl_ent.family == 1 && apl_ent.prefix < 32) ||
   1299 		    (apl_ent.family == 2 && apl_ent.prefix < 128))
   1300 		{
   1301 			isc_buffer_putuint8(aclb, '/');
   1302 			isc_buffer_putdecint(aclb, apl_ent.prefix);
   1303 		}
   1304 		isc_buffer_putstr(aclb, "; ");
   1305 	}
   1306 	if (result == ISC_R_NOMORE) {
   1307 		result = ISC_R_SUCCESS;
   1308 	} else {
   1309 		goto cleanup;
   1310 	}
   1311 	*aclbp = aclb;
   1312 	aclb = NULL;
   1313 cleanup:
   1314 	if (aclb != NULL) {
   1315 		isc_buffer_free(&aclb);
   1316 	}
   1317 	dns_rdata_freestruct(&rdata_apl);
   1318 	return (result);
   1319 }
   1320 
   1321 static isc_result_t
   1322 catz_process_zones_suboption(dns_catz_zone_t *zone, dns_rdataset_t *value,
   1323 			     dns_label_t *mhash, dns_name_t *name) {
   1324 	isc_result_t result;
   1325 	dns_catz_entry_t *entry = NULL;
   1326 	dns_label_t option;
   1327 	dns_name_t prefix;
   1328 	catz_opt_t opt;
   1329 
   1330 	REQUIRE(DNS_CATZ_ZONE_VALID(zone));
   1331 	REQUIRE(mhash != NULL);
   1332 	REQUIRE(DNS_RDATASET_VALID(value));
   1333 	REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
   1334 
   1335 	if (name->labels == 0) {
   1336 		return (ISC_R_FAILURE);
   1337 	}
   1338 	dns_name_getlabel(name, name->labels - 1, &option);
   1339 	opt = catz_get_option(&option);
   1340 
   1341 	/*
   1342 	 * We're adding this entry now, in case the option is invalid we'll get
   1343 	 * rid of it in verification phase.
   1344 	 */
   1345 	result = isc_ht_find(zone->entries, mhash->base, mhash->length,
   1346 			     (void **)&entry);
   1347 	if (result != ISC_R_SUCCESS) {
   1348 		result = dns_catz_entry_new(zone->catzs->mctx, NULL, &entry);
   1349 		if (result != ISC_R_SUCCESS) {
   1350 			return (result);
   1351 		}
   1352 		result = isc_ht_add(zone->entries, mhash->base, mhash->length,
   1353 				    entry);
   1354 		if (result != ISC_R_SUCCESS) {
   1355 			dns_catz_entry_detach(zone, &entry);
   1356 			return (result);
   1357 		}
   1358 	}
   1359 
   1360 	dns_name_init(&prefix, NULL);
   1361 	dns_name_split(name, 1, &prefix, NULL);
   1362 	switch (opt) {
   1363 	case CATZ_OPT_MASTERS:
   1364 		return (catz_process_masters(zone, &entry->opts.masters, value,
   1365 					     &prefix));
   1366 	case CATZ_OPT_ALLOW_QUERY:
   1367 		if (prefix.labels != 0) {
   1368 			return (ISC_R_FAILURE);
   1369 		}
   1370 		return (catz_process_apl(zone, &entry->opts.allow_query,
   1371 					 value));
   1372 	case CATZ_OPT_ALLOW_TRANSFER:
   1373 		if (prefix.labels != 0) {
   1374 			return (ISC_R_FAILURE);
   1375 		}
   1376 		return (catz_process_apl(zone, &entry->opts.allow_transfer,
   1377 					 value));
   1378 	default:
   1379 		return (ISC_R_FAILURE);
   1380 	}
   1381 
   1382 	return (ISC_R_FAILURE);
   1383 }
   1384 
   1385 static void
   1386 catz_entry_add_or_mod(dns_catz_zone_t *target, isc_ht_t *ht, unsigned char *key,
   1387 		      size_t keysize, dns_catz_entry_t *nentry,
   1388 		      dns_catz_entry_t *oentry, const char *msg,
   1389 		      const char *zname, const char *czname) {
   1390 	isc_result_t result = isc_ht_add(ht, key, (uint32_t)keysize, nentry);
   1391 
   1392 	if (result != ISC_R_SUCCESS) {
   1393 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
   1394 			      DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
   1395 			      "catz: error %s zone '%s' from catalog '%s' - %s",
   1396 			      msg, zname, czname, isc_result_totext(result));
   1397 	}
   1398 	if (oentry != NULL) {
   1399 		dns_catz_entry_detach(target, &oentry);
   1400 		result = isc_ht_delete(target->entries, key, (uint32_t)keysize);
   1401 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
   1402 	}
   1403 }
   1404 
   1405 static isc_result_t
   1406 catz_process_value(dns_catz_zone_t *zone, dns_name_t *name,
   1407 		   dns_rdataset_t *rdataset) {
   1408 	dns_label_t option;
   1409 	dns_name_t prefix;
   1410 	catz_opt_t opt;
   1411 
   1412 	REQUIRE(DNS_CATZ_ZONE_VALID(zone));
   1413 	REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
   1414 	REQUIRE(DNS_RDATASET_VALID(rdataset));
   1415 
   1416 	dns_name_getlabel(name, name->labels - 1, &option);
   1417 	opt = catz_get_option(&option);
   1418 	dns_name_init(&prefix, NULL);
   1419 	dns_name_split(name, 1, &prefix, NULL);
   1420 	switch (opt) {
   1421 	case CATZ_OPT_ZONES:
   1422 		return (catz_process_zones(zone, rdataset, &prefix));
   1423 	case CATZ_OPT_MASTERS:
   1424 		return (catz_process_masters(zone, &zone->zoneoptions.masters,
   1425 					     rdataset, &prefix));
   1426 	case CATZ_OPT_ALLOW_QUERY:
   1427 		if (prefix.labels != 0) {
   1428 			return (ISC_R_FAILURE);
   1429 		}
   1430 		return (catz_process_apl(zone, &zone->zoneoptions.allow_query,
   1431 					 rdataset));
   1432 	case CATZ_OPT_ALLOW_TRANSFER:
   1433 		if (prefix.labels != 0) {
   1434 			return (ISC_R_FAILURE);
   1435 		}
   1436 		return (catz_process_apl(
   1437 			zone, &zone->zoneoptions.allow_transfer, rdataset));
   1438 	case CATZ_OPT_VERSION:
   1439 		if (prefix.labels != 0) {
   1440 			return (ISC_R_FAILURE);
   1441 		}
   1442 		return (catz_process_version(zone, rdataset));
   1443 	default:
   1444 		return (ISC_R_FAILURE);
   1445 	}
   1446 }
   1447 
   1448 isc_result_t
   1449 dns_catz_update_process(dns_catz_zones_t *catzs, dns_catz_zone_t *zone,
   1450 			const dns_name_t *src_name, dns_rdataset_t *rdataset) {
   1451 	isc_result_t result;
   1452 	int order;
   1453 	unsigned int nlabels;
   1454 	dns_namereln_t nrres;
   1455 	dns_rdata_t rdata = DNS_RDATA_INIT;
   1456 	dns_rdata_soa_t soa;
   1457 	dns_name_t prefix;
   1458 
   1459 	REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
   1460 	REQUIRE(DNS_CATZ_ZONE_VALID(zone));
   1461 	REQUIRE(ISC_MAGIC_VALID(src_name, DNS_NAME_MAGIC));
   1462 
   1463 	nrres = dns_name_fullcompare(src_name, &zone->name, &order, &nlabels);
   1464 	if (nrres == dns_namereln_equal) {
   1465 		if (rdataset->type == dns_rdatatype_soa) {
   1466 			result = dns_rdataset_first(rdataset);
   1467 			if (result != ISC_R_SUCCESS) {
   1468 				return (result);
   1469 			}
   1470 
   1471 			dns_rdataset_current(rdataset, &rdata);
   1472 			result = dns_rdata_tostruct(&rdata, &soa, NULL);
   1473 			RUNTIME_CHECK(result == ISC_R_SUCCESS);
   1474 
   1475 			/*
   1476 			 * xxxwpk TODO do we want to save something from SOA?
   1477 			 */
   1478 			return (result);
   1479 		} else if (rdataset->type == dns_rdatatype_ns) {
   1480 			return (ISC_R_SUCCESS);
   1481 		} else {
   1482 			return (ISC_R_UNEXPECTED);
   1483 		}
   1484 	} else if (nrres != dns_namereln_subdomain) {
   1485 		return (ISC_R_UNEXPECTED);
   1486 	}
   1487 
   1488 	dns_name_init(&prefix, NULL);
   1489 	dns_name_split(src_name, zone->name.labels, &prefix, NULL);
   1490 	result = catz_process_value(zone, &prefix, rdataset);
   1491 
   1492 	return (result);
   1493 }
   1494 
   1495 static isc_result_t
   1496 digest2hex(unsigned char *digest, unsigned int digestlen, char *hash,
   1497 	   size_t hashlen) {
   1498 	unsigned int i;
   1499 	int ret;
   1500 	for (i = 0; i < digestlen; i++) {
   1501 		size_t left = hashlen - i * 2;
   1502 		ret = snprintf(hash + i * 2, left, "%02x", digest[i]);
   1503 		if (ret < 0 || (size_t)ret >= left) {
   1504 			return (ISC_R_NOSPACE);
   1505 		}
   1506 	}
   1507 	return (ISC_R_SUCCESS);
   1508 }
   1509 
   1510 isc_result_t
   1511 dns_catz_generate_masterfilename(dns_catz_zone_t *zone, dns_catz_entry_t *entry,
   1512 				 isc_buffer_t **buffer) {
   1513 	isc_buffer_t *tbuf = NULL;
   1514 	isc_region_t r;
   1515 	isc_result_t result;
   1516 	size_t rlen;
   1517 	bool special = false;
   1518 
   1519 	REQUIRE(DNS_CATZ_ZONE_VALID(zone));
   1520 	REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
   1521 	REQUIRE(buffer != NULL && *buffer != NULL);
   1522 
   1523 	isc_buffer_allocate(zone->catzs->mctx, &tbuf,
   1524 			    strlen(zone->catzs->view->name) +
   1525 				    2 * DNS_NAME_FORMATSIZE + 2);
   1526 
   1527 	isc_buffer_putstr(tbuf, zone->catzs->view->name);
   1528 	isc_buffer_putstr(tbuf, "_");
   1529 	result = dns_name_totext(&zone->name, true, tbuf);
   1530 	if (result != ISC_R_SUCCESS) {
   1531 		goto cleanup;
   1532 	}
   1533 
   1534 	isc_buffer_putstr(tbuf, "_");
   1535 	result = dns_name_totext(&entry->name, true, tbuf);
   1536 	if (result != ISC_R_SUCCESS) {
   1537 		goto cleanup;
   1538 	}
   1539 
   1540 	/*
   1541 	 * Search for slash and other special characters in the view and
   1542 	 * zone names.  Add a null terminator so we can use strpbrk(), then
   1543 	 * remove it.
   1544 	 */
   1545 	isc_buffer_putuint8(tbuf, 0);
   1546 	if (strpbrk(isc_buffer_base(tbuf), "\\/:") != NULL) {
   1547 		special = true;
   1548 	}
   1549 	isc_buffer_subtract(tbuf, 1);
   1550 
   1551 	/* __catz__<digest>.db */
   1552 	rlen = (isc_md_type_get_size(ISC_MD_SHA256) * 2 + 1) + 12;
   1553 
   1554 	/* optionally prepend with <zonedir>/ */
   1555 	if (entry->opts.zonedir != NULL) {
   1556 		rlen += strlen(entry->opts.zonedir) + 1;
   1557 	}
   1558 
   1559 	result = isc_buffer_reserve(buffer, (unsigned int)rlen);
   1560 	if (result != ISC_R_SUCCESS) {
   1561 		goto cleanup;
   1562 	}
   1563 
   1564 	if (entry->opts.zonedir != NULL) {
   1565 		isc_buffer_putstr(*buffer, entry->opts.zonedir);
   1566 		isc_buffer_putstr(*buffer, "/");
   1567 	}
   1568 
   1569 	isc_buffer_usedregion(tbuf, &r);
   1570 	isc_buffer_putstr(*buffer, "__catz__");
   1571 	if (special || tbuf->used > ISC_SHA256_DIGESTLENGTH * 2 + 1) {
   1572 		unsigned char digest[ISC_MAX_MD_SIZE];
   1573 		unsigned int digestlen;
   1574 
   1575 		/* we can do that because digest string < 2 * DNS_NAME */
   1576 		result = isc_md(ISC_MD_SHA256, r.base, r.length, digest,
   1577 				&digestlen);
   1578 		if (result != ISC_R_SUCCESS) {
   1579 			goto cleanup;
   1580 		}
   1581 		result = digest2hex(digest, digestlen, (char *)r.base,
   1582 				    ISC_SHA256_DIGESTLENGTH * 2 + 1);
   1583 		if (result != ISC_R_SUCCESS) {
   1584 			goto cleanup;
   1585 		}
   1586 		isc_buffer_putstr(*buffer, (char *)r.base);
   1587 	} else {
   1588 		isc_buffer_copyregion(*buffer, &r);
   1589 	}
   1590 
   1591 	isc_buffer_putstr(*buffer, ".db");
   1592 	result = ISC_R_SUCCESS;
   1593 
   1594 cleanup:
   1595 	isc_buffer_free(&tbuf);
   1596 	return (result);
   1597 }
   1598 
   1599 isc_result_t
   1600 dns_catz_generate_zonecfg(dns_catz_zone_t *zone, dns_catz_entry_t *entry,
   1601 			  isc_buffer_t **buf) {
   1602 	/*
   1603 	 * We have to generate a text buffer with regular zone config:
   1604 	 * zone "foo.bar" {
   1605 	 * 	type slave;
   1606 	 * 	masters [ dscp X ] { ip1 port port1; ip2 port port2; };
   1607 	 * }
   1608 	 */
   1609 	isc_buffer_t *buffer = NULL;
   1610 	isc_region_t region;
   1611 	isc_result_t result;
   1612 	uint32_t i;
   1613 	isc_netaddr_t netaddr;
   1614 	char pbuf[sizeof("65535")]; /* used both for port number and DSCP */
   1615 	char zname[DNS_NAME_FORMATSIZE];
   1616 
   1617 	REQUIRE(DNS_CATZ_ZONE_VALID(zone));
   1618 	REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
   1619 	REQUIRE(buf != NULL && *buf == NULL);
   1620 
   1621 	/*
   1622 	 * The buffer will be reallocated if something won't fit,
   1623 	 * ISC_BUFFER_INCR seems like a good start.
   1624 	 */
   1625 	isc_buffer_allocate(zone->catzs->mctx, &buffer, ISC_BUFFER_INCR);
   1626 
   1627 	isc_buffer_setautorealloc(buffer, true);
   1628 	isc_buffer_putstr(buffer, "zone \"");
   1629 	dns_name_totext(&entry->name, true, buffer);
   1630 	isc_buffer_putstr(buffer, "\" { type slave; masters");
   1631 
   1632 	/*
   1633 	 * DSCP value has no default, but when it is specified, it is identical
   1634 	 * for all masters and cannot be overridden for a specific master IP, so
   1635 	 * use the DSCP value set for the first master
   1636 	 */
   1637 	if (entry->opts.masters.count > 0 && entry->opts.masters.dscps[0] >= 0)
   1638 	{
   1639 		isc_buffer_putstr(buffer, " dscp ");
   1640 		snprintf(pbuf, sizeof(pbuf), "%hd",
   1641 			 entry->opts.masters.dscps[0]);
   1642 		isc_buffer_putstr(buffer, pbuf);
   1643 	}
   1644 
   1645 	isc_buffer_putstr(buffer, " { ");
   1646 	for (i = 0; i < entry->opts.masters.count; i++) {
   1647 		/*
   1648 		 * Every master must have an IP address assigned.
   1649 		 */
   1650 		switch (entry->opts.masters.addrs[i].type.sa.sa_family) {
   1651 		case AF_INET:
   1652 		case AF_INET6:
   1653 			break;
   1654 		default:
   1655 			dns_name_format(&entry->name, zname,
   1656 					DNS_NAME_FORMATSIZE);
   1657 			isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
   1658 				      DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
   1659 				      "catz: zone '%s' uses an invalid master "
   1660 				      "(no IP address assigned)",
   1661 				      zname);
   1662 			result = ISC_R_FAILURE;
   1663 			goto cleanup;
   1664 		}
   1665 		isc_netaddr_fromsockaddr(&netaddr,
   1666 					 &entry->opts.masters.addrs[i]);
   1667 		isc_buffer_reserve(&buffer, INET6_ADDRSTRLEN);
   1668 		result = isc_netaddr_totext(&netaddr, buffer);
   1669 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
   1670 
   1671 		isc_buffer_putstr(buffer, " port ");
   1672 		snprintf(pbuf, sizeof(pbuf), "%u",
   1673 			 isc_sockaddr_getport(&entry->opts.masters.addrs[i]));
   1674 		isc_buffer_putstr(buffer, pbuf);
   1675 
   1676 		if (entry->opts.masters.keys[i] != NULL) {
   1677 			isc_buffer_putstr(buffer, " key ");
   1678 			result = dns_name_totext(entry->opts.masters.keys[i],
   1679 						 true, buffer);
   1680 			if (result != ISC_R_SUCCESS) {
   1681 				goto cleanup;
   1682 			}
   1683 		}
   1684 		isc_buffer_putstr(buffer, "; ");
   1685 	}
   1686 	isc_buffer_putstr(buffer, "}; ");
   1687 	if (!entry->opts.in_memory) {
   1688 		isc_buffer_putstr(buffer, "file \"");
   1689 		result = dns_catz_generate_masterfilename(zone, entry, &buffer);
   1690 		if (result != ISC_R_SUCCESS) {
   1691 			goto cleanup;
   1692 		}
   1693 		isc_buffer_putstr(buffer, "\"; ");
   1694 	}
   1695 	if (entry->opts.allow_query != NULL) {
   1696 		isc_buffer_putstr(buffer, "allow-query { ");
   1697 		isc_buffer_usedregion(entry->opts.allow_query, &region);
   1698 		isc_buffer_copyregion(buffer, &region);
   1699 		isc_buffer_putstr(buffer, "}; ");
   1700 	}
   1701 	if (entry->opts.allow_transfer != NULL) {
   1702 		isc_buffer_putstr(buffer, "allow-transfer { ");
   1703 		isc_buffer_usedregion(entry->opts.allow_transfer, &region);
   1704 		isc_buffer_copyregion(buffer, &region);
   1705 		isc_buffer_putstr(buffer, "}; ");
   1706 	}
   1707 
   1708 	isc_buffer_putstr(buffer, "};");
   1709 	*buf = buffer;
   1710 
   1711 	return (ISC_R_SUCCESS);
   1712 
   1713 cleanup:
   1714 	isc_buffer_free(&buffer);
   1715 	return (result);
   1716 }
   1717 
   1718 void
   1719 dns_catz_update_taskaction(isc_task_t *task, isc_event_t *event) {
   1720 	isc_result_t result;
   1721 	dns_catz_zone_t *zone;
   1722 	(void)task;
   1723 
   1724 	REQUIRE(event != NULL);
   1725 	zone = event->ev_arg;
   1726 	REQUIRE(DNS_CATZ_ZONE_VALID(zone));
   1727 
   1728 	LOCK(&zone->catzs->lock);
   1729 	zone->updatepending = false;
   1730 	dns_catz_update_from_db(zone->db, zone->catzs);
   1731 	result = isc_timer_reset(zone->updatetimer, isc_timertype_inactive,
   1732 				 NULL, NULL, true);
   1733 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
   1734 	isc_event_free(&event);
   1735 	result = isc_time_now(&zone->lastupdated);
   1736 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
   1737 	UNLOCK(&zone->catzs->lock);
   1738 }
   1739 
   1740 isc_result_t
   1741 dns_catz_dbupdate_callback(dns_db_t *db, void *fn_arg) {
   1742 	dns_catz_zones_t *catzs;
   1743 	dns_catz_zone_t *zone = NULL;
   1744 	isc_time_t now;
   1745 	uint64_t tdiff;
   1746 	isc_result_t result = ISC_R_SUCCESS;
   1747 	isc_region_t r;
   1748 
   1749 	REQUIRE(DNS_DB_VALID(db));
   1750 	REQUIRE(DNS_CATZ_ZONES_VALID(fn_arg));
   1751 	catzs = (dns_catz_zones_t *)fn_arg;
   1752 
   1753 	dns_name_toregion(&db->origin, &r);
   1754 
   1755 	LOCK(&catzs->lock);
   1756 	result = isc_ht_find(catzs->zones, r.base, r.length, (void **)&zone);
   1757 	if (result != ISC_R_SUCCESS) {
   1758 		goto cleanup;
   1759 	}
   1760 
   1761 	/* New zone came as AXFR */
   1762 	if (zone->db != NULL && zone->db != db) {
   1763 		if (zone->dbversion != NULL) {
   1764 			dns_db_closeversion(zone->db, &zone->dbversion, false);
   1765 		}
   1766 		dns_db_detach(&zone->db);
   1767 		/*
   1768 		 * We're not registering db update callback, it will be
   1769 		 * registered at the end of update_from_db
   1770 		 */
   1771 		zone->db_registered = false;
   1772 	}
   1773 	if (zone->db == NULL) {
   1774 		dns_db_attach(db, &zone->db);
   1775 	}
   1776 
   1777 	if (!zone->updatepending) {
   1778 		zone->updatepending = true;
   1779 		isc_time_now(&now);
   1780 		tdiff = isc_time_microdiff(&now, &zone->lastupdated) / 1000000;
   1781 		if (tdiff < zone->defoptions.min_update_interval) {
   1782 			isc_interval_t interval;
   1783 			isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
   1784 				      DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
   1785 				      "catz: new zone version came too soon, "
   1786 				      "deferring update");
   1787 			isc_interval_set(&interval,
   1788 					 zone->defoptions.min_update_interval -
   1789 						 (unsigned int)tdiff,
   1790 					 0);
   1791 			dns_db_currentversion(db, &zone->dbversion);
   1792 			result = isc_timer_reset(zone->updatetimer,
   1793 						 isc_timertype_once, NULL,
   1794 						 &interval, true);
   1795 			if (result != ISC_R_SUCCESS) {
   1796 				goto cleanup;
   1797 			}
   1798 		} else {
   1799 			isc_event_t *event;
   1800 
   1801 			dns_db_currentversion(db, &zone->dbversion);
   1802 			ISC_EVENT_INIT(&zone->updateevent,
   1803 				       sizeof(zone->updateevent), 0, NULL,
   1804 				       DNS_EVENT_CATZUPDATED,
   1805 				       dns_catz_update_taskaction, zone, zone,
   1806 				       NULL, NULL);
   1807 			event = &zone->updateevent;
   1808 			isc_task_send(catzs->updater, &event);
   1809 		}
   1810 	} else {
   1811 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
   1812 			      DNS_LOGMODULE_MASTER, ISC_LOG_DEBUG(3),
   1813 			      "catz: update already queued");
   1814 		if (zone->dbversion != NULL) {
   1815 			dns_db_closeversion(zone->db, &zone->dbversion, false);
   1816 		}
   1817 		dns_db_currentversion(zone->db, &zone->dbversion);
   1818 	}
   1819 
   1820 cleanup:
   1821 	UNLOCK(&catzs->lock);
   1822 
   1823 	return (result);
   1824 }
   1825 
   1826 static bool
   1827 catz_rdatatype_is_processable(const dns_rdatatype_t type) {
   1828 	return (!dns_rdatatype_isdnssec(type) && type != dns_rdatatype_cds &&
   1829 		type != dns_rdatatype_cdnskey && type != dns_rdatatype_zonemd);
   1830 }
   1831 
   1832 void
   1833 dns_catz_update_from_db(dns_db_t *db, dns_catz_zones_t *catzs) {
   1834 	dns_catz_zone_t *oldzone = NULL, *newzone = NULL;
   1835 	isc_result_t result;
   1836 	isc_region_t r;
   1837 	dns_dbnode_t *node = NULL;
   1838 	dns_dbiterator_t *it = NULL;
   1839 	dns_fixedname_t fixname;
   1840 	dns_name_t *name;
   1841 	dns_rdatasetiter_t *rdsiter = NULL;
   1842 	dns_rdataset_t rdataset;
   1843 	char bname[DNS_NAME_FORMATSIZE];
   1844 	isc_buffer_t ibname;
   1845 	uint32_t vers;
   1846 
   1847 	REQUIRE(DNS_DB_VALID(db));
   1848 	REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
   1849 
   1850 	/*
   1851 	 * Create a new catz in the same context as current catz.
   1852 	 */
   1853 	dns_name_toregion(&db->origin, &r);
   1854 	result = isc_ht_find(catzs->zones, r.base, r.length, (void **)&oldzone);
   1855 	if (result != ISC_R_SUCCESS) {
   1856 		/* This can happen if we remove the zone in the meantime. */
   1857 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
   1858 			      DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
   1859 			      "catz: zone '%s' not in config", bname);
   1860 		return;
   1861 	}
   1862 
   1863 	isc_buffer_init(&ibname, bname, DNS_NAME_FORMATSIZE);
   1864 	result = dns_name_totext(&db->origin, true, &ibname);
   1865 	INSIST(result == ISC_R_SUCCESS);
   1866 
   1867 	result = dns_db_getsoaserial(db, oldzone->dbversion, &vers);
   1868 	if (result != ISC_R_SUCCESS) {
   1869 		/* A zone without SOA record?!? */
   1870 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
   1871 			      DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
   1872 			      "catz: zone '%s' has no SOA record (%s)", bname,
   1873 			      isc_result_totext(result));
   1874 		return;
   1875 	}
   1876 
   1877 	isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER,
   1878 		      ISC_LOG_INFO,
   1879 		      "catz: updating catalog zone '%s' with serial %" PRIu32,
   1880 		      bname, vers);
   1881 
   1882 	result = dns_catz_new_zone(catzs, &newzone, &db->origin);
   1883 	if (result != ISC_R_SUCCESS) {
   1884 		dns_db_closeversion(db, &oldzone->dbversion, false);
   1885 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
   1886 			      DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
   1887 			      "catz: failed to create new zone - %s",
   1888 			      isc_result_totext(result));
   1889 		return;
   1890 	}
   1891 
   1892 	result = dns_db_createiterator(db, DNS_DB_NONSEC3, &it);
   1893 	if (result != ISC_R_SUCCESS) {
   1894 		dns_catz_zone_detach(&newzone);
   1895 		dns_db_closeversion(db, &oldzone->dbversion, false);
   1896 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
   1897 			      DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
   1898 			      "catz: failed to create DB iterator - %s",
   1899 			      isc_result_totext(result));
   1900 		return;
   1901 	}
   1902 
   1903 	name = dns_fixedname_initname(&fixname);
   1904 
   1905 	/*
   1906 	 * Iterate over database to fill the new zone.
   1907 	 */
   1908 	result = dns_dbiterator_first(it);
   1909 	if (result != ISC_R_SUCCESS) {
   1910 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
   1911 			      DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
   1912 			      "catz: failed to get db iterator - %s",
   1913 			      isc_result_totext(result));
   1914 	}
   1915 
   1916 	while (result == ISC_R_SUCCESS) {
   1917 		result = dns_dbiterator_current(it, &node, name);
   1918 		if (result != ISC_R_SUCCESS) {
   1919 			isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
   1920 				      DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
   1921 				      "catz: failed to get db iterator - %s",
   1922 				      isc_result_totext(result));
   1923 			break;
   1924 		}
   1925 
   1926 		result = dns_db_allrdatasets(db, node, oldzone->dbversion, 0, 0,
   1927 					     &rdsiter);
   1928 		if (result != ISC_R_SUCCESS) {
   1929 			isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
   1930 				      DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
   1931 				      "catz: failed to fetch rrdatasets - %s",
   1932 				      isc_result_totext(result));
   1933 			dns_db_detachnode(db, &node);
   1934 			break;
   1935 		}
   1936 
   1937 		dns_rdataset_init(&rdataset);
   1938 		result = dns_rdatasetiter_first(rdsiter);
   1939 		while (result == ISC_R_SUCCESS) {
   1940 			dns_rdatasetiter_current(rdsiter, &rdataset);
   1941 
   1942 			/*
   1943 			 * Skip processing DNSSEC-related and ZONEMD types,
   1944 			 * because we are not interested in them in the context
   1945 			 * of a catalog zone, and processing them will fail
   1946 			 * and produce an unnecessary warning message.
   1947 			 */
   1948 			if (!catz_rdatatype_is_processable(rdataset.type)) {
   1949 				goto next;
   1950 			}
   1951 
   1952 			result = dns_catz_update_process(catzs, newzone, name,
   1953 							 &rdataset);
   1954 			if (result != ISC_R_SUCCESS) {
   1955 				char cname[DNS_NAME_FORMATSIZE];
   1956 				char typebuf[DNS_RDATATYPE_FORMATSIZE];
   1957 				char classbuf[DNS_RDATACLASS_FORMATSIZE];
   1958 
   1959 				dns_name_format(name, cname,
   1960 						DNS_NAME_FORMATSIZE);
   1961 				dns_rdataclass_format(rdataset.rdclass,
   1962 						      classbuf,
   1963 						      sizeof(classbuf));
   1964 				dns_rdatatype_format(rdataset.type, typebuf,
   1965 						     sizeof(typebuf));
   1966 				isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
   1967 					      DNS_LOGMODULE_MASTER,
   1968 					      ISC_LOG_WARNING,
   1969 					      "catz: unknown record in catalog "
   1970 					      "zone - %s %s %s(%s) - ignoring",
   1971 					      cname, classbuf, typebuf,
   1972 					      isc_result_totext(result));
   1973 			}
   1974 		next:
   1975 			dns_rdataset_disassociate(&rdataset);
   1976 			result = dns_rdatasetiter_next(rdsiter);
   1977 		}
   1978 
   1979 		dns_rdatasetiter_destroy(&rdsiter);
   1980 
   1981 		dns_db_detachnode(db, &node);
   1982 		result = dns_dbiterator_next(it);
   1983 	}
   1984 
   1985 	dns_dbiterator_destroy(&it);
   1986 	dns_db_closeversion(db, &oldzone->dbversion, false);
   1987 	isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER,
   1988 		      ISC_LOG_DEBUG(3),
   1989 		      "catz: update_from_db: iteration finished");
   1990 
   1991 	/*
   1992 	 * Finally merge new zone into old zone.
   1993 	 */
   1994 	result = dns_catz_zones_merge(oldzone, newzone);
   1995 	dns_catz_zone_detach(&newzone);
   1996 	if (result != ISC_R_SUCCESS) {
   1997 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
   1998 			      DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
   1999 			      "catz: failed merging zones: %s",
   2000 			      isc_result_totext(result));
   2001 
   2002 		return;
   2003 	}
   2004 
   2005 	isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER,
   2006 		      ISC_LOG_DEBUG(3),
   2007 		      "catz: update_from_db: new zone merged");
   2008 
   2009 	/*
   2010 	 * When we're doing reconfig and setting a new catalog zone
   2011 	 * from an existing zone we won't have a chance to set up
   2012 	 * update callback in zone_startload or axfr_makedb, but we will
   2013 	 * call onupdate() artificially so we can register the callback here.
   2014 	 */
   2015 	if (!oldzone->db_registered) {
   2016 		result = dns_db_updatenotify_register(
   2017 			db, dns_catz_dbupdate_callback, oldzone->catzs);
   2018 		if (result == ISC_R_SUCCESS) {
   2019 			oldzone->db_registered = true;
   2020 		}
   2021 	}
   2022 }
   2023 
   2024 void
   2025 dns_catz_prereconfig(dns_catz_zones_t *catzs) {
   2026 	isc_result_t result;
   2027 	isc_ht_iter_t *iter = NULL;
   2028 
   2029 	REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
   2030 
   2031 	isc_ht_iter_create(catzs->zones, &iter);
   2032 	for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;
   2033 	     result = isc_ht_iter_next(iter))
   2034 	{
   2035 		dns_catz_zone_t *zone = NULL;
   2036 		isc_ht_iter_current(iter, (void **)&zone);
   2037 		zone->active = false;
   2038 	}
   2039 	INSIST(result == ISC_R_NOMORE);
   2040 	isc_ht_iter_destroy(&iter);
   2041 }
   2042 
   2043 void
   2044 dns_catz_postreconfig(dns_catz_zones_t *catzs) {
   2045 	isc_result_t result;
   2046 	dns_catz_zone_t *newzone = NULL;
   2047 	isc_ht_iter_t *iter = NULL;
   2048 
   2049 	REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
   2050 
   2051 	LOCK(&catzs->lock);
   2052 	isc_ht_iter_create(catzs->zones, &iter);
   2053 	for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;) {
   2054 		dns_catz_zone_t *zone = NULL;
   2055 
   2056 		isc_ht_iter_current(iter, (void **)&zone);
   2057 		if (!zone->active) {
   2058 			char cname[DNS_NAME_FORMATSIZE];
   2059 			dns_name_format(&zone->name, cname,
   2060 					DNS_NAME_FORMATSIZE);
   2061 			isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
   2062 				      DNS_LOGMODULE_MASTER, ISC_LOG_WARNING,
   2063 				      "catz: removing catalog zone %s", cname);
   2064 
   2065 			/*
   2066 			 * Merge the old zone with an empty one to remove
   2067 			 * all members.
   2068 			 */
   2069 			result = dns_catz_new_zone(catzs, &newzone,
   2070 						   &zone->name);
   2071 			INSIST(result == ISC_R_SUCCESS);
   2072 			dns_catz_zones_merge(zone, newzone);
   2073 			dns_catz_zone_detach(&newzone);
   2074 
   2075 			/* Make sure that we have an empty catalog zone. */
   2076 			INSIST(isc_ht_count(zone->entries) == 0);
   2077 			result = isc_ht_iter_delcurrent_next(iter);
   2078 			dns_catz_zone_detach(&zone);
   2079 		} else {
   2080 			result = isc_ht_iter_next(iter);
   2081 		}
   2082 	}
   2083 	UNLOCK(&catzs->lock);
   2084 	RUNTIME_CHECK(result == ISC_R_NOMORE);
   2085 	isc_ht_iter_destroy(&iter);
   2086 }
   2087 
   2088 void
   2089 dns_catz_get_iterator(dns_catz_zone_t *catz, isc_ht_iter_t **itp) {
   2090 	REQUIRE(DNS_CATZ_ZONE_VALID(catz));
   2091 
   2092 	isc_ht_iter_create(catz->entries, itp);
   2093 }
   2094