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