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