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