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