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