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