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