catz.c revision 1.1.1.13 1 /* $NetBSD: catz.c,v 1.1.1.13 2026/01/29 18:19:53 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 #include <stdint.h>
21 #include <stdlib.h>
22
23 #include <isc/async.h>
24 #include <isc/hex.h>
25 #include <isc/loop.h>
26 #include <isc/md.h>
27 #include <isc/mem.h>
28 #include <isc/parseint.h>
29 #include <isc/result.h>
30 #include <isc/util.h>
31 #include <isc/work.h>
32
33 #include <dns/catz.h>
34 #include <dns/dbiterator.h>
35 #include <dns/rdatasetiter.h>
36 #include <dns/view.h>
37 #include <dns/zone.h>
38
39 #define DNS_CATZ_ZONE_MAGIC ISC_MAGIC('c', 'a', 't', 'z')
40 #define DNS_CATZ_ZONES_MAGIC ISC_MAGIC('c', 'a', 't', 's')
41 #define DNS_CATZ_ENTRY_MAGIC ISC_MAGIC('c', 'a', 't', 'e')
42 #define DNS_CATZ_COO_MAGIC ISC_MAGIC('c', 'a', 't', 'c')
43
44 #define DNS_CATZ_ZONE_VALID(catz) ISC_MAGIC_VALID(catz, DNS_CATZ_ZONE_MAGIC)
45 #define DNS_CATZ_ZONES_VALID(catzs) ISC_MAGIC_VALID(catzs, DNS_CATZ_ZONES_MAGIC)
46 #define DNS_CATZ_ENTRY_VALID(entry) ISC_MAGIC_VALID(entry, DNS_CATZ_ENTRY_MAGIC)
47 #define DNS_CATZ_COO_VALID(coo) ISC_MAGIC_VALID(coo, DNS_CATZ_COO_MAGIC)
48
49 #define DNS_CATZ_VERSION_UNDEFINED ((uint32_t)(-1))
50
51 /*%
52 * Change of ownership permissions
53 */
54 struct dns_catz_coo {
55 unsigned int magic;
56 dns_name_t name;
57 isc_refcount_t references;
58 };
59
60 /*%
61 * Single member zone in a catalog
62 */
63 struct dns_catz_entry {
64 unsigned int magic;
65 dns_name_t name;
66 dns_catz_options_t opts;
67 isc_refcount_t references;
68 };
69
70 /*%
71 * Catalog zone
72 */
73 struct dns_catz_zone {
74 unsigned int magic;
75 isc_loop_t *loop;
76 dns_name_t name;
77 dns_catz_zones_t *catzs;
78 dns_rdata_t soa;
79 uint32_t version;
80 /* key in entries is 'mhash', not domain name! */
81 isc_ht_t *entries;
82 /* key in coos is domain name */
83 isc_ht_t *coos;
84
85 /*
86 * defoptions are taken from named.conf
87 * zoneoptions are global options from zone
88 */
89 dns_catz_options_t defoptions;
90 dns_catz_options_t zoneoptions;
91 isc_time_t lastupdated;
92
93 bool updatepending; /* there is an update pending */
94 bool updaterunning; /* there is an update running */
95 isc_result_t updateresult; /* result from the offloaded work */
96 dns_db_t *db; /* zones database */
97 dns_dbversion_t *dbversion; /* version we will be updating to */
98 dns_db_t *updb; /* zones database we're working on */
99 dns_dbversion_t *updbversion; /* version we're working on */
100
101 isc_timer_t *updatetimer;
102
103 bool active;
104 bool broken;
105
106 isc_refcount_t references;
107 isc_mutex_t lock;
108 };
109
110 static void
111 dns__catz_timer_cb(void *);
112 static void
113 dns__catz_timer_start(dns_catz_zone_t *catz);
114 static void
115 dns__catz_timer_stop(void *arg);
116
117 static void
118 dns__catz_update_cb(void *data);
119 static void
120 dns__catz_done_cb(void *data);
121
122 static isc_result_t
123 catz_process_zones_entry(dns_catz_zone_t *catz, dns_rdataset_t *value,
124 dns_label_t *mhash);
125 static isc_result_t
126 catz_process_zones_suboption(dns_catz_zone_t *catz, dns_rdataset_t *value,
127 dns_label_t *mhash, dns_name_t *name);
128 static void
129 catz_entry_add_or_mod(dns_catz_zone_t *catz, isc_ht_t *ht, unsigned char *key,
130 size_t keysize, dns_catz_entry_t *nentry,
131 dns_catz_entry_t *oentry, const char *msg,
132 const char *zname, const char *czname);
133
134 /*%
135 * Collection of catalog zones for a view
136 */
137 struct dns_catz_zones {
138 unsigned int magic;
139 isc_ht_t *zones;
140 isc_mem_t *mctx;
141 isc_refcount_t references;
142 isc_mutex_t lock;
143 dns_catz_zonemodmethods_t *zmm;
144 isc_loopmgr_t *loopmgr;
145 dns_view_t *view;
146 atomic_bool shuttingdown;
147 };
148
149 void
150 dns_catz_options_init(dns_catz_options_t *options) {
151 REQUIRE(options != NULL);
152
153 dns_ipkeylist_init(&options->masters);
154
155 options->allow_query = NULL;
156 options->allow_transfer = NULL;
157
158 options->allow_query = NULL;
159 options->allow_transfer = NULL;
160
161 options->in_memory = false;
162 options->min_update_interval = 5;
163 options->zonedir = NULL;
164 }
165
166 void
167 dns_catz_options_free(dns_catz_options_t *options, isc_mem_t *mctx) {
168 REQUIRE(options != NULL);
169 REQUIRE(mctx != NULL);
170
171 if (options->masters.count != 0) {
172 dns_ipkeylist_clear(mctx, &options->masters);
173 }
174 if (options->zonedir != NULL) {
175 isc_mem_free(mctx, options->zonedir);
176 options->zonedir = NULL;
177 }
178 if (options->allow_query != NULL) {
179 isc_buffer_free(&options->allow_query);
180 }
181 if (options->allow_transfer != NULL) {
182 isc_buffer_free(&options->allow_transfer);
183 }
184 }
185
186 void
187 dns_catz_options_copy(isc_mem_t *mctx, const dns_catz_options_t *src,
188 dns_catz_options_t *dst) {
189 REQUIRE(mctx != NULL);
190 REQUIRE(src != NULL);
191 REQUIRE(dst != NULL);
192 REQUIRE(dst->masters.count == 0);
193 REQUIRE(dst->allow_query == NULL);
194 REQUIRE(dst->allow_transfer == NULL);
195
196 if (src->masters.count != 0) {
197 dns_ipkeylist_copy(mctx, &src->masters, &dst->masters);
198 }
199
200 if (dst->zonedir != NULL) {
201 isc_mem_free(mctx, dst->zonedir);
202 dst->zonedir = NULL;
203 }
204
205 if (src->zonedir != NULL) {
206 dst->zonedir = isc_mem_strdup(mctx, src->zonedir);
207 }
208
209 if (src->allow_query != NULL) {
210 isc_buffer_dup(mctx, &dst->allow_query, src->allow_query);
211 }
212
213 if (src->allow_transfer != NULL) {
214 isc_buffer_dup(mctx, &dst->allow_transfer, src->allow_transfer);
215 }
216 }
217
218 void
219 dns_catz_options_setdefault(isc_mem_t *mctx, const dns_catz_options_t *defaults,
220 dns_catz_options_t *opts) {
221 REQUIRE(mctx != NULL);
222 REQUIRE(defaults != NULL);
223 REQUIRE(opts != NULL);
224
225 if (opts->masters.count == 0 && defaults->masters.count != 0) {
226 dns_ipkeylist_copy(mctx, &defaults->masters, &opts->masters);
227 }
228
229 if (defaults->zonedir != NULL) {
230 opts->zonedir = isc_mem_strdup(mctx, defaults->zonedir);
231 }
232
233 if (opts->allow_query == NULL && defaults->allow_query != NULL) {
234 isc_buffer_dup(mctx, &opts->allow_query, defaults->allow_query);
235 }
236 if (opts->allow_transfer == NULL && defaults->allow_transfer != NULL) {
237 isc_buffer_dup(mctx, &opts->allow_transfer,
238 defaults->allow_transfer);
239 }
240
241 /* This option is always taken from config, so it's always 'default' */
242 opts->in_memory = defaults->in_memory;
243 }
244
245 static dns_catz_coo_t *
246 catz_coo_new(isc_mem_t *mctx, const dns_name_t *domain) {
247 REQUIRE(mctx != NULL);
248 REQUIRE(domain != NULL);
249
250 dns_catz_coo_t *ncoo = isc_mem_get(mctx, sizeof(*ncoo));
251 *ncoo = (dns_catz_coo_t){
252 .magic = DNS_CATZ_COO_MAGIC,
253 };
254 dns_name_init(&ncoo->name, NULL);
255 dns_name_dup(domain, mctx, &ncoo->name);
256 isc_refcount_init(&ncoo->references, 1);
257
258 return ncoo;
259 }
260
261 static void
262 catz_coo_detach(dns_catz_zone_t *catz, dns_catz_coo_t **coop) {
263 dns_catz_coo_t *coo;
264
265 REQUIRE(DNS_CATZ_ZONE_VALID(catz));
266 REQUIRE(coop != NULL && DNS_CATZ_COO_VALID(*coop));
267 coo = *coop;
268 *coop = NULL;
269
270 if (isc_refcount_decrement(&coo->references) == 1) {
271 isc_mem_t *mctx = catz->catzs->mctx;
272 coo->magic = 0;
273 isc_refcount_destroy(&coo->references);
274 if (dns_name_dynamic(&coo->name)) {
275 dns_name_free(&coo->name, mctx);
276 }
277 isc_mem_put(mctx, coo, sizeof(*coo));
278 }
279 }
280
281 static void
282 catz_coo_add(dns_catz_zone_t *catz, dns_catz_entry_t *entry,
283 const dns_name_t *domain) {
284 REQUIRE(DNS_CATZ_ZONE_VALID(catz));
285 REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
286 REQUIRE(domain != NULL);
287
288 /* We are write locked, so the add must succeed if not found */
289 dns_catz_coo_t *coo = NULL;
290 isc_result_t result = isc_ht_find(catz->coos, entry->name.ndata,
291 entry->name.length, (void **)&coo);
292 if (result != ISC_R_SUCCESS) {
293 coo = catz_coo_new(catz->catzs->mctx, domain);
294 result = isc_ht_add(catz->coos, entry->name.ndata,
295 entry->name.length, coo);
296 }
297 INSIST(result == ISC_R_SUCCESS);
298 }
299
300 dns_catz_entry_t *
301 dns_catz_entry_new(isc_mem_t *mctx, const dns_name_t *domain) {
302 REQUIRE(mctx != NULL);
303
304 dns_catz_entry_t *nentry = isc_mem_get(mctx, sizeof(*nentry));
305 *nentry = (dns_catz_entry_t){
306 .magic = DNS_CATZ_ENTRY_MAGIC,
307 };
308
309 dns_name_init(&nentry->name, NULL);
310 if (domain != NULL) {
311 dns_name_dup(domain, mctx, &nentry->name);
312 }
313
314 dns_catz_options_init(&nentry->opts);
315 isc_refcount_init(&nentry->references, 1);
316
317 return nentry;
318 }
319
320 dns_name_t *
321 dns_catz_entry_getname(dns_catz_entry_t *entry) {
322 REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
323 return &entry->name;
324 }
325
326 dns_catz_entry_t *
327 dns_catz_entry_copy(dns_catz_zone_t *catz, const dns_catz_entry_t *entry) {
328 REQUIRE(DNS_CATZ_ZONE_VALID(catz));
329 REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
330
331 dns_catz_entry_t *nentry = dns_catz_entry_new(catz->catzs->mctx,
332 &entry->name);
333
334 dns_catz_options_copy(catz->catzs->mctx, &entry->opts, &nentry->opts);
335
336 return nentry;
337 }
338
339 void
340 dns_catz_entry_attach(dns_catz_entry_t *entry, dns_catz_entry_t **entryp) {
341 REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
342 REQUIRE(entryp != NULL && *entryp == NULL);
343
344 isc_refcount_increment(&entry->references);
345 *entryp = entry;
346 }
347
348 void
349 dns_catz_entry_detach(dns_catz_zone_t *catz, dns_catz_entry_t **entryp) {
350 dns_catz_entry_t *entry;
351
352 REQUIRE(DNS_CATZ_ZONE_VALID(catz));
353 REQUIRE(entryp != NULL && DNS_CATZ_ENTRY_VALID(*entryp));
354 entry = *entryp;
355 *entryp = NULL;
356
357 if (isc_refcount_decrement(&entry->references) == 1) {
358 isc_mem_t *mctx = catz->catzs->mctx;
359 entry->magic = 0;
360 isc_refcount_destroy(&entry->references);
361 dns_catz_options_free(&entry->opts, mctx);
362 if (dns_name_dynamic(&entry->name)) {
363 dns_name_free(&entry->name, mctx);
364 }
365 isc_mem_put(mctx, entry, sizeof(*entry));
366 }
367 }
368
369 bool
370 dns_catz_entry_validate(const dns_catz_entry_t *entry) {
371 REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
372 UNUSED(entry);
373
374 return true;
375 }
376
377 bool
378 dns_catz_entry_cmp(const dns_catz_entry_t *ea, const dns_catz_entry_t *eb) {
379 isc_region_t ra, rb;
380
381 REQUIRE(DNS_CATZ_ENTRY_VALID(ea));
382 REQUIRE(DNS_CATZ_ENTRY_VALID(eb));
383
384 if (ea == eb) {
385 return true;
386 }
387
388 if (ea->opts.masters.count != eb->opts.masters.count) {
389 return false;
390 }
391
392 if (memcmp(ea->opts.masters.addrs, eb->opts.masters.addrs,
393 ea->opts.masters.count * sizeof(isc_sockaddr_t)))
394 {
395 return false;
396 }
397
398 for (size_t i = 0; i < eb->opts.masters.count; i++) {
399 if ((ea->opts.masters.keys[i] == NULL) !=
400 (eb->opts.masters.keys[i] == NULL))
401 {
402 return false;
403 }
404 if (ea->opts.masters.keys[i] == NULL) {
405 continue;
406 }
407 if (!dns_name_equal(ea->opts.masters.keys[i],
408 eb->opts.masters.keys[i]))
409 {
410 return false;
411 }
412 }
413
414 for (size_t i = 0; i < eb->opts.masters.count; i++) {
415 if ((ea->opts.masters.tlss[i] == NULL) !=
416 (eb->opts.masters.tlss[i] == NULL))
417 {
418 return false;
419 }
420 if (ea->opts.masters.tlss[i] == NULL) {
421 continue;
422 }
423 if (!dns_name_equal(ea->opts.masters.tlss[i],
424 eb->opts.masters.tlss[i]))
425 {
426 return false;
427 }
428 }
429
430 /* If one is NULL and the other isn't, the entries don't match */
431 if ((ea->opts.allow_query == NULL) != (eb->opts.allow_query == NULL)) {
432 return false;
433 }
434
435 /* If one is non-NULL, then they both are */
436 if (ea->opts.allow_query != NULL) {
437 isc_buffer_usedregion(ea->opts.allow_query, &ra);
438 isc_buffer_usedregion(eb->opts.allow_query, &rb);
439 if (isc_region_compare(&ra, &rb)) {
440 return false;
441 }
442 }
443
444 /* Repeat the above checks with allow_transfer */
445 if ((ea->opts.allow_transfer == NULL) !=
446 (eb->opts.allow_transfer == NULL))
447 {
448 return false;
449 }
450
451 if (ea->opts.allow_transfer != NULL) {
452 isc_buffer_usedregion(ea->opts.allow_transfer, &ra);
453 isc_buffer_usedregion(eb->opts.allow_transfer, &rb);
454 if (isc_region_compare(&ra, &rb)) {
455 return false;
456 }
457 }
458
459 return true;
460 }
461
462 dns_name_t *
463 dns_catz_zone_getname(dns_catz_zone_t *catz) {
464 REQUIRE(DNS_CATZ_ZONE_VALID(catz));
465
466 return &catz->name;
467 }
468
469 dns_catz_options_t *
470 dns_catz_zone_getdefoptions(dns_catz_zone_t *catz) {
471 REQUIRE(DNS_CATZ_ZONE_VALID(catz));
472
473 return &catz->defoptions;
474 }
475
476 void
477 dns_catz_zone_resetdefoptions(dns_catz_zone_t *catz) {
478 REQUIRE(DNS_CATZ_ZONE_VALID(catz));
479
480 dns_catz_options_free(&catz->defoptions, catz->catzs->mctx);
481 dns_catz_options_init(&catz->defoptions);
482 }
483
484 /*%<
485 * Merge 'newcatz' into 'catz', calling addzone/delzone/modzone
486 * (from catz->catzs->zmm) for appropriate member zones.
487 *
488 * Requires:
489 * \li 'catz' is a valid dns_catz_zone_t.
490 * \li 'newcatz' is a valid dns_catz_zone_t.
491 *
492 */
493 static isc_result_t
494 dns__catz_zones_merge(dns_catz_zone_t *catz, dns_catz_zone_t *newcatz) {
495 isc_result_t result;
496 isc_ht_iter_t *iter1 = NULL, *iter2 = NULL;
497 isc_ht_iter_t *iteradd = NULL, *itermod = NULL;
498 isc_ht_t *toadd = NULL, *tomod = NULL;
499 bool delcur = false;
500 char czname[DNS_NAME_FORMATSIZE];
501 char zname[DNS_NAME_FORMATSIZE];
502 dns_catz_zoneop_fn_t addzone, modzone, delzone;
503
504 REQUIRE(DNS_CATZ_ZONE_VALID(catz));
505 REQUIRE(DNS_CATZ_ZONE_VALID(newcatz));
506
507 LOCK(&catz->lock);
508
509 /* TODO verify the new zone first! */
510
511 addzone = catz->catzs->zmm->addzone;
512 modzone = catz->catzs->zmm->modzone;
513 delzone = catz->catzs->zmm->delzone;
514
515 /* Copy zoneoptions from newcatz into catz. */
516
517 dns_catz_options_free(&catz->zoneoptions, catz->catzs->mctx);
518 dns_catz_options_copy(catz->catzs->mctx, &newcatz->zoneoptions,
519 &catz->zoneoptions);
520 dns_catz_options_setdefault(catz->catzs->mctx, &catz->defoptions,
521 &catz->zoneoptions);
522
523 dns_name_format(&catz->name, czname, DNS_NAME_FORMATSIZE);
524
525 isc_ht_init(&toadd, catz->catzs->mctx, 1, ISC_HT_CASE_SENSITIVE);
526 isc_ht_init(&tomod, catz->catzs->mctx, 1, ISC_HT_CASE_SENSITIVE);
527 isc_ht_iter_create(newcatz->entries, &iter1);
528 isc_ht_iter_create(catz->entries, &iter2);
529
530 /*
531 * We can create those iterators now, even though toadd and tomod are
532 * empty
533 */
534 isc_ht_iter_create(toadd, &iteradd);
535 isc_ht_iter_create(tomod, &itermod);
536
537 /*
538 * First - walk the new zone and find all nodes that are not in the
539 * old zone, or are in both zones and are modified.
540 */
541 for (result = isc_ht_iter_first(iter1); result == ISC_R_SUCCESS;
542 result = delcur ? isc_ht_iter_delcurrent_next(iter1)
543 : isc_ht_iter_next(iter1))
544 {
545 isc_result_t find_result;
546 dns_catz_zone_t *parentcatz = NULL;
547 dns_catz_entry_t *nentry = NULL;
548 dns_catz_entry_t *oentry = NULL;
549 dns_zone_t *zone = NULL;
550 unsigned char *key = NULL;
551 size_t keysize;
552 delcur = false;
553
554 isc_ht_iter_current(iter1, (void **)&nentry);
555 isc_ht_iter_currentkey(iter1, &key, &keysize);
556
557 /*
558 * Spurious record that came from suboption without main
559 * record, removed.
560 * xxxwpk: make it a separate verification phase?
561 */
562 if (dns_name_countlabels(&nentry->name) == 0) {
563 dns_catz_entry_detach(newcatz, &nentry);
564 delcur = true;
565 continue;
566 }
567
568 dns_name_format(&nentry->name, zname, DNS_NAME_FORMATSIZE);
569
570 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
571 DNS_LOGMODULE_MASTER, ISC_LOG_DEBUG(3),
572 "catz: iterating over '%s' from catalog '%s'",
573 zname, czname);
574 dns_catz_options_setdefault(catz->catzs->mctx,
575 &catz->zoneoptions, &nentry->opts);
576
577 /* Try to find the zone in the view */
578 find_result = dns_view_findzone(catz->catzs->view,
579 dns_catz_entry_getname(nentry),
580 DNS_ZTFIND_EXACT, &zone);
581 if (find_result == ISC_R_SUCCESS) {
582 dns_catz_coo_t *coo = NULL;
583 char pczname[DNS_NAME_FORMATSIZE];
584 bool parentcatz_locked = false;
585
586 /*
587 * Change of ownership (coo) processing, if required
588 */
589 parentcatz = dns_zone_get_parentcatz(zone);
590 if (parentcatz != NULL && parentcatz != catz) {
591 UNLOCK(&catz->lock);
592 LOCK(&parentcatz->lock);
593 parentcatz_locked = true;
594 }
595 if (parentcatz_locked &&
596 isc_ht_find(parentcatz->coos, nentry->name.ndata,
597 nentry->name.length,
598 (void **)&coo) == ISC_R_SUCCESS &&
599 dns_name_equal(&coo->name, &catz->name))
600 {
601 dns_name_format(&parentcatz->name, pczname,
602 DNS_NAME_FORMATSIZE);
603 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
604 DNS_LOGMODULE_MASTER,
605 ISC_LOG_DEBUG(3),
606 "catz: zone '%s' "
607 "change of ownership from "
608 "'%s' to '%s'",
609 zname, pczname, czname);
610 result = delzone(nentry, parentcatz,
611 parentcatz->catzs->view,
612 parentcatz->catzs->zmm->udata);
613 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
614 DNS_LOGMODULE_MASTER,
615 ISC_LOG_INFO,
616 "catz: deleting zone '%s' "
617 "from catalog '%s' - %s",
618 zname, pczname,
619 isc_result_totext(result));
620 }
621 if (parentcatz_locked) {
622 UNLOCK(&parentcatz->lock);
623 LOCK(&catz->lock);
624 }
625 dns_zone_detach(&zone);
626 }
627
628 /* Try to find the zone in the old catalog zone */
629 result = isc_ht_find(catz->entries, key, (uint32_t)keysize,
630 (void **)&oentry);
631 if (result != ISC_R_SUCCESS) {
632 if (find_result == ISC_R_SUCCESS && parentcatz == catz)
633 {
634 /*
635 * This means that the zone's unique label
636 * has been changed, in that case we must
637 * reset the zone's internal state by removing
638 * and re-adding it.
639 *
640 * Scheduling the addition now, the removal will
641 * be scheduled below, when walking the old
642 * zone for remaining entries, and then we will
643 * perform deletions earlier than additions and
644 * modifications.
645 */
646 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
647 DNS_LOGMODULE_MASTER,
648 ISC_LOG_INFO,
649 "catz: zone '%s' unique label "
650 "has changed, reset state",
651 zname);
652 }
653
654 catz_entry_add_or_mod(catz, toadd, key, keysize, nentry,
655 NULL, "adding", zname, czname);
656 continue;
657 }
658
659 if (find_result != ISC_R_SUCCESS) {
660 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
661 DNS_LOGMODULE_MASTER, ISC_LOG_DEBUG(3),
662 "catz: zone '%s' was expected to exist "
663 "but can not be found, will be restored",
664 zname);
665 catz_entry_add_or_mod(catz, toadd, key, keysize, nentry,
666 oentry, "adding", zname, czname);
667 continue;
668 }
669
670 if (dns_catz_entry_cmp(oentry, nentry) != true) {
671 catz_entry_add_or_mod(catz, tomod, key, keysize, nentry,
672 oentry, "modifying", zname,
673 czname);
674 continue;
675 }
676
677 /*
678 * Delete the old entry so that it won't accidentally be
679 * removed as a non-existing entry below.
680 */
681 dns_catz_entry_detach(catz, &oentry);
682 result = isc_ht_delete(catz->entries, key, (uint32_t)keysize);
683 RUNTIME_CHECK(result == ISC_R_SUCCESS);
684 }
685 RUNTIME_CHECK(result == ISC_R_NOMORE);
686 isc_ht_iter_destroy(&iter1);
687
688 /*
689 * Then - walk the old zone; only deleted entries should remain.
690 */
691 for (result = isc_ht_iter_first(iter2); result == ISC_R_SUCCESS;
692 result = isc_ht_iter_delcurrent_next(iter2))
693 {
694 dns_catz_entry_t *entry = NULL;
695 isc_ht_iter_current(iter2, (void **)&entry);
696
697 dns_name_format(&entry->name, zname, DNS_NAME_FORMATSIZE);
698 result = delzone(entry, catz, catz->catzs->view,
699 catz->catzs->zmm->udata);
700 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
701 DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
702 "catz: deleting zone '%s' from catalog '%s' - %s",
703 zname, czname, isc_result_totext(result));
704 dns_catz_entry_detach(catz, &entry);
705 }
706 RUNTIME_CHECK(result == ISC_R_NOMORE);
707 isc_ht_iter_destroy(&iter2);
708 /* At this moment catz->entries has to be be empty. */
709 INSIST(isc_ht_count(catz->entries) == 0);
710 isc_ht_destroy(&catz->entries);
711
712 for (result = isc_ht_iter_first(iteradd); result == ISC_R_SUCCESS;
713 result = isc_ht_iter_delcurrent_next(iteradd))
714 {
715 dns_catz_entry_t *entry = NULL;
716 isc_ht_iter_current(iteradd, (void **)&entry);
717
718 dns_name_format(&entry->name, zname, DNS_NAME_FORMATSIZE);
719 result = addzone(entry, catz, catz->catzs->view,
720 catz->catzs->zmm->udata);
721 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
722 DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
723 "catz: adding zone '%s' from catalog "
724 "'%s' - %s",
725 zname, czname, isc_result_totext(result));
726 }
727
728 for (result = isc_ht_iter_first(itermod); result == ISC_R_SUCCESS;
729 result = isc_ht_iter_delcurrent_next(itermod))
730 {
731 dns_catz_entry_t *entry = NULL;
732 isc_ht_iter_current(itermod, (void **)&entry);
733
734 dns_name_format(&entry->name, zname, DNS_NAME_FORMATSIZE);
735 result = modzone(entry, catz, catz->catzs->view,
736 catz->catzs->zmm->udata);
737 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
738 DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
739 "catz: modifying zone '%s' from catalog "
740 "'%s' - %s",
741 zname, czname, isc_result_totext(result));
742 }
743
744 catz->entries = newcatz->entries;
745 newcatz->entries = NULL;
746
747 /*
748 * We do not need to merge old coo (change of ownership) permission
749 * records with the new ones, just replace them.
750 */
751 if (catz->coos != NULL && newcatz->coos != NULL) {
752 isc_ht_iter_t *iter = NULL;
753
754 isc_ht_iter_create(catz->coos, &iter);
755 for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;
756 result = isc_ht_iter_delcurrent_next(iter))
757 {
758 dns_catz_coo_t *coo = NULL;
759
760 isc_ht_iter_current(iter, (void **)&coo);
761 catz_coo_detach(catz, &coo);
762 }
763 INSIST(result == ISC_R_NOMORE);
764 isc_ht_iter_destroy(&iter);
765
766 /* The hashtable has to be empty now. */
767 INSIST(isc_ht_count(catz->coos) == 0);
768 isc_ht_destroy(&catz->coos);
769
770 catz->coos = newcatz->coos;
771 newcatz->coos = NULL;
772 }
773
774 result = ISC_R_SUCCESS;
775
776 isc_ht_iter_destroy(&iteradd);
777 isc_ht_iter_destroy(&itermod);
778 isc_ht_destroy(&toadd);
779 isc_ht_destroy(&tomod);
780
781 UNLOCK(&catz->lock);
782
783 return result;
784 }
785
786 dns_catz_zones_t *
787 dns_catz_zones_new(isc_mem_t *mctx, isc_loopmgr_t *loopmgr,
788 dns_catz_zonemodmethods_t *zmm) {
789 REQUIRE(mctx != NULL);
790 REQUIRE(loopmgr != NULL);
791 REQUIRE(zmm != NULL);
792
793 dns_catz_zones_t *catzs = isc_mem_get(mctx, sizeof(*catzs));
794 *catzs = (dns_catz_zones_t){ .loopmgr = loopmgr,
795 .zmm = zmm,
796 .magic = DNS_CATZ_ZONES_MAGIC };
797
798 isc_mutex_init(&catzs->lock);
799 isc_refcount_init(&catzs->references, 1);
800 isc_ht_init(&catzs->zones, mctx, 4, ISC_HT_CASE_SENSITIVE);
801 isc_mem_attach(mctx, &catzs->mctx);
802
803 return catzs;
804 }
805
806 void *
807 dns_catz_zones_get_udata(dns_catz_zones_t *catzs) {
808 REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
809
810 return catzs->zmm->udata;
811 }
812
813 void
814 dns_catz_catzs_set_view(dns_catz_zones_t *catzs, dns_view_t *view) {
815 REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
816 REQUIRE(DNS_VIEW_VALID(view));
817 /* Either it's a new one or it's being reconfigured. */
818 REQUIRE(catzs->view == NULL || !strcmp(catzs->view->name, view->name));
819
820 if (catzs->view == NULL) {
821 dns_view_weakattach(view, &catzs->view);
822 } else if (catzs->view != view) {
823 dns_view_weakdetach(&catzs->view);
824 dns_view_weakattach(view, &catzs->view);
825 }
826 }
827
828 dns_catz_zone_t *
829 dns_catz_zone_new(dns_catz_zones_t *catzs, const dns_name_t *name) {
830 REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
831 REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
832
833 dns_catz_zone_t *catz = isc_mem_get(catzs->mctx, sizeof(*catz));
834 *catz = (dns_catz_zone_t){ .active = true,
835 .version = DNS_CATZ_VERSION_UNDEFINED,
836 .magic = DNS_CATZ_ZONE_MAGIC };
837
838 dns_catz_zones_attach(catzs, &catz->catzs);
839 isc_mutex_init(&catz->lock);
840 isc_refcount_init(&catz->references, 1);
841 isc_ht_init(&catz->entries, catzs->mctx, 4, ISC_HT_CASE_SENSITIVE);
842 isc_ht_init(&catz->coos, catzs->mctx, 4, ISC_HT_CASE_INSENSITIVE);
843 isc_time_settoepoch(&catz->lastupdated);
844 dns_catz_options_init(&catz->defoptions);
845 dns_catz_options_init(&catz->zoneoptions);
846 dns_name_init(&catz->name, NULL);
847 dns_name_dup(name, catzs->mctx, &catz->name);
848
849 return catz;
850 }
851
852 static void
853 dns__catz_timer_start(dns_catz_zone_t *catz) {
854 uint64_t tdiff;
855 isc_interval_t interval;
856 isc_time_t now;
857
858 REQUIRE(DNS_CATZ_ZONE_VALID(catz));
859
860 now = isc_time_now();
861 tdiff = isc_time_microdiff(&now, &catz->lastupdated) / 1000000;
862 if (tdiff < catz->defoptions.min_update_interval) {
863 uint64_t defer = catz->defoptions.min_update_interval - tdiff;
864 char dname[DNS_NAME_FORMATSIZE];
865
866 dns_name_format(&catz->name, dname, DNS_NAME_FORMATSIZE);
867 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
868 DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
869 "catz: %s: new zone version came "
870 "too soon, deferring update for "
871 "%" PRIu64 " seconds",
872 dname, defer);
873 isc_interval_set(&interval, (unsigned int)defer, 0);
874 } else {
875 isc_interval_set(&interval, 0, 0);
876 }
877
878 catz->loop = isc_loop();
879
880 isc_timer_create(catz->loop, dns__catz_timer_cb, catz,
881 &catz->updatetimer);
882 isc_timer_start(catz->updatetimer, isc_timertype_once, &interval);
883 }
884
885 static void
886 dns__catz_timer_stop(void *arg) {
887 dns_catz_zone_t *catz = arg;
888 REQUIRE(DNS_CATZ_ZONE_VALID(catz));
889
890 isc_timer_stop(catz->updatetimer);
891 isc_timer_destroy(&catz->updatetimer);
892 catz->loop = NULL;
893
894 dns_catz_zone_detach(&catz);
895 }
896
897 isc_result_t
898 dns_catz_zone_add(dns_catz_zones_t *catzs, const dns_name_t *name,
899 dns_catz_zone_t **catzp) {
900 REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
901 REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
902 REQUIRE(catzp != NULL && *catzp == NULL);
903
904 dns_catz_zone_t *catz = NULL;
905 isc_result_t result;
906 char zname[DNS_NAME_FORMATSIZE];
907
908 dns_name_format(name, zname, DNS_NAME_FORMATSIZE);
909 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER,
910 ISC_LOG_DEBUG(3), "catz: dns_catz_zone_add %s", zname);
911
912 LOCK(&catzs->lock);
913
914 /*
915 * This function is called only during a (re)configuration, while
916 * 'catzs->zones' can become NULL only during shutdown.
917 */
918 INSIST(catzs->zones != NULL);
919 INSIST(!atomic_load(&catzs->shuttingdown));
920
921 result = isc_ht_find(catzs->zones, name->ndata, name->length,
922 (void **)&catz);
923 switch (result) {
924 case ISC_R_SUCCESS:
925 INSIST(!catz->active);
926 catz->active = true;
927 result = ISC_R_EXISTS;
928 break;
929 case ISC_R_NOTFOUND:
930 catz = dns_catz_zone_new(catzs, name);
931
932 result = isc_ht_add(catzs->zones, catz->name.ndata,
933 catz->name.length, catz);
934 INSIST(result == ISC_R_SUCCESS);
935 break;
936 default:
937 UNREACHABLE();
938 }
939
940 UNLOCK(&catzs->lock);
941
942 *catzp = catz;
943
944 return result;
945 }
946
947 dns_catz_zone_t *
948 dns_catz_zone_get(dns_catz_zones_t *catzs, const dns_name_t *name) {
949 isc_result_t result;
950 dns_catz_zone_t *found = NULL;
951
952 REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
953 REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
954
955 LOCK(&catzs->lock);
956 if (catzs->zones == NULL) {
957 UNLOCK(&catzs->lock);
958 return NULL;
959 }
960 result = isc_ht_find(catzs->zones, name->ndata, name->length,
961 (void **)&found);
962 UNLOCK(&catzs->lock);
963 if (result != ISC_R_SUCCESS) {
964 return NULL;
965 }
966
967 return found;
968 }
969
970 static void
971 dns__catz_zone_shutdown(dns_catz_zone_t *catz) {
972 /* lock must be locked */
973 if (catz->updatetimer != NULL) {
974 /* Don't wait for timer to trigger for shutdown */
975 INSIST(catz->loop != NULL);
976
977 isc_async_run(catz->loop, dns__catz_timer_stop, catz);
978 } else {
979 dns_catz_zone_detach(&catz);
980 }
981 }
982
983 static void
984 dns__catz_zone_destroy(dns_catz_zone_t *catz) {
985 isc_mem_t *mctx = catz->catzs->mctx;
986
987 if (catz->entries != NULL) {
988 isc_ht_iter_t *iter = NULL;
989 isc_result_t result;
990 isc_ht_iter_create(catz->entries, &iter);
991 for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;
992 result = isc_ht_iter_delcurrent_next(iter))
993 {
994 dns_catz_entry_t *entry = NULL;
995
996 isc_ht_iter_current(iter, (void **)&entry);
997 dns_catz_entry_detach(catz, &entry);
998 }
999 INSIST(result == ISC_R_NOMORE);
1000 isc_ht_iter_destroy(&iter);
1001
1002 /* The hashtable has to be empty now. */
1003 INSIST(isc_ht_count(catz->entries) == 0);
1004 isc_ht_destroy(&catz->entries);
1005 }
1006 if (catz->coos != NULL) {
1007 isc_ht_iter_t *iter = NULL;
1008 isc_result_t result;
1009 isc_ht_iter_create(catz->coos, &iter);
1010 for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;
1011 result = isc_ht_iter_delcurrent_next(iter))
1012 {
1013 dns_catz_coo_t *coo = NULL;
1014
1015 isc_ht_iter_current(iter, (void **)&coo);
1016 catz_coo_detach(catz, &coo);
1017 }
1018 INSIST(result == ISC_R_NOMORE);
1019 isc_ht_iter_destroy(&iter);
1020
1021 /* The hashtable has to be empty now. */
1022 INSIST(isc_ht_count(catz->coos) == 0);
1023 isc_ht_destroy(&catz->coos);
1024 }
1025 catz->magic = 0;
1026 isc_mutex_destroy(&catz->lock);
1027
1028 if (catz->updatetimer != NULL) {
1029 isc_timer_async_destroy(&catz->updatetimer);
1030 }
1031
1032 if (catz->db != NULL) {
1033 if (catz->dbversion != NULL) {
1034 dns_db_closeversion(catz->db, &catz->dbversion, false);
1035 }
1036 dns_db_updatenotify_unregister(
1037 catz->db, dns_catz_dbupdate_callback, catz->catzs);
1038 dns_db_detach(&catz->db);
1039 }
1040
1041 INSIST(!catz->updaterunning);
1042
1043 dns_name_free(&catz->name, mctx);
1044 dns_catz_options_free(&catz->defoptions, mctx);
1045 dns_catz_options_free(&catz->zoneoptions, mctx);
1046
1047 dns_catz_zones_detach(&catz->catzs);
1048
1049 isc_mem_put(mctx, catz, sizeof(*catz));
1050 }
1051
1052 static void
1053 dns__catz_zones_destroy(dns_catz_zones_t *catzs) {
1054 REQUIRE(atomic_load(&catzs->shuttingdown));
1055 REQUIRE(catzs->zones == NULL);
1056
1057 catzs->magic = 0;
1058 isc_mutex_destroy(&catzs->lock);
1059 if (catzs->view != NULL) {
1060 dns_view_weakdetach(&catzs->view);
1061 }
1062 isc_mem_putanddetach(&catzs->mctx, catzs, sizeof(*catzs));
1063 }
1064
1065 void
1066 dns_catz_zones_shutdown(dns_catz_zones_t *catzs) {
1067 REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
1068
1069 if (!atomic_compare_exchange_strong(&catzs->shuttingdown,
1070 &(bool){ false }, true))
1071 {
1072 return;
1073 }
1074
1075 LOCK(&catzs->lock);
1076 if (catzs->zones != NULL) {
1077 isc_ht_iter_t *iter = NULL;
1078 isc_result_t result;
1079 isc_ht_iter_create(catzs->zones, &iter);
1080 for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;)
1081 {
1082 dns_catz_zone_t *catz = NULL;
1083 isc_ht_iter_current(iter, (void **)&catz);
1084 result = isc_ht_iter_delcurrent_next(iter);
1085 dns__catz_zone_shutdown(catz);
1086 }
1087 INSIST(result == ISC_R_NOMORE);
1088 isc_ht_iter_destroy(&iter);
1089 INSIST(isc_ht_count(catzs->zones) == 0);
1090 isc_ht_destroy(&catzs->zones);
1091 }
1092 UNLOCK(&catzs->lock);
1093 }
1094
1095 #ifdef DNS_CATZ_TRACE
1096 ISC_REFCOUNT_TRACE_IMPL(dns_catz_zone, dns__catz_zone_destroy);
1097 ISC_REFCOUNT_TRACE_IMPL(dns_catz_zones, dns__catz_zones_destroy);
1098 #else
1099 ISC_REFCOUNT_IMPL(dns_catz_zone, dns__catz_zone_destroy);
1100 ISC_REFCOUNT_IMPL(dns_catz_zones, dns__catz_zones_destroy);
1101 #endif
1102
1103 typedef enum {
1104 CATZ_OPT_NONE,
1105 CATZ_OPT_ZONES,
1106 CATZ_OPT_COO,
1107 CATZ_OPT_VERSION,
1108 CATZ_OPT_CUSTOM_START, /* CATZ custom properties must go below this */
1109 CATZ_OPT_EXT,
1110 CATZ_OPT_PRIMARIES,
1111 CATZ_OPT_ALLOW_QUERY,
1112 CATZ_OPT_ALLOW_TRANSFER,
1113 } catz_opt_t;
1114
1115 static bool
1116 catz_opt_cmp(const dns_label_t *option, const char *opt) {
1117 size_t len = strlen(opt);
1118
1119 if (option->length - 1 == len &&
1120 memcmp(opt, option->base + 1, len) == 0)
1121 {
1122 return true;
1123 } else {
1124 return false;
1125 }
1126 }
1127
1128 static catz_opt_t
1129 catz_get_option(const dns_label_t *option) {
1130 if (catz_opt_cmp(option, "ext")) {
1131 return CATZ_OPT_EXT;
1132 } else if (catz_opt_cmp(option, "zones")) {
1133 return CATZ_OPT_ZONES;
1134 } else if (catz_opt_cmp(option, "masters") ||
1135 catz_opt_cmp(option, "primaries"))
1136 {
1137 return CATZ_OPT_PRIMARIES;
1138 } else if (catz_opt_cmp(option, "allow-query")) {
1139 return CATZ_OPT_ALLOW_QUERY;
1140 } else if (catz_opt_cmp(option, "allow-transfer")) {
1141 return CATZ_OPT_ALLOW_TRANSFER;
1142 } else if (catz_opt_cmp(option, "coo")) {
1143 return CATZ_OPT_COO;
1144 } else if (catz_opt_cmp(option, "version")) {
1145 return CATZ_OPT_VERSION;
1146 } else {
1147 return CATZ_OPT_NONE;
1148 }
1149 }
1150
1151 static isc_result_t
1152 catz_process_zones(dns_catz_zone_t *catz, dns_rdataset_t *value,
1153 dns_name_t *name) {
1154 dns_label_t mhash;
1155 dns_name_t opt;
1156
1157 REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1158 REQUIRE(DNS_RDATASET_VALID(value));
1159 REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
1160
1161 if (name->labels == 0) {
1162 return ISC_R_FAILURE;
1163 }
1164
1165 dns_name_getlabel(name, name->labels - 1, &mhash);
1166
1167 if (name->labels == 1) {
1168 return catz_process_zones_entry(catz, value, &mhash);
1169 } else {
1170 dns_name_init(&opt, NULL);
1171 dns_name_split(name, 1, &opt, NULL);
1172 return catz_process_zones_suboption(catz, value, &mhash, &opt);
1173 }
1174 }
1175
1176 static isc_result_t
1177 catz_process_coo(dns_catz_zone_t *catz, dns_label_t *mhash,
1178 dns_rdataset_t *value) {
1179 isc_result_t result;
1180 dns_rdata_t rdata;
1181 dns_rdata_ptr_t ptr;
1182 dns_catz_entry_t *entry = NULL;
1183
1184 REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1185 REQUIRE(mhash != NULL);
1186 REQUIRE(DNS_RDATASET_VALID(value));
1187
1188 /* Change of Ownership was introduced in version "2" of the schema. */
1189 if (catz->version < 2) {
1190 return ISC_R_FAILURE;
1191 }
1192
1193 if (value->type != dns_rdatatype_ptr) {
1194 return ISC_R_FAILURE;
1195 }
1196
1197 if (dns_rdataset_count(value) != 1) {
1198 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
1199 DNS_LOGMODULE_MASTER, ISC_LOG_WARNING,
1200 "catz: 'coo' property PTR RRset contains "
1201 "more than one record, which is invalid");
1202 catz->broken = true;
1203 return ISC_R_FAILURE;
1204 }
1205
1206 result = dns_rdataset_first(value);
1207 if (result != ISC_R_SUCCESS) {
1208 return result;
1209 }
1210
1211 dns_rdata_init(&rdata);
1212 dns_rdataset_current(value, &rdata);
1213
1214 result = dns_rdata_tostruct(&rdata, &ptr, NULL);
1215 if (result != ISC_R_SUCCESS) {
1216 return result;
1217 }
1218
1219 if (dns_name_countlabels(&ptr.ptr) == 0) {
1220 result = ISC_R_FAILURE;
1221 goto cleanup;
1222 }
1223
1224 result = isc_ht_find(catz->entries, mhash->base, mhash->length,
1225 (void **)&entry);
1226 if (result != ISC_R_SUCCESS) {
1227 /* The entry was not found .*/
1228 goto cleanup;
1229 }
1230
1231 if (dns_name_countlabels(&entry->name) == 0) {
1232 result = ISC_R_FAILURE;
1233 goto cleanup;
1234 }
1235
1236 catz_coo_add(catz, entry, &ptr.ptr);
1237
1238 cleanup:
1239 dns_rdata_freestruct(&ptr);
1240
1241 return result;
1242 }
1243
1244 static isc_result_t
1245 catz_process_zones_entry(dns_catz_zone_t *catz, dns_rdataset_t *value,
1246 dns_label_t *mhash) {
1247 isc_result_t result;
1248 dns_rdata_t rdata;
1249 dns_rdata_ptr_t ptr;
1250 dns_catz_entry_t *entry = NULL;
1251
1252 if (value->type != dns_rdatatype_ptr) {
1253 return ISC_R_FAILURE;
1254 }
1255
1256 if (dns_rdataset_count(value) != 1) {
1257 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
1258 DNS_LOGMODULE_MASTER, ISC_LOG_WARNING,
1259 "catz: member zone PTR RRset contains "
1260 "more than one record, which is invalid");
1261 catz->broken = true;
1262 return ISC_R_FAILURE;
1263 }
1264
1265 result = dns_rdataset_first(value);
1266 if (result != ISC_R_SUCCESS) {
1267 return result;
1268 }
1269
1270 dns_rdata_init(&rdata);
1271 dns_rdataset_current(value, &rdata);
1272
1273 result = dns_rdata_tostruct(&rdata, &ptr, NULL);
1274 if (result != ISC_R_SUCCESS) {
1275 return result;
1276 }
1277
1278 result = isc_ht_find(catz->entries, mhash->base, mhash->length,
1279 (void **)&entry);
1280 if (result == ISC_R_SUCCESS) {
1281 if (dns_name_countlabels(&entry->name) != 0) {
1282 /* We have a duplicate. */
1283 dns_rdata_freestruct(&ptr);
1284 return ISC_R_FAILURE;
1285 } else {
1286 dns_name_dup(&ptr.ptr, catz->catzs->mctx, &entry->name);
1287 }
1288 } else {
1289 entry = dns_catz_entry_new(catz->catzs->mctx, &ptr.ptr);
1290
1291 result = isc_ht_add(catz->entries, mhash->base, mhash->length,
1292 entry);
1293 }
1294 INSIST(result == ISC_R_SUCCESS);
1295
1296 dns_rdata_freestruct(&ptr);
1297
1298 return ISC_R_SUCCESS;
1299 }
1300
1301 static isc_result_t
1302 catz_process_version(dns_catz_zone_t *catz, dns_rdataset_t *value) {
1303 isc_result_t result;
1304 dns_rdata_t rdata;
1305 dns_rdata_txt_t rdatatxt;
1306 dns_rdata_txt_string_t rdatastr;
1307 uint32_t tversion;
1308 char t[16];
1309
1310 REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1311 REQUIRE(DNS_RDATASET_VALID(value));
1312
1313 if (value->type != dns_rdatatype_txt) {
1314 return ISC_R_FAILURE;
1315 }
1316
1317 if (dns_rdataset_count(value) != 1) {
1318 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
1319 DNS_LOGMODULE_MASTER, ISC_LOG_WARNING,
1320 "catz: 'version' property TXT RRset contains "
1321 "more than one record, which is invalid");
1322 catz->broken = true;
1323 return ISC_R_FAILURE;
1324 }
1325
1326 result = dns_rdataset_first(value);
1327 if (result != ISC_R_SUCCESS) {
1328 return result;
1329 }
1330
1331 dns_rdata_init(&rdata);
1332 dns_rdataset_current(value, &rdata);
1333
1334 result = dns_rdata_tostruct(&rdata, &rdatatxt, NULL);
1335 if (result != ISC_R_SUCCESS) {
1336 return result;
1337 }
1338
1339 result = dns_rdata_txt_first(&rdatatxt);
1340 if (result != ISC_R_SUCCESS) {
1341 goto cleanup;
1342 }
1343
1344 result = dns_rdata_txt_current(&rdatatxt, &rdatastr);
1345 if (result != ISC_R_SUCCESS) {
1346 goto cleanup;
1347 }
1348
1349 result = dns_rdata_txt_next(&rdatatxt);
1350 if (result != ISC_R_NOMORE) {
1351 result = ISC_R_FAILURE;
1352 goto cleanup;
1353 }
1354 if (rdatastr.length > 15) {
1355 result = ISC_R_BADNUMBER;
1356 goto cleanup;
1357 }
1358 memmove(t, rdatastr.data, rdatastr.length);
1359 t[rdatastr.length] = 0;
1360 result = isc_parse_uint32(&tversion, t, 10);
1361 if (result != ISC_R_SUCCESS) {
1362 goto cleanup;
1363 }
1364 catz->version = tversion;
1365 result = ISC_R_SUCCESS;
1366
1367 cleanup:
1368 dns_rdata_freestruct(&rdatatxt);
1369 if (result != ISC_R_SUCCESS) {
1370 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
1371 DNS_LOGMODULE_MASTER, ISC_LOG_WARNING,
1372 "catz: invalid record for the catalog "
1373 "zone version property");
1374 catz->broken = true;
1375 }
1376 return result;
1377 }
1378
1379 static isc_result_t
1380 catz_process_primaries(dns_catz_zone_t *catz, dns_ipkeylist_t *ipkl,
1381 dns_rdataset_t *value, dns_name_t *name) {
1382 isc_result_t result;
1383 dns_rdata_t rdata;
1384 dns_rdata_in_a_t rdata_a;
1385 dns_rdata_in_aaaa_t rdata_aaaa;
1386 dns_rdata_txt_t rdata_txt;
1387 dns_rdata_txt_string_t rdatastr;
1388 dns_name_t *keyname = NULL;
1389 isc_mem_t *mctx;
1390 char keycbuf[DNS_NAME_FORMATSIZE];
1391 isc_buffer_t keybuf;
1392 unsigned int rcount;
1393
1394 REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1395 REQUIRE(ipkl != NULL);
1396 REQUIRE(DNS_RDATASET_VALID(value));
1397 REQUIRE(dns_rdataset_isassociated(value));
1398 REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
1399
1400 mctx = catz->catzs->mctx;
1401 memset(&rdata_a, 0, sizeof(rdata_a));
1402 memset(&rdata_aaaa, 0, sizeof(rdata_aaaa));
1403 memset(&rdata_txt, 0, sizeof(rdata_txt));
1404 isc_buffer_init(&keybuf, keycbuf, sizeof(keycbuf));
1405
1406 /*
1407 * We have three possibilities here:
1408 * - either empty name and IN A/IN AAAA record
1409 * - label and IN A/IN AAAA
1410 * - label and IN TXT - TSIG key name
1411 */
1412 if (name->labels > 0) {
1413 isc_sockaddr_t sockaddr;
1414 size_t i;
1415
1416 /*
1417 * We're pre-preparing the data once, we'll put it into
1418 * the right spot in the primaries array once we find it.
1419 */
1420 result = dns_rdataset_first(value);
1421 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1422 dns_rdata_init(&rdata);
1423 dns_rdataset_current(value, &rdata);
1424 switch (value->type) {
1425 case dns_rdatatype_a:
1426 result = dns_rdata_tostruct(&rdata, &rdata_a, NULL);
1427 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1428 isc_sockaddr_fromin(&sockaddr, &rdata_a.in_addr, 0);
1429 dns_rdata_freestruct(&rdata_a);
1430 break;
1431 case dns_rdatatype_aaaa:
1432 result = dns_rdata_tostruct(&rdata, &rdata_aaaa, NULL);
1433 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1434 isc_sockaddr_fromin6(&sockaddr, &rdata_aaaa.in6_addr,
1435 0);
1436 dns_rdata_freestruct(&rdata_aaaa);
1437 break;
1438 case dns_rdatatype_txt:
1439 result = dns_rdata_tostruct(&rdata, &rdata_txt, NULL);
1440 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1441
1442 result = dns_rdata_txt_first(&rdata_txt);
1443 if (result != ISC_R_SUCCESS) {
1444 dns_rdata_freestruct(&rdata_txt);
1445 return result;
1446 }
1447
1448 result = dns_rdata_txt_current(&rdata_txt, &rdatastr);
1449 if (result != ISC_R_SUCCESS) {
1450 dns_rdata_freestruct(&rdata_txt);
1451 return result;
1452 }
1453
1454 result = dns_rdata_txt_next(&rdata_txt);
1455 if (result != ISC_R_NOMORE) {
1456 dns_rdata_freestruct(&rdata_txt);
1457 return ISC_R_FAILURE;
1458 }
1459
1460 /* rdatastr.length < DNS_NAME_MAXTEXT */
1461 keyname = isc_mem_get(mctx, sizeof(*keyname));
1462 dns_name_init(keyname, 0);
1463 memmove(keycbuf, rdatastr.data, rdatastr.length);
1464 keycbuf[rdatastr.length] = 0;
1465 dns_rdata_freestruct(&rdata_txt);
1466 result = dns_name_fromstring(keyname, keycbuf,
1467 dns_rootname, 0, mctx);
1468 if (result != ISC_R_SUCCESS) {
1469 dns_name_free(keyname, mctx);
1470 isc_mem_put(mctx, keyname, sizeof(*keyname));
1471 return result;
1472 }
1473 break;
1474 default:
1475 return ISC_R_FAILURE;
1476 }
1477
1478 /*
1479 * We have to find the appropriate labeled record in
1480 * primaries if it exists. In the common case we'll
1481 * have no more than 3-4 records here, so no optimization.
1482 */
1483 for (i = 0; i < ipkl->count; i++) {
1484 if (ipkl->labels[i] != NULL &&
1485 !dns_name_compare(name, ipkl->labels[i]))
1486 {
1487 break;
1488 }
1489 }
1490
1491 if (i < ipkl->count) { /* we have this record already */
1492 if (value->type == dns_rdatatype_txt) {
1493 ipkl->keys[i] = keyname;
1494 } else { /* A/AAAA */
1495 memmove(&ipkl->addrs[i], &sockaddr,
1496 sizeof(sockaddr));
1497 }
1498 } else {
1499 result = dns_ipkeylist_resize(mctx, ipkl, i + 1);
1500 if (result != ISC_R_SUCCESS) {
1501 return result;
1502 }
1503
1504 ipkl->labels[i] = isc_mem_get(mctx,
1505 sizeof(*ipkl->labels[0]));
1506 dns_name_init(ipkl->labels[i], NULL);
1507 dns_name_dup(name, mctx, ipkl->labels[i]);
1508
1509 if (value->type == dns_rdatatype_txt) {
1510 ipkl->keys[i] = keyname;
1511 } else { /* A/AAAA */
1512 memmove(&ipkl->addrs[i], &sockaddr,
1513 sizeof(sockaddr));
1514 }
1515 ipkl->count++;
1516 }
1517 return ISC_R_SUCCESS;
1518 }
1519 /* else - 'simple' case - without labels */
1520
1521 if (value->type != dns_rdatatype_a && value->type != dns_rdatatype_aaaa)
1522 {
1523 return ISC_R_FAILURE;
1524 }
1525
1526 rcount = dns_rdataset_count(value) + ipkl->count;
1527
1528 result = dns_ipkeylist_resize(mctx, ipkl, rcount);
1529 if (result != ISC_R_SUCCESS) {
1530 return result;
1531 }
1532
1533 for (result = dns_rdataset_first(value); result == ISC_R_SUCCESS;
1534 result = dns_rdataset_next(value))
1535 {
1536 dns_rdata_init(&rdata);
1537 dns_rdataset_current(value, &rdata);
1538 /*
1539 * port 0 == take the default
1540 */
1541 if (value->type == dns_rdatatype_a) {
1542 result = dns_rdata_tostruct(&rdata, &rdata_a, NULL);
1543 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1544 isc_sockaddr_fromin(&ipkl->addrs[ipkl->count],
1545 &rdata_a.in_addr, 0);
1546 dns_rdata_freestruct(&rdata_a);
1547 } else {
1548 result = dns_rdata_tostruct(&rdata, &rdata_aaaa, NULL);
1549 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1550 isc_sockaddr_fromin6(&ipkl->addrs[ipkl->count],
1551 &rdata_aaaa.in6_addr, 0);
1552 dns_rdata_freestruct(&rdata_aaaa);
1553 }
1554 ipkl->keys[ipkl->count] = NULL;
1555 ipkl->labels[ipkl->count] = NULL;
1556 ipkl->count++;
1557 }
1558 return ISC_R_SUCCESS;
1559 }
1560
1561 static isc_result_t
1562 catz_process_apl(dns_catz_zone_t *catz, isc_buffer_t **aclbp,
1563 dns_rdataset_t *value) {
1564 isc_result_t result = ISC_R_SUCCESS;
1565 dns_rdata_t rdata;
1566 dns_rdata_in_apl_t rdata_apl;
1567 dns_rdata_apl_ent_t apl_ent;
1568 isc_netaddr_t addr;
1569 isc_buffer_t *aclb = NULL;
1570 unsigned char buf[256]; /* larger than INET6_ADDRSTRLEN */
1571
1572 REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1573 REQUIRE(aclbp != NULL);
1574 REQUIRE(*aclbp == NULL);
1575 REQUIRE(DNS_RDATASET_VALID(value));
1576 REQUIRE(dns_rdataset_isassociated(value));
1577
1578 if (value->type != dns_rdatatype_apl) {
1579 return ISC_R_FAILURE;
1580 }
1581
1582 if (dns_rdataset_count(value) > 1) {
1583 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
1584 DNS_LOGMODULE_MASTER, ISC_LOG_WARNING,
1585 "catz: more than one APL entry for member zone, "
1586 "result is undefined");
1587 }
1588 result = dns_rdataset_first(value);
1589 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1590 dns_rdata_init(&rdata);
1591 dns_rdataset_current(value, &rdata);
1592 result = dns_rdata_tostruct(&rdata, &rdata_apl, catz->catzs->mctx);
1593 if (result != ISC_R_SUCCESS) {
1594 return result;
1595 }
1596 isc_buffer_allocate(catz->catzs->mctx, &aclb, 16);
1597 for (result = dns_rdata_apl_first(&rdata_apl); result == ISC_R_SUCCESS;
1598 result = dns_rdata_apl_next(&rdata_apl))
1599 {
1600 result = dns_rdata_apl_current(&rdata_apl, &apl_ent);
1601 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1602 memset(buf, 0, sizeof(buf));
1603 if (apl_ent.data != NULL && apl_ent.length > 0) {
1604 memmove(buf, apl_ent.data, apl_ent.length);
1605 }
1606 if (apl_ent.family == 1) {
1607 isc_netaddr_fromin(&addr, (struct in_addr *)buf);
1608 } else if (apl_ent.family == 2) {
1609 isc_netaddr_fromin6(&addr, (struct in6_addr *)buf);
1610 } else {
1611 continue; /* xxxwpk log it or simply ignore? */
1612 }
1613 if (apl_ent.negative) {
1614 isc_buffer_putuint8(aclb, '!');
1615 }
1616 isc_buffer_reserve(aclb, INET6_ADDRSTRLEN);
1617 result = isc_netaddr_totext(&addr, aclb);
1618 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1619 if ((apl_ent.family == 1 && apl_ent.prefix < 32) ||
1620 (apl_ent.family == 2 && apl_ent.prefix < 128))
1621 {
1622 isc_buffer_putuint8(aclb, '/');
1623 isc_buffer_printf(aclb, "%" PRId8, apl_ent.prefix);
1624 }
1625 isc_buffer_putstr(aclb, "; ");
1626 }
1627 if (result == ISC_R_NOMORE) {
1628 result = ISC_R_SUCCESS;
1629 } else {
1630 goto cleanup;
1631 }
1632 *aclbp = aclb;
1633 aclb = NULL;
1634 cleanup:
1635 if (aclb != NULL) {
1636 isc_buffer_free(&aclb);
1637 }
1638 dns_rdata_freestruct(&rdata_apl);
1639 return result;
1640 }
1641
1642 static isc_result_t
1643 catz_process_zones_suboption(dns_catz_zone_t *catz, dns_rdataset_t *value,
1644 dns_label_t *mhash, dns_name_t *name) {
1645 isc_result_t result;
1646 dns_catz_entry_t *entry = NULL;
1647 dns_label_t option;
1648 dns_name_t prefix;
1649 catz_opt_t opt;
1650 unsigned int suffix_labels = 1;
1651
1652 REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1653 REQUIRE(mhash != NULL);
1654 REQUIRE(DNS_RDATASET_VALID(value));
1655 REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
1656
1657 if (name->labels < 1) {
1658 return ISC_R_FAILURE;
1659 }
1660 dns_name_getlabel(name, name->labels - 1, &option);
1661 opt = catz_get_option(&option);
1662
1663 /*
1664 * The custom properties in version 2 schema must be placed under the
1665 * "ext" label.
1666 */
1667 if (catz->version >= 2 && opt >= CATZ_OPT_CUSTOM_START) {
1668 if (opt != CATZ_OPT_EXT || name->labels < 2) {
1669 return ISC_R_FAILURE;
1670 }
1671 suffix_labels++;
1672 dns_name_getlabel(name, name->labels - 2, &option);
1673 opt = catz_get_option(&option);
1674 }
1675
1676 /*
1677 * We're adding this entry now, in case the option is invalid we'll get
1678 * rid of it in verification phase.
1679 */
1680 result = isc_ht_find(catz->entries, mhash->base, mhash->length,
1681 (void **)&entry);
1682 if (result != ISC_R_SUCCESS) {
1683 entry = dns_catz_entry_new(catz->catzs->mctx, NULL);
1684 result = isc_ht_add(catz->entries, mhash->base, mhash->length,
1685 entry);
1686 }
1687 INSIST(result == ISC_R_SUCCESS);
1688
1689 dns_name_init(&prefix, NULL);
1690 dns_name_split(name, suffix_labels, &prefix, NULL);
1691 switch (opt) {
1692 case CATZ_OPT_COO:
1693 return catz_process_coo(catz, mhash, value);
1694 case CATZ_OPT_PRIMARIES:
1695 return catz_process_primaries(catz, &entry->opts.masters, value,
1696 &prefix);
1697 case CATZ_OPT_ALLOW_QUERY:
1698 if (prefix.labels != 0) {
1699 return ISC_R_FAILURE;
1700 }
1701 return catz_process_apl(catz, &entry->opts.allow_query, value);
1702 case CATZ_OPT_ALLOW_TRANSFER:
1703 if (prefix.labels != 0) {
1704 return ISC_R_FAILURE;
1705 }
1706 return catz_process_apl(catz, &entry->opts.allow_transfer,
1707 value);
1708 default:
1709 return ISC_R_FAILURE;
1710 }
1711
1712 return ISC_R_FAILURE;
1713 }
1714
1715 static void
1716 catz_entry_add_or_mod(dns_catz_zone_t *catz, isc_ht_t *ht, unsigned char *key,
1717 size_t keysize, dns_catz_entry_t *nentry,
1718 dns_catz_entry_t *oentry, const char *msg,
1719 const char *zname, const char *czname) {
1720 isc_result_t result = isc_ht_add(ht, key, (uint32_t)keysize, nentry);
1721
1722 if (result != ISC_R_SUCCESS) {
1723 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
1724 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
1725 "catz: error %s zone '%s' from catalog '%s' - %s",
1726 msg, zname, czname, isc_result_totext(result));
1727 }
1728 if (oentry != NULL) {
1729 dns_catz_entry_detach(catz, &oentry);
1730 result = isc_ht_delete(catz->entries, key, (uint32_t)keysize);
1731 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1732 }
1733 }
1734
1735 static isc_result_t
1736 catz_process_value(dns_catz_zone_t *catz, dns_name_t *name,
1737 dns_rdataset_t *rdataset) {
1738 dns_label_t option;
1739 dns_name_t prefix;
1740 catz_opt_t opt;
1741 unsigned int suffix_labels = 1;
1742
1743 REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1744 REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
1745 REQUIRE(DNS_RDATASET_VALID(rdataset));
1746
1747 if (name->labels < 1) {
1748 return ISC_R_FAILURE;
1749 }
1750 dns_name_getlabel(name, name->labels - 1, &option);
1751 opt = catz_get_option(&option);
1752
1753 /*
1754 * The custom properties in version 2 schema must be placed under the
1755 * "ext" label.
1756 */
1757 if (catz->version >= 2 && opt >= CATZ_OPT_CUSTOM_START) {
1758 if (opt != CATZ_OPT_EXT || name->labels < 2) {
1759 return ISC_R_FAILURE;
1760 }
1761 suffix_labels++;
1762 dns_name_getlabel(name, name->labels - 2, &option);
1763 opt = catz_get_option(&option);
1764 }
1765
1766 dns_name_init(&prefix, NULL);
1767 dns_name_split(name, suffix_labels, &prefix, NULL);
1768
1769 switch (opt) {
1770 case CATZ_OPT_ZONES:
1771 return catz_process_zones(catz, rdataset, &prefix);
1772 case CATZ_OPT_PRIMARIES:
1773 return catz_process_primaries(catz, &catz->zoneoptions.masters,
1774 rdataset, &prefix);
1775 case CATZ_OPT_ALLOW_QUERY:
1776 if (prefix.labels != 0) {
1777 return ISC_R_FAILURE;
1778 }
1779 return catz_process_apl(catz, &catz->zoneoptions.allow_query,
1780 rdataset);
1781 case CATZ_OPT_ALLOW_TRANSFER:
1782 if (prefix.labels != 0) {
1783 return ISC_R_FAILURE;
1784 }
1785 return catz_process_apl(catz, &catz->zoneoptions.allow_transfer,
1786 rdataset);
1787 case CATZ_OPT_VERSION:
1788 if (prefix.labels != 0) {
1789 return ISC_R_FAILURE;
1790 }
1791 return catz_process_version(catz, rdataset);
1792 default:
1793 return ISC_R_FAILURE;
1794 }
1795 }
1796
1797 /*%<
1798 * Process a single rdataset from a catalog zone 'catz' update, src_name is the
1799 * record name.
1800 *
1801 * Requires:
1802 * \li 'catz' is a valid dns_catz_zone_t.
1803 * \li 'src_name' is a valid dns_name_t.
1804 * \li 'rdataset' is valid rdataset.
1805 */
1806 static isc_result_t
1807 dns__catz_update_process(dns_catz_zone_t *catz, const dns_name_t *src_name,
1808 dns_rdataset_t *rdataset) {
1809 isc_result_t result;
1810 int order;
1811 unsigned int nlabels;
1812 dns_namereln_t nrres;
1813 dns_rdata_t rdata = DNS_RDATA_INIT;
1814 dns_rdata_soa_t soa;
1815 dns_name_t prefix;
1816
1817 REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1818 REQUIRE(ISC_MAGIC_VALID(src_name, DNS_NAME_MAGIC));
1819
1820 if (rdataset->rdclass != dns_rdataclass_in) {
1821 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
1822 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
1823 "catz: RR found which has a non-IN class");
1824 catz->broken = true;
1825 return ISC_R_FAILURE;
1826 }
1827
1828 nrres = dns_name_fullcompare(src_name, &catz->name, &order, &nlabels);
1829 if (nrres == dns_namereln_equal) {
1830 if (rdataset->type == dns_rdatatype_soa) {
1831 result = dns_rdataset_first(rdataset);
1832 if (result != ISC_R_SUCCESS) {
1833 return result;
1834 }
1835
1836 dns_rdataset_current(rdataset, &rdata);
1837 result = dns_rdata_tostruct(&rdata, &soa, NULL);
1838 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1839
1840 /*
1841 * xxxwpk TODO do we want to save something from SOA?
1842 */
1843 dns_rdata_freestruct(&soa);
1844 return result;
1845 } else if (rdataset->type == dns_rdatatype_ns) {
1846 return ISC_R_SUCCESS;
1847 } else {
1848 return ISC_R_UNEXPECTED;
1849 }
1850 } else if (nrres != dns_namereln_subdomain) {
1851 return ISC_R_UNEXPECTED;
1852 }
1853
1854 dns_name_init(&prefix, NULL);
1855 dns_name_split(src_name, catz->name.labels, &prefix, NULL);
1856 result = catz_process_value(catz, &prefix, rdataset);
1857
1858 return result;
1859 }
1860
1861 static isc_result_t
1862 digest2hex(unsigned char *digest, unsigned int digestlen, char *hash,
1863 size_t hashlen) {
1864 unsigned int i;
1865 for (i = 0; i < digestlen; i++) {
1866 size_t left = hashlen - i * 2;
1867 int ret = snprintf(hash + i * 2, left, "%02x", digest[i]);
1868 if (ret < 0 || (size_t)ret >= left) {
1869 return ISC_R_NOSPACE;
1870 }
1871 }
1872 return ISC_R_SUCCESS;
1873 }
1874
1875 isc_result_t
1876 dns_catz_generate_masterfilename(dns_catz_zone_t *catz, dns_catz_entry_t *entry,
1877 isc_buffer_t **buffer) {
1878 isc_buffer_t *tbuf = NULL;
1879 isc_region_t r;
1880 isc_result_t result;
1881 size_t rlen;
1882 bool special = false;
1883
1884 REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1885 REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
1886 REQUIRE(buffer != NULL && *buffer != NULL);
1887
1888 isc_buffer_allocate(catz->catzs->mctx, &tbuf,
1889 strlen(catz->catzs->view->name) +
1890 2 * DNS_NAME_FORMATSIZE + 2);
1891
1892 isc_buffer_putstr(tbuf, catz->catzs->view->name);
1893 isc_buffer_putstr(tbuf, "_");
1894 result = dns_name_totext(&catz->name, DNS_NAME_OMITFINALDOT, tbuf);
1895 if (result != ISC_R_SUCCESS) {
1896 goto cleanup;
1897 }
1898
1899 isc_buffer_putstr(tbuf, "_");
1900 result = dns_name_totext(&entry->name, DNS_NAME_OMITFINALDOT, tbuf);
1901 if (result != ISC_R_SUCCESS) {
1902 goto cleanup;
1903 }
1904
1905 /*
1906 * Search for slash and other special characters in the view and
1907 * zone names. Add a null terminator so we can use strpbrk(), then
1908 * remove it.
1909 */
1910 isc_buffer_putuint8(tbuf, 0);
1911 if (strpbrk(isc_buffer_base(tbuf), "\\/:") != NULL) {
1912 special = true;
1913 }
1914 isc_buffer_subtract(tbuf, 1);
1915
1916 /* __catz__<digest>.db */
1917 rlen = (isc_md_type_get_size(ISC_MD_SHA256) * 2 + 1) + 12;
1918
1919 /* optionally prepend with <zonedir>/ */
1920 if (entry->opts.zonedir != NULL) {
1921 rlen += strlen(entry->opts.zonedir) + 1;
1922 }
1923
1924 result = isc_buffer_reserve(*buffer, (unsigned int)rlen);
1925 if (result != ISC_R_SUCCESS) {
1926 goto cleanup;
1927 }
1928
1929 if (entry->opts.zonedir != NULL) {
1930 isc_buffer_putstr(*buffer, entry->opts.zonedir);
1931 isc_buffer_putstr(*buffer, "/");
1932 }
1933
1934 isc_buffer_usedregion(tbuf, &r);
1935 isc_buffer_putstr(*buffer, "__catz__");
1936 if (special || tbuf->used > ISC_SHA256_DIGESTLENGTH * 2 + 1) {
1937 unsigned char digest[ISC_MAX_MD_SIZE];
1938 unsigned int digestlen;
1939
1940 /* we can do that because digest string < 2 * DNS_NAME */
1941 result = isc_md(ISC_MD_SHA256, r.base, r.length, digest,
1942 &digestlen);
1943 if (result != ISC_R_SUCCESS) {
1944 goto cleanup;
1945 }
1946 result = digest2hex(digest, digestlen, (char *)r.base,
1947 ISC_SHA256_DIGESTLENGTH * 2 + 1);
1948 if (result != ISC_R_SUCCESS) {
1949 goto cleanup;
1950 }
1951 isc_buffer_putstr(*buffer, (char *)r.base);
1952 } else {
1953 isc_buffer_copyregion(*buffer, &r);
1954 }
1955
1956 isc_buffer_putstr(*buffer, ".db");
1957 result = ISC_R_SUCCESS;
1958
1959 cleanup:
1960 isc_buffer_free(&tbuf);
1961 return result;
1962 }
1963
1964 /*
1965 * We have to generate a text buffer with regular zone config:
1966 * zone "foo.bar" {
1967 * type secondary;
1968 * primaries { ip1 port port1; ip2 port port2; };
1969 * }
1970 */
1971 isc_result_t
1972 dns_catz_generate_zonecfg(dns_catz_zone_t *catz, dns_catz_entry_t *entry,
1973 isc_buffer_t **buf) {
1974 isc_buffer_t *buffer = NULL;
1975 isc_region_t region;
1976 isc_result_t result;
1977 uint32_t i;
1978 isc_netaddr_t netaddr;
1979 char pbuf[sizeof("65535")]; /* used for port number */
1980 char namebuf[DNS_NAME_FORMATSIZE];
1981
1982 REQUIRE(DNS_CATZ_ZONE_VALID(catz));
1983 REQUIRE(DNS_CATZ_ENTRY_VALID(entry));
1984 REQUIRE(buf != NULL && *buf == NULL);
1985
1986 /*
1987 * The buffer will be reallocated if something won't fit,
1988 * ISC_BUFFER_INCR seems like a good start.
1989 */
1990 isc_buffer_allocate(catz->catzs->mctx, &buffer, ISC_BUFFER_INCR);
1991
1992 isc_buffer_putstr(buffer, "zone \"");
1993 dns_name_format(&entry->name, namebuf, sizeof(namebuf));
1994 isc_buffer_putstr(buffer, namebuf);
1995 isc_buffer_putstr(buffer, "\" { type secondary; primaries");
1996
1997 isc_buffer_putstr(buffer, " { ");
1998 for (i = 0; i < entry->opts.masters.count; i++) {
1999 /*
2000 * Every primary must have an IP address assigned.
2001 */
2002 switch (entry->opts.masters.addrs[i].type.sa.sa_family) {
2003 case AF_INET:
2004 case AF_INET6:
2005 break;
2006 default:
2007 dns_name_format(&entry->name, namebuf, sizeof(namebuf));
2008 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
2009 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
2010 "catz: zone '%s' uses an invalid primary "
2011 "(no IP address assigned)",
2012 namebuf);
2013 result = ISC_R_FAILURE;
2014 goto cleanup;
2015 }
2016 isc_netaddr_fromsockaddr(&netaddr,
2017 &entry->opts.masters.addrs[i]);
2018 isc_buffer_reserve(buffer, INET6_ADDRSTRLEN);
2019 result = isc_netaddr_totext(&netaddr, buffer);
2020 RUNTIME_CHECK(result == ISC_R_SUCCESS);
2021
2022 isc_buffer_putstr(buffer, " port ");
2023 snprintf(pbuf, sizeof(pbuf), "%u",
2024 isc_sockaddr_getport(&entry->opts.masters.addrs[i]));
2025 isc_buffer_putstr(buffer, pbuf);
2026
2027 if (entry->opts.masters.keys[i] != NULL) {
2028 isc_buffer_putstr(buffer, " key ");
2029 dns_name_format(entry->opts.masters.keys[i], namebuf,
2030 sizeof(namebuf));
2031 isc_buffer_putstr(buffer, namebuf);
2032 }
2033
2034 if (entry->opts.masters.tlss[i] != NULL) {
2035 isc_buffer_putstr(buffer, " tls ");
2036 dns_name_format(entry->opts.masters.tlss[i], namebuf,
2037 sizeof(namebuf));
2038 isc_buffer_putstr(buffer, namebuf);
2039 }
2040 isc_buffer_putstr(buffer, "; ");
2041 }
2042 isc_buffer_putstr(buffer, "}; ");
2043 if (!entry->opts.in_memory) {
2044 isc_buffer_putstr(buffer, "file \"");
2045 result = dns_catz_generate_masterfilename(catz, entry, &buffer);
2046 if (result != ISC_R_SUCCESS) {
2047 goto cleanup;
2048 }
2049 isc_buffer_putstr(buffer, "\"; ");
2050 }
2051 if (entry->opts.allow_query != NULL) {
2052 isc_buffer_putstr(buffer, "allow-query { ");
2053 isc_buffer_usedregion(entry->opts.allow_query, ®ion);
2054 isc_buffer_copyregion(buffer, ®ion);
2055 isc_buffer_putstr(buffer, "}; ");
2056 }
2057 if (entry->opts.allow_transfer != NULL) {
2058 isc_buffer_putstr(buffer, "allow-transfer { ");
2059 isc_buffer_usedregion(entry->opts.allow_transfer, ®ion);
2060 isc_buffer_copyregion(buffer, ®ion);
2061 isc_buffer_putstr(buffer, "}; ");
2062 }
2063
2064 isc_buffer_putstr(buffer, "};");
2065 *buf = buffer;
2066
2067 return ISC_R_SUCCESS;
2068
2069 cleanup:
2070 isc_buffer_free(&buffer);
2071 return result;
2072 }
2073
2074 static void
2075 dns__catz_timer_cb(void *arg) {
2076 char domain[DNS_NAME_FORMATSIZE];
2077 dns_catz_zone_t *catz = (dns_catz_zone_t *)arg;
2078
2079 REQUIRE(DNS_CATZ_ZONE_VALID(catz));
2080
2081 if (atomic_load(&catz->catzs->shuttingdown)) {
2082 return;
2083 }
2084
2085 LOCK(&catz->catzs->lock);
2086
2087 INSIST(DNS_DB_VALID(catz->db));
2088 INSIST(catz->dbversion != NULL);
2089 INSIST(catz->updb == NULL);
2090 INSIST(catz->updbversion == NULL);
2091
2092 catz->updatepending = false;
2093 catz->updaterunning = true;
2094 catz->updateresult = ISC_R_UNSET;
2095
2096 dns_name_format(&catz->name, domain, DNS_NAME_FORMATSIZE);
2097
2098 if (!catz->active) {
2099 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
2100 DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
2101 "catz: %s: no longer active, reload is canceled",
2102 domain);
2103 catz->updaterunning = false;
2104 catz->updateresult = ISC_R_CANCELED;
2105 goto exit;
2106 }
2107
2108 dns_db_attach(catz->db, &catz->updb);
2109 catz->updbversion = catz->dbversion;
2110 catz->dbversion = NULL;
2111
2112 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER,
2113 ISC_LOG_INFO, "catz: %s: reload start", domain);
2114
2115 dns_catz_zone_ref(catz);
2116 isc_work_enqueue(catz->loop, dns__catz_update_cb, dns__catz_done_cb,
2117 catz);
2118
2119 exit:
2120 isc_timer_destroy(&catz->updatetimer);
2121 catz->loop = NULL;
2122
2123 catz->lastupdated = isc_time_now();
2124
2125 UNLOCK(&catz->catzs->lock);
2126 }
2127
2128 isc_result_t
2129 dns_catz_dbupdate_callback(dns_db_t *db, void *fn_arg) {
2130 dns_catz_zones_t *catzs = NULL;
2131 dns_catz_zone_t *catz = NULL;
2132 isc_result_t result = ISC_R_SUCCESS;
2133 isc_region_t r;
2134
2135 REQUIRE(DNS_DB_VALID(db));
2136 REQUIRE(DNS_CATZ_ZONES_VALID(fn_arg));
2137 catzs = (dns_catz_zones_t *)fn_arg;
2138
2139 if (atomic_load(&catzs->shuttingdown)) {
2140 return ISC_R_SHUTTINGDOWN;
2141 }
2142
2143 dns_name_toregion(&db->origin, &r);
2144
2145 LOCK(&catzs->lock);
2146 if (catzs->zones == NULL) {
2147 result = ISC_R_SHUTTINGDOWN;
2148 goto cleanup;
2149 }
2150 result = isc_ht_find(catzs->zones, r.base, r.length, (void **)&catz);
2151 if (result != ISC_R_SUCCESS) {
2152 goto cleanup;
2153 }
2154
2155 /* New zone came as AXFR */
2156 if (catz->db != NULL && catz->db != db) {
2157 /* Old db cleanup. */
2158 if (catz->dbversion != NULL) {
2159 dns_db_closeversion(catz->db, &catz->dbversion, false);
2160 }
2161 dns_db_updatenotify_unregister(
2162 catz->db, dns_catz_dbupdate_callback, catz->catzs);
2163 dns_db_detach(&catz->db);
2164 }
2165 if (catz->db == NULL) {
2166 /* New db registration. */
2167 dns_db_attach(db, &catz->db);
2168 dns_db_updatenotify_register(db, dns_catz_dbupdate_callback,
2169 catz->catzs);
2170 }
2171
2172 if (!catz->updatepending && !catz->updaterunning) {
2173 catz->updatepending = true;
2174 dns_db_currentversion(db, &catz->dbversion);
2175 dns__catz_timer_start(catz);
2176 } else {
2177 char dname[DNS_NAME_FORMATSIZE];
2178
2179 catz->updatepending = true;
2180 dns_name_format(&catz->name, dname, DNS_NAME_FORMATSIZE);
2181 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
2182 DNS_LOGMODULE_MASTER, ISC_LOG_DEBUG(3),
2183 "catz: %s: update already queued or running",
2184 dname);
2185 if (catz->dbversion != NULL) {
2186 dns_db_closeversion(catz->db, &catz->dbversion, false);
2187 }
2188 dns_db_currentversion(catz->db, &catz->dbversion);
2189 }
2190
2191 cleanup:
2192 UNLOCK(&catzs->lock);
2193
2194 return result;
2195 }
2196
2197 void
2198 dns_catz_dbupdate_unregister(dns_db_t *db, dns_catz_zones_t *catzs) {
2199 REQUIRE(DNS_DB_VALID(db));
2200 REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
2201
2202 dns_db_updatenotify_unregister(db, dns_catz_dbupdate_callback, catzs);
2203 }
2204
2205 void
2206 dns_catz_dbupdate_register(dns_db_t *db, dns_catz_zones_t *catzs) {
2207 REQUIRE(DNS_DB_VALID(db));
2208 REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
2209
2210 dns_db_updatenotify_register(db, dns_catz_dbupdate_callback, catzs);
2211 }
2212
2213 static bool
2214 catz_rdatatype_is_processable(const dns_rdatatype_t type) {
2215 return !dns_rdatatype_isdnssec(type) && type != dns_rdatatype_cds &&
2216 type != dns_rdatatype_cdnskey && type != dns_rdatatype_zonemd;
2217 }
2218
2219 /*
2220 * Process an updated database for a catalog zone.
2221 * It creates a new catz, iterates over database to fill it with content, and
2222 * then merges new catz into old catz.
2223 */
2224 static void
2225 dns__catz_update_cb(void *data) {
2226 dns_catz_zone_t *catz = (dns_catz_zone_t *)data;
2227 dns_db_t *updb = NULL;
2228 dns_catz_zones_t *catzs = NULL;
2229 dns_catz_zone_t *oldcatz = NULL, *newcatz = NULL;
2230 isc_result_t result;
2231 isc_region_t r;
2232 dns_dbnode_t *node = NULL;
2233 const dns_dbnode_t *vers_node = NULL;
2234 dns_dbiterator_t *updbit = NULL;
2235 dns_fixedname_t fixname;
2236 dns_name_t *name = NULL;
2237 dns_rdatasetiter_t *rdsiter = NULL;
2238 dns_rdataset_t rdataset;
2239 char bname[DNS_NAME_FORMATSIZE];
2240 char cname[DNS_NAME_FORMATSIZE];
2241 bool is_vers_processed = false;
2242 bool is_active;
2243 uint32_t vers;
2244 uint32_t catz_vers;
2245
2246 REQUIRE(DNS_CATZ_ZONE_VALID(catz));
2247 REQUIRE(DNS_DB_VALID(catz->updb));
2248 REQUIRE(DNS_CATZ_ZONES_VALID(catz->catzs));
2249
2250 updb = catz->updb;
2251 catzs = catz->catzs;
2252
2253 if (atomic_load(&catzs->shuttingdown)) {
2254 result = ISC_R_SHUTTINGDOWN;
2255 goto exit;
2256 }
2257
2258 dns_name_format(&updb->origin, bname, DNS_NAME_FORMATSIZE);
2259
2260 /*
2261 * Create a new catz in the same context as current catz.
2262 */
2263 dns_name_toregion(&updb->origin, &r);
2264 LOCK(&catzs->lock);
2265 if (catzs->zones == NULL) {
2266 UNLOCK(&catzs->lock);
2267 result = ISC_R_SHUTTINGDOWN;
2268 goto exit;
2269 }
2270 result = isc_ht_find(catzs->zones, r.base, r.length, (void **)&oldcatz);
2271 is_active = (result == ISC_R_SUCCESS && oldcatz->active);
2272 UNLOCK(&catzs->lock);
2273 if (result != ISC_R_SUCCESS) {
2274 /* This can happen if we remove the zone in the meantime. */
2275 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
2276 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
2277 "catz: zone '%s' not in config", bname);
2278 goto exit;
2279 }
2280
2281 if (!is_active) {
2282 /* This can happen during a reconfiguration. */
2283 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
2284 DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
2285 "catz: zone '%s' is no longer active", bname);
2286 result = ISC_R_CANCELED;
2287 goto exit;
2288 }
2289
2290 result = dns_db_getsoaserial(updb, oldcatz->updbversion, &vers);
2291 if (result != ISC_R_SUCCESS) {
2292 /* A zone without SOA record?!? */
2293 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
2294 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
2295 "catz: zone '%s' has no SOA record (%s)", bname,
2296 isc_result_totext(result));
2297 goto exit;
2298 }
2299
2300 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER,
2301 ISC_LOG_INFO,
2302 "catz: updating catalog zone '%s' with serial %" PRIu32,
2303 bname, vers);
2304
2305 result = dns_db_createiterator(updb, DNS_DB_NONSEC3, &updbit);
2306 if (result != ISC_R_SUCCESS) {
2307 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
2308 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
2309 "catz: failed to create DB iterator - %s",
2310 isc_result_totext(result));
2311 goto exit;
2312 }
2313
2314 name = dns_fixedname_initname(&fixname);
2315
2316 /*
2317 * Take the version record to process first, because the other
2318 * records might be processed differently depending on the version of
2319 * the catalog zone's schema.
2320 */
2321 result = dns_name_fromstring(name, "version", &updb->origin, 0, NULL);
2322 if (result != ISC_R_SUCCESS) {
2323 dns_dbiterator_destroy(&updbit);
2324 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
2325 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
2326 "catz: failed to create name from string - %s",
2327 isc_result_totext(result));
2328 goto exit;
2329 }
2330
2331 result = dns_dbiterator_seek(updbit, name);
2332 if (result != ISC_R_SUCCESS) {
2333 dns_dbiterator_destroy(&updbit);
2334 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
2335 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
2336 "catz: zone '%s' has no 'version' record (%s) "
2337 "and will not be processed",
2338 bname, isc_result_totext(result));
2339 goto exit;
2340 }
2341
2342 newcatz = dns_catz_zone_new(catzs, &updb->origin);
2343 name = dns_fixedname_initname(&fixname);
2344
2345 /*
2346 * Iterate over database to fill the new zone.
2347 */
2348 while (result == ISC_R_SUCCESS) {
2349 if (atomic_load(&catzs->shuttingdown)) {
2350 result = ISC_R_SHUTTINGDOWN;
2351 break;
2352 }
2353
2354 result = dns_dbiterator_current(updbit, &node, name);
2355 if (result != ISC_R_SUCCESS) {
2356 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
2357 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
2358 "catz: failed to get db iterator - %s",
2359 isc_result_totext(result));
2360 break;
2361 }
2362
2363 result = dns_dbiterator_pause(updbit);
2364 RUNTIME_CHECK(result == ISC_R_SUCCESS);
2365
2366 if (!is_vers_processed) {
2367 /* Keep the version node to skip it later in the loop */
2368 vers_node = node;
2369 } else if (node == vers_node) {
2370 /* Skip the already processed version node */
2371 dns_db_detachnode(updb, &node);
2372 result = dns_dbiterator_next(updbit);
2373 continue;
2374 }
2375
2376 result = dns_db_allrdatasets(updb, node, oldcatz->updbversion,
2377 0, 0, &rdsiter);
2378 if (result != ISC_R_SUCCESS) {
2379 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
2380 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
2381 "catz: failed to fetch rrdatasets - %s",
2382 isc_result_totext(result));
2383 dns_db_detachnode(updb, &node);
2384 break;
2385 }
2386
2387 dns_rdataset_init(&rdataset);
2388 result = dns_rdatasetiter_first(rdsiter);
2389 while (result == ISC_R_SUCCESS) {
2390 dns_rdatasetiter_current(rdsiter, &rdataset);
2391
2392 /*
2393 * Skip processing DNSSEC-related and ZONEMD types,
2394 * because we are not interested in them in the context
2395 * of a catalog zone, and processing them will fail
2396 * and produce an unnecessary warning message.
2397 */
2398 if (!catz_rdatatype_is_processable(rdataset.type)) {
2399 goto next;
2400 }
2401
2402 /*
2403 * Although newcatz->coos is accessed in
2404 * catz_process_coo() in the call-chain below, we don't
2405 * need to hold the newcatz->lock, because the newcatz
2406 * is still local to this thread and function and
2407 * newcatz->coos can't be accessed from the outside
2408 * until dns__catz_zones_merge() has been called.
2409 */
2410 result = dns__catz_update_process(newcatz, name,
2411 &rdataset);
2412 if (result != ISC_R_SUCCESS) {
2413 char typebuf[DNS_RDATATYPE_FORMATSIZE];
2414 char classbuf[DNS_RDATACLASS_FORMATSIZE];
2415
2416 dns_name_format(name, cname,
2417 DNS_NAME_FORMATSIZE);
2418 dns_rdataclass_format(rdataset.rdclass,
2419 classbuf,
2420 sizeof(classbuf));
2421 dns_rdatatype_format(rdataset.type, typebuf,
2422 sizeof(typebuf));
2423 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
2424 DNS_LOGMODULE_MASTER,
2425 ISC_LOG_WARNING,
2426 "catz: invalid record in catalog "
2427 "zone - %s %s %s (%s) - ignoring",
2428 cname, classbuf, typebuf,
2429 isc_result_totext(result));
2430 }
2431 next:
2432 dns_rdataset_disassociate(&rdataset);
2433 result = dns_rdatasetiter_next(rdsiter);
2434 }
2435
2436 dns_rdatasetiter_destroy(&rdsiter);
2437
2438 dns_db_detachnode(updb, &node);
2439
2440 if (!is_vers_processed) {
2441 is_vers_processed = true;
2442 result = dns_dbiterator_first(updbit);
2443 } else {
2444 result = dns_dbiterator_next(updbit);
2445 }
2446 }
2447
2448 dns_dbiterator_destroy(&updbit);
2449 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER,
2450 ISC_LOG_DEBUG(3),
2451 "catz: update_from_db: iteration finished: %s",
2452 isc_result_totext(result));
2453
2454 /*
2455 * Check catalog zone version compatibilites.
2456 */
2457 catz_vers = (newcatz->version == DNS_CATZ_VERSION_UNDEFINED)
2458 ? oldcatz->version
2459 : newcatz->version;
2460 if (catz_vers == DNS_CATZ_VERSION_UNDEFINED) {
2461 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
2462 DNS_LOGMODULE_MASTER, ISC_LOG_WARNING,
2463 "catz: zone '%s' version is not set", bname);
2464 newcatz->broken = true;
2465 } else if (catz_vers != 1 && catz_vers != 2) {
2466 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
2467 DNS_LOGMODULE_MASTER, ISC_LOG_WARNING,
2468 "catz: zone '%s' unsupported version "
2469 "'%" PRIu32 "'",
2470 bname, catz_vers);
2471 newcatz->broken = true;
2472 } else {
2473 oldcatz->version = catz_vers;
2474 }
2475
2476 if (newcatz->broken) {
2477 dns_name_format(name, cname, DNS_NAME_FORMATSIZE);
2478 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
2479 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
2480 "catz: new catalog zone '%s' is broken and "
2481 "will not be processed",
2482 bname);
2483 dns_catz_zone_detach(&newcatz);
2484 result = ISC_R_FAILURE;
2485 goto exit;
2486 }
2487
2488 /*
2489 * Finally merge new zone into old zone.
2490 */
2491 result = dns__catz_zones_merge(oldcatz, newcatz);
2492 dns_catz_zone_detach(&newcatz);
2493 if (result != ISC_R_SUCCESS) {
2494 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
2495 DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
2496 "catz: failed merging zones: %s",
2497 isc_result_totext(result));
2498
2499 goto exit;
2500 }
2501
2502 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER,
2503 ISC_LOG_DEBUG(3),
2504 "catz: update_from_db: new zone merged");
2505
2506 exit:
2507 catz->updateresult = result;
2508 }
2509
2510 static void
2511 dns__catz_done_cb(void *data) {
2512 dns_catz_zone_t *catz = (dns_catz_zone_t *)data;
2513 char dname[DNS_NAME_FORMATSIZE];
2514
2515 REQUIRE(DNS_CATZ_ZONE_VALID(catz));
2516
2517 LOCK(&catz->catzs->lock);
2518 catz->updaterunning = false;
2519
2520 dns_name_format(&catz->name, dname, DNS_NAME_FORMATSIZE);
2521
2522 if (catz->updatepending && !atomic_load(&catz->catzs->shuttingdown)) {
2523 /* Restart the timer */
2524 dns__catz_timer_start(catz);
2525 }
2526
2527 dns_db_closeversion(catz->updb, &catz->updbversion, false);
2528 dns_db_detach(&catz->updb);
2529
2530 UNLOCK(&catz->catzs->lock);
2531
2532 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_MASTER,
2533 ISC_LOG_INFO, "catz: %s: reload done: %s", dname,
2534 isc_result_totext(catz->updateresult));
2535
2536 dns_catz_zone_unref(catz);
2537 }
2538
2539 void
2540 dns_catz_prereconfig(dns_catz_zones_t *catzs) {
2541 isc_result_t result;
2542 isc_ht_iter_t *iter = NULL;
2543
2544 REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
2545
2546 LOCK(&catzs->lock);
2547 isc_ht_iter_create(catzs->zones, &iter);
2548 for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;
2549 result = isc_ht_iter_next(iter))
2550 {
2551 dns_catz_zone_t *catz = NULL;
2552 isc_ht_iter_current(iter, (void **)&catz);
2553 catz->active = false;
2554 }
2555 UNLOCK(&catzs->lock);
2556 INSIST(result == ISC_R_NOMORE);
2557 isc_ht_iter_destroy(&iter);
2558 }
2559
2560 void
2561 dns_catz_postreconfig(dns_catz_zones_t *catzs) {
2562 isc_result_t result;
2563 dns_catz_zone_t *newcatz = NULL;
2564 isc_ht_iter_t *iter = NULL;
2565
2566 REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
2567
2568 LOCK(&catzs->lock);
2569 isc_ht_iter_create(catzs->zones, &iter);
2570 for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;) {
2571 dns_catz_zone_t *catz = NULL;
2572
2573 isc_ht_iter_current(iter, (void **)&catz);
2574 if (!catz->active) {
2575 char cname[DNS_NAME_FORMATSIZE];
2576 dns_name_format(&catz->name, cname,
2577 DNS_NAME_FORMATSIZE);
2578 isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
2579 DNS_LOGMODULE_MASTER, ISC_LOG_WARNING,
2580 "catz: removing catalog zone %s", cname);
2581
2582 /*
2583 * Merge the old zone with an empty one to remove
2584 * all members.
2585 */
2586 newcatz = dns_catz_zone_new(catzs, &catz->name);
2587 dns__catz_zones_merge(catz, newcatz);
2588 dns_catz_zone_detach(&newcatz);
2589
2590 /* Make sure that we have an empty catalog zone. */
2591 INSIST(isc_ht_count(catz->entries) == 0);
2592 result = isc_ht_iter_delcurrent_next(iter);
2593 dns_catz_zone_detach(&catz);
2594 } else {
2595 result = isc_ht_iter_next(iter);
2596 }
2597 }
2598 UNLOCK(&catzs->lock);
2599 RUNTIME_CHECK(result == ISC_R_NOMORE);
2600 isc_ht_iter_destroy(&iter);
2601 }
2602
2603 void
2604 dns_catz_zone_prereconfig(dns_catz_zone_t *catz) {
2605 LOCK(&catz->lock);
2606 }
2607
2608 void
2609 dns_catz_zone_postreconfig(dns_catz_zone_t *catz) {
2610 UNLOCK(&catz->lock);
2611 }
2612
2613 void
2614 dns_catz_zone_for_each_entry2(dns_catz_zone_t *catz, dns_catz_entry_cb2 cb,
2615 void *arg1, void *arg2) {
2616 REQUIRE(DNS_CATZ_ZONE_VALID(catz));
2617
2618 isc_ht_iter_t *iter = NULL;
2619 isc_result_t result;
2620
2621 LOCK(&catz->catzs->lock);
2622 isc_ht_iter_create(catz->entries, &iter);
2623 for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;
2624 result = isc_ht_iter_next(iter))
2625 {
2626 dns_catz_entry_t *entry = NULL;
2627
2628 isc_ht_iter_current(iter, (void **)&entry);
2629 cb(entry, arg1, arg2);
2630 }
2631 isc_ht_iter_destroy(&iter);
2632 UNLOCK(&catz->catzs->lock);
2633 }
2634