xref: /netbsd-src/external/mpl/bind/dist/lib/dns/catz.c (revision 4f645668ed707e1f969c546666f8c8e45e6f8888)
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