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