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