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