catz.c revision 1.9 1 /* $NetBSD: catz.c,v 1.9 2023/01/25 21:43: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
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 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 *
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 *
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
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
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);
429
430 isc_ht_init(&tomod, target->catzs->mctx, 16);
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
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);
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_put(mctx, new_zones, sizeof(*new_zones));
636
637 return (result);
638 }
639
640 void
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
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);
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
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 *
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 result = isc_ht_find(catzs->zones, name->ndata, name->length,
758 (void **)&found);
759 if (result != ISC_R_SUCCESS) {
760 return (NULL);
761 }
762
763 return (found);
764 }
765
766 void
767 dns_catz_catzs_attach(dns_catz_zones_t *catzs, dns_catz_zones_t **catzsp) {
768 REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
769 REQUIRE(catzsp != NULL && *catzsp == NULL);
770
771 isc_refcount_increment(&catzs->refs);
772 *catzsp = catzs;
773 }
774
775 void
776 dns_catz_zone_attach(dns_catz_zone_t *zone, dns_catz_zone_t **zonep) {
777 REQUIRE(zonep != NULL && *zonep == NULL);
778
779 isc_refcount_increment(&zone->refs);
780 *zonep = zone;
781 }
782
783 void
784 dns_catz_zone_detach(dns_catz_zone_t **zonep) {
785 REQUIRE(zonep != NULL && *zonep != NULL);
786 dns_catz_zone_t *zone = *zonep;
787 *zonep = NULL;
788
789 if (isc_refcount_decrement(&zone->refs) == 1) {
790 isc_mem_t *mctx = zone->catzs->mctx;
791 isc_refcount_destroy(&zone->refs);
792 if (zone->entries != NULL) {
793 isc_ht_iter_t *iter = NULL;
794 isc_result_t result;
795 isc_ht_iter_create(zone->entries, &iter);
796 for (result = isc_ht_iter_first(iter);
797 result == ISC_R_SUCCESS;
798 result = isc_ht_iter_delcurrent_next(iter))
799 {
800 dns_catz_entry_t *entry = NULL;
801
802 isc_ht_iter_current(iter, (void **)&entry);
803 dns_catz_entry_detach(zone, &entry);
804 }
805 INSIST(result == ISC_R_NOMORE);
806 isc_ht_iter_destroy(&iter);
807
808 /* The hashtable has to be empty now. */
809 INSIST(isc_ht_count(zone->entries) == 0);
810 isc_ht_destroy(&zone->entries);
811 }
812 zone->magic = 0;
813 isc_timer_detach(&zone->updatetimer);
814 if (zone->db_registered) {
815 dns_db_updatenotify_unregister(
816 zone->db, dns_catz_dbupdate_callback,
817 zone->catzs);
818 }
819 if (zone->dbversion) {
820 dns_db_closeversion(zone->db, &zone->dbversion, false);
821 }
822 if (zone->db != NULL) {
823 dns_db_detach(&zone->db);
824 }
825
826 dns_name_free(&zone->name, mctx);
827 dns_catz_options_free(&zone->defoptions, mctx);
828 dns_catz_options_free(&zone->zoneoptions, mctx);
829
830 zone->catzs = NULL;
831 isc_mem_put(mctx, zone, sizeof(dns_catz_zone_t));
832 }
833 }
834
835 void
836 dns_catz_catzs_detach(dns_catz_zones_t **catzsp) {
837 dns_catz_zones_t *catzs;
838
839 REQUIRE(catzsp != NULL && DNS_CATZ_ZONES_VALID(*catzsp));
840
841 catzs = *catzsp;
842 *catzsp = NULL;
843
844 if (isc_refcount_decrement(&catzs->refs) == 1) {
845 catzs->magic = 0;
846 isc_task_destroy(&catzs->updater);
847 isc_mutex_destroy(&catzs->lock);
848 if (catzs->zones != NULL) {
849 isc_ht_iter_t *iter = NULL;
850 isc_result_t result;
851 isc_ht_iter_create(catzs->zones, &iter);
852 for (result = isc_ht_iter_first(iter);
853 result == ISC_R_SUCCESS;)
854 {
855 dns_catz_zone_t *zone = NULL;
856 isc_ht_iter_current(iter, (void **)&zone);
857 result = isc_ht_iter_delcurrent_next(iter);
858 dns_catz_zone_detach(&zone);
859 }
860 INSIST(result == ISC_R_NOMORE);
861 isc_ht_iter_destroy(&iter);
862 INSIST(isc_ht_count(catzs->zones) == 0);
863 isc_ht_destroy(&catzs->zones);
864 }
865 isc_refcount_destroy(&catzs->refs);
866 isc_mem_putanddetach(&catzs->mctx, catzs, sizeof(*catzs));
867 }
868 }
869
870 typedef enum {
871 CATZ_OPT_NONE,
872 CATZ_OPT_ZONES,
873 CATZ_OPT_MASTERS,
874 CATZ_OPT_ALLOW_QUERY,
875 CATZ_OPT_ALLOW_TRANSFER,
876 CATZ_OPT_VERSION,
877 } catz_opt_t;
878
879 static bool
880 catz_opt_cmp(const dns_label_t *option, const char *opt) {
881 unsigned int l = strlen(opt);
882 if (option->length - 1 == l &&
883 memcmp(opt, option->base + 1, l - 1) == 0)
884 {
885 return (true);
886 } else {
887 return (false);
888 }
889 }
890
891 static catz_opt_t
892 catz_get_option(const dns_label_t *option) {
893 if (catz_opt_cmp(option, "zones")) {
894 return (CATZ_OPT_ZONES);
895 } else if (catz_opt_cmp(option, "masters")) {
896 return (CATZ_OPT_MASTERS);
897 } else if (catz_opt_cmp(option, "allow-query")) {
898 return (CATZ_OPT_ALLOW_QUERY);
899 } else if (catz_opt_cmp(option, "allow-transfer")) {
900 return (CATZ_OPT_ALLOW_TRANSFER);
901 } else if (catz_opt_cmp(option, "version")) {
902 return (CATZ_OPT_VERSION);
903 } else {
904 return (CATZ_OPT_NONE);
905 }
906 }
907
908 static isc_result_t
909 catz_process_zones(dns_catz_zone_t *zone, dns_rdataset_t *value,
910 dns_name_t *name) {
911 dns_label_t mhash;
912 dns_name_t opt;
913
914 REQUIRE(DNS_CATZ_ZONE_VALID(zone));
915 REQUIRE(DNS_RDATASET_VALID(value));
916 REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
917
918 if (value->rdclass != dns_rdataclass_in) {
919 return (ISC_R_FAILURE);
920 }
921
922 if (name->labels == 0) {
923 return (ISC_R_FAILURE);
924 }
925
926 dns_name_getlabel(name, name->labels - 1, &mhash);
927
928 if (name->labels == 1) {
929 return (catz_process_zones_entry(zone, value, &mhash));
930 } else {
931 dns_name_init(&opt, NULL);
932 dns_name_split(name, 1, &opt, NULL);
933 return (catz_process_zones_suboption(zone, value, &mhash,
934 &opt));
935 }
936 }
937
938 static isc_result_t
939 catz_process_zones_entry(dns_catz_zone_t *zone, dns_rdataset_t *value,
940 dns_label_t *mhash) {
941 isc_result_t result;
942 dns_rdata_t rdata;
943 dns_rdata_ptr_t ptr;
944 dns_catz_entry_t *entry = NULL;
945
946 /*
947 * We only take -first- value, as mhash must be
948 * different.
949 */
950 if (value->type != dns_rdatatype_ptr) {
951 return (ISC_R_FAILURE);
952 }
953
954 result = dns_rdataset_first(value);
955 if (result != ISC_R_SUCCESS) {
956 return (ISC_R_FAILURE);
957 }
958
959 dns_rdata_init(&rdata);
960 dns_rdataset_current(value, &rdata);
961
962 result = dns_rdata_tostruct(&rdata, &ptr, NULL);
963 RUNTIME_CHECK(result == ISC_R_SUCCESS);
964
965 result = isc_ht_find(zone->entries, mhash->base, mhash->length,
966 (void **)&entry);
967 if (result == ISC_R_SUCCESS) {
968 if (dns_name_countlabels(&entry->name) != 0) {
969 /* We have a duplicate. */
970 dns_rdata_freestruct(&ptr);
971 return (ISC_R_FAILURE);
972 } else {
973 dns_name_dup(&ptr.ptr, zone->catzs->mctx, &entry->name);
974 }
975 } else {
976 result = dns_catz_entry_new(zone->catzs->mctx, &ptr.ptr,
977 &entry);
978 if (result != ISC_R_SUCCESS) {
979 dns_rdata_freestruct(&ptr);
980 return (result);
981 }
982
983 result = isc_ht_add(zone->entries, mhash->base, mhash->length,
984 entry);
985 if (result != ISC_R_SUCCESS) {
986 dns_rdata_freestruct(&ptr);
987 dns_catz_entry_detach(zone, &entry);
988 return (result);
989 }
990 }
991
992 dns_rdata_freestruct(&ptr);
993
994 return (ISC_R_SUCCESS);
995 }
996
997 static isc_result_t
998 catz_process_version(dns_catz_zone_t *zone, dns_rdataset_t *value) {
999 isc_result_t result;
1000 dns_rdata_t rdata;
1001 dns_rdata_txt_t rdatatxt;
1002 dns_rdata_txt_string_t rdatastr;
1003 uint32_t tversion;
1004 char t[16];
1005
1006 REQUIRE(DNS_CATZ_ZONE_VALID(zone));
1007 REQUIRE(DNS_RDATASET_VALID(value));
1008
1009 if (value->rdclass != dns_rdataclass_in ||
1010 value->type != dns_rdatatype_txt)
1011 {
1012 return (ISC_R_FAILURE);
1013 }
1014
1015 result = dns_rdataset_first(value);
1016 if (result != ISC_R_SUCCESS) {
1017 return (result);
1018 }
1019
1020 dns_rdata_init(&rdata);
1021 dns_rdataset_current(value, &rdata);
1022
1023 result = dns_rdata_tostruct(&rdata, &rdatatxt, NULL);
1024 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1025
1026 result = dns_rdata_txt_first(&rdatatxt);
1027 if (result != ISC_R_SUCCESS) {
1028 goto cleanup;
1029 }
1030
1031 result = dns_rdata_txt_current(&rdatatxt, &rdatastr);
1032 if (result != ISC_R_SUCCESS) {
1033 goto cleanup;
1034 }
1035
1036 result = dns_rdata_txt_next(&rdatatxt);
1037 if (result != ISC_R_NOMORE) {
1038 result = ISC_R_FAILURE;
1039 goto cleanup;
1040 }
1041 if (rdatastr.length > 15) {
1042 result = ISC_R_BADNUMBER;
1043 goto cleanup;
1044 }
1045 memmove(t, rdatastr.data, rdatastr.length);
1046 t[rdatastr.length] = 0;
1047 result = isc_parse_uint32(&tversion, t, 10);
1048 if (result != ISC_R_SUCCESS) {
1049 goto cleanup;
1050 }
1051 zone->version = tversion;
1052 result = ISC_R_SUCCESS;
1053
1054 cleanup:
1055 dns_rdata_freestruct(&rdatatxt);
1056 return (result);
1057 }
1058
1059 static isc_result_t
1060 catz_process_masters(dns_catz_zone_t *zone, dns_ipkeylist_t *ipkl,
1061 dns_rdataset_t *value, dns_name_t *name) {
1062 isc_result_t result;
1063 dns_rdata_t rdata;
1064 dns_rdata_in_a_t rdata_a;
1065 dns_rdata_in_aaaa_t rdata_aaaa;
1066 dns_rdata_txt_t rdata_txt;
1067 dns_rdata_txt_string_t rdatastr;
1068 dns_name_t *keyname = NULL;
1069 isc_mem_t *mctx;
1070 char keycbuf[DNS_NAME_FORMATSIZE];
1071 isc_buffer_t keybuf;
1072 unsigned int rcount;
1073 unsigned int i;
1074
1075 REQUIRE(DNS_CATZ_ZONE_VALID(zone));
1076 REQUIRE(ipkl != NULL);
1077 REQUIRE(DNS_RDATASET_VALID(value));
1078 REQUIRE(dns_rdataset_isassociated(value));
1079 REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
1080
1081 mctx = zone->catzs->mctx;
1082 memset(&rdata_a, 0, sizeof(rdata_a));
1083 memset(&rdata_aaaa, 0, sizeof(rdata_aaaa));
1084 memset(&rdata_txt, 0, sizeof(rdata_txt));
1085 isc_buffer_init(&keybuf, keycbuf, sizeof(keycbuf));
1086
1087 /*
1088 * We have three possibilities here:
1089 * - either empty name and IN A/IN AAAA record
1090 * - label and IN A/IN AAAA
1091 * - label and IN TXT - TSIG key name
1092 */
1093 if (value->rdclass != dns_rdataclass_in) {
1094 return (ISC_R_FAILURE);
1095 }
1096
1097 if (name->labels > 0) {
1098 isc_sockaddr_t sockaddr;
1099
1100 /*
1101 * We're pre-preparing the data once, we'll put it into
1102 * the right spot in the masters array once we find it.
1103 */
1104 result = dns_rdataset_first(value);
1105 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1106 dns_rdata_init(&rdata);
1107 dns_rdataset_current(value, &rdata);
1108 switch (value->type) {
1109 case dns_rdatatype_a:
1110 result = dns_rdata_tostruct(&rdata, &rdata_a, NULL);
1111 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1112 isc_sockaddr_fromin(&sockaddr, &rdata_a.in_addr, 0);
1113 break;
1114 case dns_rdatatype_aaaa:
1115 result = dns_rdata_tostruct(&rdata, &rdata_aaaa, NULL);
1116 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1117 isc_sockaddr_fromin6(&sockaddr, &rdata_aaaa.in6_addr,
1118 0);
1119 break;
1120 case dns_rdatatype_txt:
1121 result = dns_rdata_tostruct(&rdata, &rdata_txt, NULL);
1122 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1123
1124 result = dns_rdata_txt_first(&rdata_txt);
1125 if (result != ISC_R_SUCCESS) {
1126 return (result);
1127 }
1128
1129 result = dns_rdata_txt_current(&rdata_txt, &rdatastr);
1130 if (result != ISC_R_SUCCESS) {
1131 return (result);
1132 }
1133
1134 result = dns_rdata_txt_next(&rdata_txt);
1135 if (result != ISC_R_NOMORE) {
1136 return (ISC_R_FAILURE);
1137 }
1138
1139 /* rdatastr.length < DNS_NAME_MAXTEXT */
1140 keyname = isc_mem_get(mctx, sizeof(dns_name_t));
1141 dns_name_init(keyname, 0);
1142 memmove(keycbuf, rdatastr.data, rdatastr.length);
1143 keycbuf[rdatastr.length] = 0;
1144 result = dns_name_fromstring(keyname, keycbuf, 0, mctx);
1145 if (result != ISC_R_SUCCESS) {
1146 dns_name_free(keyname, mctx);
1147 isc_mem_put(mctx, keyname, sizeof(dns_name_t));
1148 return (result);
1149 }
1150 break;
1151 default:
1152 return (ISC_R_FAILURE);
1153 }
1154
1155 /*
1156 * We have to find the appropriate labeled record in masters
1157 * if it exists.
1158 * In common case we'll have no more than 3-4 records here so
1159 * no optimization.
1160 */
1161 for (i = 0; i < ipkl->count; i++) {
1162 if (ipkl->labels[i] != NULL &&
1163 !dns_name_compare(name, ipkl->labels[i]))
1164 {
1165 break;
1166 }
1167 }
1168
1169 if (i < ipkl->count) { /* we have this record already */
1170 if (value->type == dns_rdatatype_txt) {
1171 ipkl->keys[i] = keyname;
1172 } else { /* A/AAAA */
1173 memmove(&ipkl->addrs[i], &sockaddr,
1174 sizeof(isc_sockaddr_t));
1175 }
1176 } else {
1177 result = dns_ipkeylist_resize(mctx, ipkl, i + 1);
1178 if (result != ISC_R_SUCCESS) {
1179 return (result);
1180 }
1181
1182 ipkl->labels[i] = isc_mem_get(mctx, sizeof(dns_name_t));
1183 dns_name_init(ipkl->labels[i], NULL);
1184 dns_name_dup(name, mctx, ipkl->labels[i]);
1185
1186 if (value->type == dns_rdatatype_txt) {
1187 ipkl->keys[i] = keyname;
1188 } else { /* A/AAAA */
1189 memmove(&ipkl->addrs[i], &sockaddr,
1190 sizeof(isc_sockaddr_t));
1191 }
1192 ipkl->count++;
1193 }
1194 return (ISC_R_SUCCESS);
1195 }
1196 /* else - 'simple' case - without labels */
1197
1198 if (value->type != dns_rdatatype_a && value->type != dns_rdatatype_aaaa)
1199 {
1200 return (ISC_R_FAILURE);
1201 }
1202
1203 rcount = dns_rdataset_count(value) + ipkl->count;
1204
1205 result = dns_ipkeylist_resize(mctx, ipkl, rcount);
1206 if (result != ISC_R_SUCCESS) {
1207 return (result);
1208 }
1209
1210 for (result = dns_rdataset_first(value); result == ISC_R_SUCCESS;
1211 result = dns_rdataset_next(value))
1212 {
1213 dns_rdata_init(&rdata);
1214 dns_rdataset_current(value, &rdata);
1215 /*
1216 * port 0 == take the default
1217 */
1218 if (value->type == dns_rdatatype_a) {
1219 result = dns_rdata_tostruct(&rdata, &rdata_a, NULL);
1220 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1221 isc_sockaddr_fromin(&ipkl->addrs[ipkl->count],
1222 &rdata_a.in_addr, 0);
1223 } else {
1224 result = dns_rdata_tostruct(&rdata, &rdata_aaaa, NULL);
1225 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1226 isc_sockaddr_fromin6(&ipkl->addrs[ipkl->count],
1227 &rdata_aaaa.in6_addr, 0);
1228 }
1229 ipkl->keys[ipkl->count] = NULL;
1230 ipkl->labels[ipkl->count] = NULL;
1231 ipkl->count++;
1232 dns_rdata_freestruct(&rdata_a);
1233 }
1234 return (ISC_R_SUCCESS);
1235 }
1236
1237 static isc_result_t
1238 catz_process_apl(dns_catz_zone_t *zone, isc_buffer_t **aclbp,
1239 dns_rdataset_t *value) {
1240 isc_result_t result = ISC_R_SUCCESS;
1241 dns_rdata_t rdata;
1242 dns_rdata_in_apl_t rdata_apl;
1243 dns_rdata_apl_ent_t apl_ent;
1244 isc_netaddr_t addr;
1245 isc_buffer_t *aclb = NULL;
1246 unsigned char buf[256]; /* larger than INET6_ADDRSTRLEN */
1247
1248 REQUIRE(DNS_CATZ_ZONE_VALID(zone));
1249 REQUIRE(aclbp != NULL);
1250 REQUIRE(*aclbp == NULL);
1251 REQUIRE(DNS_RDATASET_VALID(value));
1252 REQUIRE(dns_rdataset_isassociated(value));
1253
1254 if (value->rdclass != dns_rdataclass_in ||
1255 value->type != dns_rdatatype_apl)
1256 {
1257 return (ISC_R_FAILURE);
1258 }
1259
1260 if (dns_rdataset_count(value) > 1) {
1261 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
1262 DNS_LOGMODULE_MASTER, ISC_LOG_WARNING,
1263 "catz: more than one APL entry for member zone, "
1264 "result is undefined");
1265 }
1266 result = dns_rdataset_first(value);
1267 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1268 dns_rdata_init(&rdata);
1269 dns_rdataset_current(value, &rdata);
1270 result = dns_rdata_tostruct(&rdata, &rdata_apl, zone->catzs->mctx);
1271 if (result != ISC_R_SUCCESS) {
1272 return (result);
1273 }
1274 isc_buffer_allocate(zone->catzs->mctx, &aclb, 16);
1275 isc_buffer_setautorealloc(aclb, true);
1276 for (result = dns_rdata_apl_first(&rdata_apl); result == ISC_R_SUCCESS;
1277 result = dns_rdata_apl_next(&rdata_apl))
1278 {
1279 result = dns_rdata_apl_current(&rdata_apl, &apl_ent);
1280 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1281 memset(buf, 0, sizeof(buf));
1282 if (apl_ent.data != NULL && apl_ent.length > 0) {
1283 memmove(buf, apl_ent.data, apl_ent.length);
1284 }
1285 if (apl_ent.family == 1) {
1286 isc_netaddr_fromin(&addr, (struct in_addr *)buf);
1287 } else if (apl_ent.family == 2) {
1288 isc_netaddr_fromin6(&addr, (struct in6_addr *)buf);
1289 } else {
1290 continue; /* xxxwpk log it or simply ignore? */
1291 }
1292 if (apl_ent.negative) {
1293 isc_buffer_putuint8(aclb, '!');
1294 }
1295 isc_buffer_reserve(&aclb, INET6_ADDRSTRLEN);
1296 result = isc_netaddr_totext(&addr, aclb);
1297 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1298 if ((apl_ent.family == 1 && apl_ent.prefix < 32) ||
1299 (apl_ent.family == 2 && apl_ent.prefix < 128))
1300 {
1301 isc_buffer_putuint8(aclb, '/');
1302 isc_buffer_putdecint(aclb, apl_ent.prefix);
1303 }
1304 isc_buffer_putstr(aclb, "; ");
1305 }
1306 if (result == ISC_R_NOMORE) {
1307 result = ISC_R_SUCCESS;
1308 } else {
1309 goto cleanup;
1310 }
1311 *aclbp = aclb;
1312 aclb = NULL;
1313 cleanup:
1314 if (aclb != NULL) {
1315 isc_buffer_free(&aclb);
1316 }
1317 dns_rdata_freestruct(&rdata_apl);
1318 return (result);
1319 }
1320
1321 static isc_result_t
1322 catz_process_zones_suboption(dns_catz_zone_t *zone, dns_rdataset_t *value,
1323 dns_label_t *mhash, dns_name_t *name) {
1324 isc_result_t result;
1325 dns_catz_entry_t *entry = NULL;
1326 dns_label_t option;
1327 dns_name_t prefix;
1328 catz_opt_t opt;
1329
1330 REQUIRE(DNS_CATZ_ZONE_VALID(zone));
1331 REQUIRE(mhash != NULL);
1332 REQUIRE(DNS_RDATASET_VALID(value));
1333 REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
1334
1335 if (name->labels == 0) {
1336 return (ISC_R_FAILURE);
1337 }
1338 dns_name_getlabel(name, name->labels - 1, &option);
1339 opt = catz_get_option(&option);
1340
1341 /*
1342 * We're adding this entry now, in case the option is invalid we'll get
1343 * rid of it in verification phase.
1344 */
1345 result = isc_ht_find(zone->entries, mhash->base, mhash->length,
1346 (void **)&entry);
1347 if (result != ISC_R_SUCCESS) {
1348 result = dns_catz_entry_new(zone->catzs->mctx, NULL, &entry);
1349 if (result != ISC_R_SUCCESS) {
1350 return (result);
1351 }
1352 result = isc_ht_add(zone->entries, mhash->base, mhash->length,
1353 entry);
1354 if (result != ISC_R_SUCCESS) {
1355 dns_catz_entry_detach(zone, &entry);
1356 return (result);
1357 }
1358 }
1359
1360 dns_name_init(&prefix, NULL);
1361 dns_name_split(name, 1, &prefix, NULL);
1362 switch (opt) {
1363 case CATZ_OPT_MASTERS:
1364 return (catz_process_masters(zone, &entry->opts.masters, value,
1365 &prefix));
1366 case CATZ_OPT_ALLOW_QUERY:
1367 if (prefix.labels != 0) {
1368 return (ISC_R_FAILURE);
1369 }
1370 return (catz_process_apl(zone, &entry->opts.allow_query,
1371 value));
1372 case CATZ_OPT_ALLOW_TRANSFER:
1373 if (prefix.labels != 0) {
1374 return (ISC_R_FAILURE);
1375 }
1376 return (catz_process_apl(zone, &entry->opts.allow_transfer,
1377 value));
1378 default:
1379 return (ISC_R_FAILURE);
1380 }
1381
1382 return (ISC_R_FAILURE);
1383 }
1384
1385 static void
1386 catz_entry_add_or_mod(dns_catz_zone_t *target, isc_ht_t *ht, unsigned char *key,
1387 size_t keysize, dns_catz_entry_t *nentry,
1388 dns_catz_entry_t *oentry, const char *msg,
1389 const char *zname, const char *czname) {
1390 isc_result_t result = isc_ht_add(ht, key, (uint32_t)keysize, nentry);
1391
1392 if (result != ISC_R_SUCCESS) {
1393 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
1394 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
1395 "catz: error %s zone '%s' from catalog '%s' - %s",
1396 msg, zname, czname, isc_result_totext(result));
1397 }
1398 if (oentry != NULL) {
1399 dns_catz_entry_detach(target, &oentry);
1400 result = isc_ht_delete(target->entries, key, (uint32_t)keysize);
1401 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1402 }
1403 }
1404
1405 static isc_result_t
1406 catz_process_value(dns_catz_zone_t *zone, dns_name_t *name,
1407 dns_rdataset_t *rdataset) {
1408 dns_label_t option;
1409 dns_name_t prefix;
1410 catz_opt_t opt;
1411
1412 REQUIRE(DNS_CATZ_ZONE_VALID(zone));
1413 REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
1414 REQUIRE(DNS_RDATASET_VALID(rdataset));
1415
1416 dns_name_getlabel(name, name->labels - 1, &option);
1417 opt = catz_get_option(&option);
1418 dns_name_init(&prefix, NULL);
1419 dns_name_split(name, 1, &prefix, NULL);
1420 switch (opt) {
1421 case CATZ_OPT_ZONES:
1422 return (catz_process_zones(zone, rdataset, &prefix));
1423 case CATZ_OPT_MASTERS:
1424 return (catz_process_masters(zone, &zone->zoneoptions.masters,
1425 rdataset, &prefix));
1426 case CATZ_OPT_ALLOW_QUERY:
1427 if (prefix.labels != 0) {
1428 return (ISC_R_FAILURE);
1429 }
1430 return (catz_process_apl(zone, &zone->zoneoptions.allow_query,
1431 rdataset));
1432 case CATZ_OPT_ALLOW_TRANSFER:
1433 if (prefix.labels != 0) {
1434 return (ISC_R_FAILURE);
1435 }
1436 return (catz_process_apl(
1437 zone, &zone->zoneoptions.allow_transfer, rdataset));
1438 case CATZ_OPT_VERSION:
1439 if (prefix.labels != 0) {
1440 return (ISC_R_FAILURE);
1441 }
1442 return (catz_process_version(zone, rdataset));
1443 default:
1444 return (ISC_R_FAILURE);
1445 }
1446 }
1447
1448 isc_result_t
1449 dns_catz_update_process(dns_catz_zones_t *catzs, dns_catz_zone_t *zone,
1450 const dns_name_t *src_name, dns_rdataset_t *rdataset) {
1451 isc_result_t result;
1452 int order;
1453 unsigned int nlabels;
1454 dns_namereln_t nrres;
1455 dns_rdata_t rdata = DNS_RDATA_INIT;
1456 dns_rdata_soa_t soa;
1457 dns_name_t prefix;
1458
1459 REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
1460 REQUIRE(DNS_CATZ_ZONE_VALID(zone));
1461 REQUIRE(ISC_MAGIC_VALID(src_name, DNS_NAME_MAGIC));
1462
1463 nrres = dns_name_fullcompare(src_name, &zone->name, &order, &nlabels);
1464 if (nrres == dns_namereln_equal) {
1465 if (rdataset->type == dns_rdatatype_soa) {
1466 result = dns_rdataset_first(rdataset);
1467 if (result != ISC_R_SUCCESS) {
1468 return (result);
1469 }
1470
1471 dns_rdataset_current(rdataset, &rdata);
1472 result = dns_rdata_tostruct(&rdata, &soa, NULL);
1473 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1474
1475 /*
1476 * xxxwpk TODO do we want to save something from SOA?
1477 */
1478 return (result);
1479 } else if (rdataset->type == dns_rdatatype_ns) {
1480 return (ISC_R_SUCCESS);
1481 } else {
1482 return (ISC_R_UNEXPECTED);
1483 }
1484 } else if (nrres != dns_namereln_subdomain) {
1485 return (ISC_R_UNEXPECTED);
1486 }
1487
1488 dns_name_init(&prefix, NULL);
1489 dns_name_split(src_name, zone->name.labels, &prefix, NULL);
1490 result = catz_process_value(zone, &prefix, rdataset);
1491
1492 return (result);
1493 }
1494
1495 static isc_result_t
1496 digest2hex(unsigned char *digest, unsigned int digestlen, char *hash,
1497 size_t hashlen) {
1498 unsigned int i;
1499 int ret;
1500 for (i = 0; i < digestlen; i++) {
1501 size_t left = hashlen - i * 2;
1502 ret = snprintf(hash + i * 2, left, "%02x", digest[i]);
1503 if (ret < 0 || (size_t)ret >= left) {
1504 return (ISC_R_NOSPACE);
1505 }
1506 }
1507 return (ISC_R_SUCCESS);
1508 }
1509
1510 isc_result_t
1511 dns_catz_generate_masterfilename(dns_catz_zone_t *zone, dns_catz_entry_t *entry,
1512 isc_buffer_t **buffer) {
1513 isc_buffer_t *tbuf = NULL;
1514 isc_region_t r;
1515 isc_result_t result;
1516 size_t rlen;
1517 bool special = false;
1518
1519 REQUIRE(DNS_CATZ_ZONE_VALID(zone));
1520 REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
1521 REQUIRE(buffer != NULL && *buffer != NULL);
1522
1523 isc_buffer_allocate(zone->catzs->mctx, &tbuf,
1524 strlen(zone->catzs->view->name) +
1525 2 * DNS_NAME_FORMATSIZE + 2);
1526
1527 isc_buffer_putstr(tbuf, zone->catzs->view->name);
1528 isc_buffer_putstr(tbuf, "_");
1529 result = dns_name_totext(&zone->name, true, tbuf);
1530 if (result != ISC_R_SUCCESS) {
1531 goto cleanup;
1532 }
1533
1534 isc_buffer_putstr(tbuf, "_");
1535 result = dns_name_totext(&entry->name, true, tbuf);
1536 if (result != ISC_R_SUCCESS) {
1537 goto cleanup;
1538 }
1539
1540 /*
1541 * Search for slash and other special characters in the view and
1542 * zone names. Add a null terminator so we can use strpbrk(), then
1543 * remove it.
1544 */
1545 isc_buffer_putuint8(tbuf, 0);
1546 if (strpbrk(isc_buffer_base(tbuf), "\\/:") != NULL) {
1547 special = true;
1548 }
1549 isc_buffer_subtract(tbuf, 1);
1550
1551 /* __catz__<digest>.db */
1552 rlen = (isc_md_type_get_size(ISC_MD_SHA256) * 2 + 1) + 12;
1553
1554 /* optionally prepend with <zonedir>/ */
1555 if (entry->opts.zonedir != NULL) {
1556 rlen += strlen(entry->opts.zonedir) + 1;
1557 }
1558
1559 result = isc_buffer_reserve(buffer, (unsigned int)rlen);
1560 if (result != ISC_R_SUCCESS) {
1561 goto cleanup;
1562 }
1563
1564 if (entry->opts.zonedir != NULL) {
1565 isc_buffer_putstr(*buffer, entry->opts.zonedir);
1566 isc_buffer_putstr(*buffer, "/");
1567 }
1568
1569 isc_buffer_usedregion(tbuf, &r);
1570 isc_buffer_putstr(*buffer, "__catz__");
1571 if (special || tbuf->used > ISC_SHA256_DIGESTLENGTH * 2 + 1) {
1572 unsigned char digest[ISC_MAX_MD_SIZE];
1573 unsigned int digestlen;
1574
1575 /* we can do that because digest string < 2 * DNS_NAME */
1576 result = isc_md(ISC_MD_SHA256, r.base, r.length, digest,
1577 &digestlen);
1578 if (result != ISC_R_SUCCESS) {
1579 goto cleanup;
1580 }
1581 result = digest2hex(digest, digestlen, (char *)r.base,
1582 ISC_SHA256_DIGESTLENGTH * 2 + 1);
1583 if (result != ISC_R_SUCCESS) {
1584 goto cleanup;
1585 }
1586 isc_buffer_putstr(*buffer, (char *)r.base);
1587 } else {
1588 isc_buffer_copyregion(*buffer, &r);
1589 }
1590
1591 isc_buffer_putstr(*buffer, ".db");
1592 result = ISC_R_SUCCESS;
1593
1594 cleanup:
1595 isc_buffer_free(&tbuf);
1596 return (result);
1597 }
1598
1599 isc_result_t
1600 dns_catz_generate_zonecfg(dns_catz_zone_t *zone, dns_catz_entry_t *entry,
1601 isc_buffer_t **buf) {
1602 /*
1603 * We have to generate a text buffer with regular zone config:
1604 * zone "foo.bar" {
1605 * type slave;
1606 * masters [ dscp X ] { ip1 port port1; ip2 port port2; };
1607 * }
1608 */
1609 isc_buffer_t *buffer = NULL;
1610 isc_region_t region;
1611 isc_result_t result;
1612 uint32_t i;
1613 isc_netaddr_t netaddr;
1614 char pbuf[sizeof("65535")]; /* used both for port number and DSCP */
1615 char zname[DNS_NAME_FORMATSIZE];
1616
1617 REQUIRE(DNS_CATZ_ZONE_VALID(zone));
1618 REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
1619 REQUIRE(buf != NULL && *buf == NULL);
1620
1621 /*
1622 * The buffer will be reallocated if something won't fit,
1623 * ISC_BUFFER_INCR seems like a good start.
1624 */
1625 isc_buffer_allocate(zone->catzs->mctx, &buffer, ISC_BUFFER_INCR);
1626
1627 isc_buffer_setautorealloc(buffer, true);
1628 isc_buffer_putstr(buffer, "zone \"");
1629 dns_name_totext(&entry->name, true, buffer);
1630 isc_buffer_putstr(buffer, "\" { type slave; masters");
1631
1632 /*
1633 * DSCP value has no default, but when it is specified, it is identical
1634 * for all masters and cannot be overridden for a specific master IP, so
1635 * use the DSCP value set for the first master
1636 */
1637 if (entry->opts.masters.count > 0 && entry->opts.masters.dscps[0] >= 0)
1638 {
1639 isc_buffer_putstr(buffer, " dscp ");
1640 snprintf(pbuf, sizeof(pbuf), "%hd",
1641 entry->opts.masters.dscps[0]);
1642 isc_buffer_putstr(buffer, pbuf);
1643 }
1644
1645 isc_buffer_putstr(buffer, " { ");
1646 for (i = 0; i < entry->opts.masters.count; i++) {
1647 /*
1648 * Every master must have an IP address assigned.
1649 */
1650 switch (entry->opts.masters.addrs[i].type.sa.sa_family) {
1651 case AF_INET:
1652 case AF_INET6:
1653 break;
1654 default:
1655 dns_name_format(&entry->name, zname,
1656 DNS_NAME_FORMATSIZE);
1657 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
1658 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
1659 "catz: zone '%s' uses an invalid master "
1660 "(no IP address assigned)",
1661 zname);
1662 result = ISC_R_FAILURE;
1663 goto cleanup;
1664 }
1665 isc_netaddr_fromsockaddr(&netaddr,
1666 &entry->opts.masters.addrs[i]);
1667 isc_buffer_reserve(&buffer, INET6_ADDRSTRLEN);
1668 result = isc_netaddr_totext(&netaddr, buffer);
1669 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1670
1671 isc_buffer_putstr(buffer, " port ");
1672 snprintf(pbuf, sizeof(pbuf), "%u",
1673 isc_sockaddr_getport(&entry->opts.masters.addrs[i]));
1674 isc_buffer_putstr(buffer, pbuf);
1675
1676 if (entry->opts.masters.keys[i] != NULL) {
1677 isc_buffer_putstr(buffer, " key ");
1678 result = dns_name_totext(entry->opts.masters.keys[i],
1679 true, buffer);
1680 if (result != ISC_R_SUCCESS) {
1681 goto cleanup;
1682 }
1683 }
1684 isc_buffer_putstr(buffer, "; ");
1685 }
1686 isc_buffer_putstr(buffer, "}; ");
1687 if (!entry->opts.in_memory) {
1688 isc_buffer_putstr(buffer, "file \"");
1689 result = dns_catz_generate_masterfilename(zone, entry, &buffer);
1690 if (result != ISC_R_SUCCESS) {
1691 goto cleanup;
1692 }
1693 isc_buffer_putstr(buffer, "\"; ");
1694 }
1695 if (entry->opts.allow_query != NULL) {
1696 isc_buffer_putstr(buffer, "allow-query { ");
1697 isc_buffer_usedregion(entry->opts.allow_query, ®ion);
1698 isc_buffer_copyregion(buffer, ®ion);
1699 isc_buffer_putstr(buffer, "}; ");
1700 }
1701 if (entry->opts.allow_transfer != NULL) {
1702 isc_buffer_putstr(buffer, "allow-transfer { ");
1703 isc_buffer_usedregion(entry->opts.allow_transfer, ®ion);
1704 isc_buffer_copyregion(buffer, ®ion);
1705 isc_buffer_putstr(buffer, "}; ");
1706 }
1707
1708 isc_buffer_putstr(buffer, "};");
1709 *buf = buffer;
1710
1711 return (ISC_R_SUCCESS);
1712
1713 cleanup:
1714 isc_buffer_free(&buffer);
1715 return (result);
1716 }
1717
1718 void
1719 dns_catz_update_taskaction(isc_task_t *task, isc_event_t *event) {
1720 isc_result_t result;
1721 dns_catz_zone_t *zone;
1722 (void)task;
1723
1724 REQUIRE(event != NULL);
1725 zone = event->ev_arg;
1726 REQUIRE(DNS_CATZ_ZONE_VALID(zone));
1727
1728 LOCK(&zone->catzs->lock);
1729 zone->updatepending = false;
1730 dns_catz_update_from_db(zone->db, zone->catzs);
1731 result = isc_timer_reset(zone->updatetimer, isc_timertype_inactive,
1732 NULL, NULL, true);
1733 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1734 isc_event_free(&event);
1735 result = isc_time_now(&zone->lastupdated);
1736 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1737 UNLOCK(&zone->catzs->lock);
1738 }
1739
1740 isc_result_t
1741 dns_catz_dbupdate_callback(dns_db_t *db, void *fn_arg) {
1742 dns_catz_zones_t *catzs;
1743 dns_catz_zone_t *zone = NULL;
1744 isc_time_t now;
1745 uint64_t tdiff;
1746 isc_result_t result = ISC_R_SUCCESS;
1747 isc_region_t r;
1748
1749 REQUIRE(DNS_DB_VALID(db));
1750 REQUIRE(DNS_CATZ_ZONES_VALID(fn_arg));
1751 catzs = (dns_catz_zones_t *)fn_arg;
1752
1753 dns_name_toregion(&db->origin, &r);
1754
1755 LOCK(&catzs->lock);
1756 result = isc_ht_find(catzs->zones, r.base, r.length, (void **)&zone);
1757 if (result != ISC_R_SUCCESS) {
1758 goto cleanup;
1759 }
1760
1761 /* New zone came as AXFR */
1762 if (zone->db != NULL && zone->db != db) {
1763 if (zone->dbversion != NULL) {
1764 dns_db_closeversion(zone->db, &zone->dbversion, false);
1765 }
1766 dns_db_detach(&zone->db);
1767 /*
1768 * We're not registering db update callback, it will be
1769 * registered at the end of update_from_db
1770 */
1771 zone->db_registered = false;
1772 }
1773 if (zone->db == NULL) {
1774 dns_db_attach(db, &zone->db);
1775 }
1776
1777 if (!zone->updatepending) {
1778 zone->updatepending = true;
1779 isc_time_now(&now);
1780 tdiff = isc_time_microdiff(&now, &zone->lastupdated) / 1000000;
1781 if (tdiff < zone->defoptions.min_update_interval) {
1782 isc_interval_t interval;
1783 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
1784 DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
1785 "catz: new zone version came too soon, "
1786 "deferring update");
1787 isc_interval_set(&interval,
1788 zone->defoptions.min_update_interval -
1789 (unsigned int)tdiff,
1790 0);
1791 dns_db_currentversion(db, &zone->dbversion);
1792 result = isc_timer_reset(zone->updatetimer,
1793 isc_timertype_once, NULL,
1794 &interval, true);
1795 if (result != ISC_R_SUCCESS) {
1796 goto cleanup;
1797 }
1798 } else {
1799 isc_event_t *event;
1800
1801 dns_db_currentversion(db, &zone->dbversion);
1802 ISC_EVENT_INIT(&zone->updateevent,
1803 sizeof(zone->updateevent), 0, NULL,
1804 DNS_EVENT_CATZUPDATED,
1805 dns_catz_update_taskaction, zone, zone,
1806 NULL, NULL);
1807 event = &zone->updateevent;
1808 isc_task_send(catzs->updater, &event);
1809 }
1810 } else {
1811 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
1812 DNS_LOGMODULE_MASTER, ISC_LOG_DEBUG(3),
1813 "catz: update already queued");
1814 if (zone->dbversion != NULL) {
1815 dns_db_closeversion(zone->db, &zone->dbversion, false);
1816 }
1817 dns_db_currentversion(zone->db, &zone->dbversion);
1818 }
1819
1820 cleanup:
1821 UNLOCK(&catzs->lock);
1822
1823 return (result);
1824 }
1825
1826 static bool
1827 catz_rdatatype_is_processable(const dns_rdatatype_t type) {
1828 return (!dns_rdatatype_isdnssec(type) && type != dns_rdatatype_cds &&
1829 type != dns_rdatatype_cdnskey && type != dns_rdatatype_zonemd);
1830 }
1831
1832 void
1833 dns_catz_update_from_db(dns_db_t *db, dns_catz_zones_t *catzs) {
1834 dns_catz_zone_t *oldzone = NULL, *newzone = NULL;
1835 isc_result_t result;
1836 isc_region_t r;
1837 dns_dbnode_t *node = NULL;
1838 dns_dbiterator_t *it = NULL;
1839 dns_fixedname_t fixname;
1840 dns_name_t *name;
1841 dns_rdatasetiter_t *rdsiter = NULL;
1842 dns_rdataset_t rdataset;
1843 char bname[DNS_NAME_FORMATSIZE];
1844 isc_buffer_t ibname;
1845 uint32_t vers;
1846
1847 REQUIRE(DNS_DB_VALID(db));
1848 REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
1849
1850 /*
1851 * Create a new catz in the same context as current catz.
1852 */
1853 dns_name_toregion(&db->origin, &r);
1854 result = isc_ht_find(catzs->zones, r.base, r.length, (void **)&oldzone);
1855 if (result != ISC_R_SUCCESS) {
1856 /* This can happen if we remove the zone in the meantime. */
1857 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
1858 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
1859 "catz: zone '%s' not in config", bname);
1860 return;
1861 }
1862
1863 isc_buffer_init(&ibname, bname, DNS_NAME_FORMATSIZE);
1864 result = dns_name_totext(&db->origin, true, &ibname);
1865 INSIST(result == ISC_R_SUCCESS);
1866
1867 result = dns_db_getsoaserial(db, oldzone->dbversion, &vers);
1868 if (result != ISC_R_SUCCESS) {
1869 /* A zone without SOA record?!? */
1870 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
1871 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
1872 "catz: zone '%s' has no SOA record (%s)", bname,
1873 isc_result_totext(result));
1874 return;
1875 }
1876
1877 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER,
1878 ISC_LOG_INFO,
1879 "catz: updating catalog zone '%s' with serial %" PRIu32,
1880 bname, vers);
1881
1882 result = dns_catz_new_zone(catzs, &newzone, &db->origin);
1883 if (result != ISC_R_SUCCESS) {
1884 dns_db_closeversion(db, &oldzone->dbversion, false);
1885 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
1886 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
1887 "catz: failed to create new zone - %s",
1888 isc_result_totext(result));
1889 return;
1890 }
1891
1892 result = dns_db_createiterator(db, DNS_DB_NONSEC3, &it);
1893 if (result != ISC_R_SUCCESS) {
1894 dns_catz_zone_detach(&newzone);
1895 dns_db_closeversion(db, &oldzone->dbversion, false);
1896 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
1897 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
1898 "catz: failed to create DB iterator - %s",
1899 isc_result_totext(result));
1900 return;
1901 }
1902
1903 name = dns_fixedname_initname(&fixname);
1904
1905 /*
1906 * Iterate over database to fill the new zone.
1907 */
1908 result = dns_dbiterator_first(it);
1909 if (result != ISC_R_SUCCESS) {
1910 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
1911 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
1912 "catz: failed to get db iterator - %s",
1913 isc_result_totext(result));
1914 }
1915
1916 while (result == ISC_R_SUCCESS) {
1917 result = dns_dbiterator_current(it, &node, name);
1918 if (result != ISC_R_SUCCESS) {
1919 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
1920 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
1921 "catz: failed to get db iterator - %s",
1922 isc_result_totext(result));
1923 break;
1924 }
1925
1926 result = dns_db_allrdatasets(db, node, oldzone->dbversion, 0, 0,
1927 &rdsiter);
1928 if (result != ISC_R_SUCCESS) {
1929 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
1930 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
1931 "catz: failed to fetch rrdatasets - %s",
1932 isc_result_totext(result));
1933 dns_db_detachnode(db, &node);
1934 break;
1935 }
1936
1937 dns_rdataset_init(&rdataset);
1938 result = dns_rdatasetiter_first(rdsiter);
1939 while (result == ISC_R_SUCCESS) {
1940 dns_rdatasetiter_current(rdsiter, &rdataset);
1941
1942 /*
1943 * Skip processing DNSSEC-related and ZONEMD types,
1944 * because we are not interested in them in the context
1945 * of a catalog zone, and processing them will fail
1946 * and produce an unnecessary warning message.
1947 */
1948 if (!catz_rdatatype_is_processable(rdataset.type)) {
1949 goto next;
1950 }
1951
1952 result = dns_catz_update_process(catzs, newzone, name,
1953 &rdataset);
1954 if (result != ISC_R_SUCCESS) {
1955 char cname[DNS_NAME_FORMATSIZE];
1956 char typebuf[DNS_RDATATYPE_FORMATSIZE];
1957 char classbuf[DNS_RDATACLASS_FORMATSIZE];
1958
1959 dns_name_format(name, cname,
1960 DNS_NAME_FORMATSIZE);
1961 dns_rdataclass_format(rdataset.rdclass,
1962 classbuf,
1963 sizeof(classbuf));
1964 dns_rdatatype_format(rdataset.type, typebuf,
1965 sizeof(typebuf));
1966 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
1967 DNS_LOGMODULE_MASTER,
1968 ISC_LOG_WARNING,
1969 "catz: unknown record in catalog "
1970 "zone - %s %s %s(%s) - ignoring",
1971 cname, classbuf, typebuf,
1972 isc_result_totext(result));
1973 }
1974 next:
1975 dns_rdataset_disassociate(&rdataset);
1976 result = dns_rdatasetiter_next(rdsiter);
1977 }
1978
1979 dns_rdatasetiter_destroy(&rdsiter);
1980
1981 dns_db_detachnode(db, &node);
1982 result = dns_dbiterator_next(it);
1983 }
1984
1985 dns_dbiterator_destroy(&it);
1986 dns_db_closeversion(db, &oldzone->dbversion, false);
1987 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER,
1988 ISC_LOG_DEBUG(3),
1989 "catz: update_from_db: iteration finished");
1990
1991 /*
1992 * Finally merge new zone into old zone.
1993 */
1994 result = dns_catz_zones_merge(oldzone, newzone);
1995 dns_catz_zone_detach(&newzone);
1996 if (result != ISC_R_SUCCESS) {
1997 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
1998 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
1999 "catz: failed merging zones: %s",
2000 isc_result_totext(result));
2001
2002 return;
2003 }
2004
2005 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER,
2006 ISC_LOG_DEBUG(3),
2007 "catz: update_from_db: new zone merged");
2008
2009 /*
2010 * When we're doing reconfig and setting a new catalog zone
2011 * from an existing zone we won't have a chance to set up
2012 * update callback in zone_startload or axfr_makedb, but we will
2013 * call onupdate() artificially so we can register the callback here.
2014 */
2015 if (!oldzone->db_registered) {
2016 result = dns_db_updatenotify_register(
2017 db, dns_catz_dbupdate_callback, oldzone->catzs);
2018 if (result == ISC_R_SUCCESS) {
2019 oldzone->db_registered = true;
2020 }
2021 }
2022 }
2023
2024 void
2025 dns_catz_prereconfig(dns_catz_zones_t *catzs) {
2026 isc_result_t result;
2027 isc_ht_iter_t *iter = NULL;
2028
2029 REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
2030
2031 isc_ht_iter_create(catzs->zones, &iter);
2032 for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;
2033 result = isc_ht_iter_next(iter))
2034 {
2035 dns_catz_zone_t *zone = NULL;
2036 isc_ht_iter_current(iter, (void **)&zone);
2037 zone->active = false;
2038 }
2039 INSIST(result == ISC_R_NOMORE);
2040 isc_ht_iter_destroy(&iter);
2041 }
2042
2043 void
2044 dns_catz_postreconfig(dns_catz_zones_t *catzs) {
2045 isc_result_t result;
2046 dns_catz_zone_t *newzone = NULL;
2047 isc_ht_iter_t *iter = NULL;
2048
2049 REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
2050
2051 LOCK(&catzs->lock);
2052 isc_ht_iter_create(catzs->zones, &iter);
2053 for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;) {
2054 dns_catz_zone_t *zone = NULL;
2055
2056 isc_ht_iter_current(iter, (void **)&zone);
2057 if (!zone->active) {
2058 char cname[DNS_NAME_FORMATSIZE];
2059 dns_name_format(&zone->name, cname,
2060 DNS_NAME_FORMATSIZE);
2061 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
2062 DNS_LOGMODULE_MASTER, ISC_LOG_WARNING,
2063 "catz: removing catalog zone %s", cname);
2064
2065 /*
2066 * Merge the old zone with an empty one to remove
2067 * all members.
2068 */
2069 result = dns_catz_new_zone(catzs, &newzone,
2070 &zone->name);
2071 INSIST(result == ISC_R_SUCCESS);
2072 dns_catz_zones_merge(zone, newzone);
2073 dns_catz_zone_detach(&newzone);
2074
2075 /* Make sure that we have an empty catalog zone. */
2076 INSIST(isc_ht_count(zone->entries) == 0);
2077 result = isc_ht_iter_delcurrent_next(iter);
2078 dns_catz_zone_detach(&zone);
2079 } else {
2080 result = isc_ht_iter_next(iter);
2081 }
2082 }
2083 UNLOCK(&catzs->lock);
2084 RUNTIME_CHECK(result == ISC_R_NOMORE);
2085 isc_ht_iter_destroy(&iter);
2086 }
2087
2088 void
2089 dns_catz_get_iterator(dns_catz_zone_t *catz, isc_ht_iter_t **itp) {
2090 REQUIRE(DNS_CATZ_ZONE_VALID(catz));
2091
2092 isc_ht_iter_create(catz->entries, itp);
2093 }
2094