1/* 2 * xfrd-catalog-zones.c -- catalog zone implementation for NSD 3 * 4 * Copyright (c) 2024, NLnet Labs. All rights reserved. 5 * 6 * See LICENSE for the license. 7 */ 8#include "config.h" 9#include "difffile.h" 10#include "nsd.h" 11#include "packet.h" 12#include "xfrd-catalog-zones.h" 13#include "xfrd-notify.h" 14 15 16/****************** ****************** 17 ****************** catalog consumer zone processing ****************** 18 ****************** ******************/ 19 20/** process a catalog consumer zone, load if needed */ 21static void xfrd_process_catalog_consumer_zone( 22 struct xfrd_catalog_consumer_zone* consumer_zone); 23 24/** make the catalog consumer zone invalid for given reason */ 25static void vmake_catalog_consumer_invalid( 26 struct xfrd_catalog_consumer_zone *consumer_zone, 27 const char *format, va_list args); 28 29/** return (static) dname with label prepended to dname */ 30static dname_type* label_plus_dname(const char* label,const dname_type* dname); 31 32/** delete the catalog member zone */ 33static void catalog_del_consumer_member_zone( 34 struct xfrd_catalog_consumer_zone* consumer_zone, 35 struct catalog_member_zone* consumer_member_zone); 36 37#ifndef MULTIPLE_CATALOG_CONSUMER_ZONES 38/* return a single catalog consumer zone from xfrd struct */ 39static inline struct xfrd_catalog_consumer_zone* 40xfrd_one_catalog_consumer_zone() 41{ 42 return xfrd 43 && xfrd->catalog_consumer_zones 44 && xfrd->catalog_consumer_zones->count == 1 45 ? (struct xfrd_catalog_consumer_zone*) 46 rbtree_first(xfrd->catalog_consumer_zones) : NULL; 47} 48#endif 49 50/** return the catalog-member-pattern or NULL on error if not present */ 51static inline struct pattern_options* 52catalog_member_pattern(struct xfrd_catalog_consumer_zone* consumer_zone) 53{ 54 if (!consumer_zone->options->pattern 55 || !consumer_zone->options->pattern->catalog_member_pattern) 56 return NULL; 57 return pattern_options_find(xfrd->nsd->options, 58 consumer_zone->options->pattern->catalog_member_pattern); 59} 60 61/** see if we have more zonestatistics entries and it has to be incremented */ 62static inline void 63zonestat_inc_ifneeded() 64{ 65#ifdef USE_ZONE_STATS 66 if(xfrd->nsd->options->zonestatnames->count != xfrd->zonestat_safe) 67 task_new_zonestat_inc(xfrd->nsd->task[xfrd->nsd->mytask], 68 xfrd->last_task, 69 xfrd->nsd->options->zonestatnames->count); 70#endif /* USE_ZONE_STATS */ 71} 72 73 74/****************** ****************** 75 ****************** catalog producer zone processing ****************** 76 ****************** ******************/ 77 78/** process catalog producer zone producer_zone */ 79static void xfrd_process_catalog_producer_zone( 80 struct xfrd_catalog_producer_zone* producer_zone); 81 82/** rbnode must be struct catalog_member_zone*; compares (key->member_id) */ 83static int member_id_compare(const void *left, const void *right); 84 85/** return xfrd_catalog_producer_zone* pointed to by cmz' catalog-producer-zone 86 * pattern option. struct is created if necessary. returns NULL on failure. */ 87static struct xfrd_catalog_producer_zone* xfrd_get_catalog_producer_zone( 88 struct catalog_member_zone* cmz); 89 90/** helper struct for generating XFR files, for conveying the catalog producer 91 * zone content to the server process. 92 */ 93struct xfrd_xfr_writer { 94 struct xfrd_catalog_producer_zone* producer_zone; 95 char packet_space[16384]; 96 buffer_type packet; 97 uint32_t seq_nr; /* number of messages already handled */ 98 uint32_t old_serial, new_serial; /* host byte order */ 99 uint64_t xfrfilenumber; /* identifier for file to store xfr into */ 100}; 101 102/** initialize xfrd_xfr_writer struct xw */ 103static void xfr_writer_init(struct xfrd_xfr_writer* xw, 104 struct xfrd_catalog_producer_zone* producer_zone); 105 106/** write packet from xfrd_xfr_writer struct xw to xfr file */ 107static void xfr_writer_write_packet(struct xfrd_xfr_writer* xw); 108 109/** commit xfr file (send to server process), with provided log message */ 110static void xfr_writer_commit(struct xfrd_xfr_writer* xw, const char *fmt, 111 ...); 112 113/** try writing SOA RR with serial to packet buffer. returns 0 on failure */ 114static int try_buffer_write_SOA(buffer_type* packet, const dname_type* owner, 115 uint32_t serial); 116 117/** try writing RR to packet buffer. returns 0 on failure */ 118static int try_buffer_write_RR(buffer_type* packet, const dname_type* owner, 119 uint16_t rr_type, uint16_t rdata_len, const void* rdata); 120 121/** try writing PTR RR to packet buffer. returns 0 on failure */ 122static inline int try_buffer_write_PTR(buffer_type* packet, 123 const dname_type* owner, const dname_type* name); 124 125/** try writing TXT RR to packet buffer. returns 0 on failure */ 126static int try_buffer_write_TXT(buffer_type* packet, const dname_type* name, 127 const char *txt); 128 129/** add SOA RR with serial serial to xfrd_xfr_writer xw */ 130static inline void xfr_writer_add_SOA(struct xfrd_xfr_writer* xw, 131 const dname_type* owner, uint32_t serial) 132{ 133 if(try_buffer_write_SOA(&xw->packet, owner, serial)) 134 return; 135 xfr_writer_write_packet(xw); 136 assert(buffer_position(&xw->packet) == 12); 137 try_buffer_write_SOA(&xw->packet, owner, serial); 138} 139 140/** add RR to xfrd_xfr_writer xw */ 141static inline void xfr_writer_add_RR(struct xfrd_xfr_writer* xw, 142 const dname_type* owner, 143 uint16_t rr_type, uint16_t rdata_len, const void* rdata) 144{ 145 if(try_buffer_write_RR(&xw->packet, owner, rr_type, rdata_len, rdata)) 146 return; 147 xfr_writer_write_packet(xw); 148 assert(buffer_position(&xw->packet) == 12); 149 try_buffer_write_RR(&xw->packet, owner, rr_type, rdata_len, rdata); 150} 151 152/** add PTR RR to xfrd_xfr_writer xw */ 153static inline void xfr_writer_add_PTR(struct xfrd_xfr_writer* xw, 154 const dname_type* owner, const dname_type* name) 155{ 156 if(try_buffer_write_PTR(&xw->packet, owner, name)) 157 return; 158 xfr_writer_write_packet(xw); 159 assert(buffer_position(&xw->packet) == 12); 160 try_buffer_write_PTR(&xw->packet, owner, name); 161} 162 163/** add TXT RR to xfrd_xfr_writer xw */ 164static inline void xfr_writer_add_TXT(struct xfrd_xfr_writer* xw, 165 const dname_type* owner, const char* txt) 166{ 167 if(try_buffer_write_TXT(&xw->packet, owner, txt)) 168 return; 169 xfr_writer_write_packet(xw); 170 assert(buffer_position(&xw->packet) == 12); 171 try_buffer_write_TXT(&xw->packet, owner, txt); 172} 173 174 175/****************** ****************** 176 ****************** catalog consumer zone processing ****************** 177 ****************** ******************/ 178 179void 180xfrd_init_catalog_consumer_zone(xfrd_state_type* xfrd, 181 struct zone_options* zone) 182{ 183 struct xfrd_catalog_consumer_zone* consumer_zone; 184 185 if ((consumer_zone = (struct xfrd_catalog_consumer_zone*)rbtree_search( 186 xfrd->catalog_consumer_zones, zone->node.key))) { 187 log_msg(LOG_ERR, "cannot initialize new catalog consumer zone:" 188 " '%s: it already exists in xfrd's catalog " 189 " consumer zones index", zone->name); 190 /* Maybe we need to reprocess it? */ 191 make_catalog_consumer_valid(consumer_zone); 192 return; 193 } 194 consumer_zone = (struct xfrd_catalog_consumer_zone*) 195 region_alloc(xfrd->region, 196 sizeof(struct xfrd_catalog_consumer_zone)); 197 memset(consumer_zone, 0, sizeof(struct xfrd_catalog_consumer_zone)); 198 consumer_zone->node.key = zone->node.key; 199 consumer_zone->options = zone; 200 consumer_zone->member_ids.region = xfrd->region; 201 consumer_zone->member_ids.root = RBTREE_NULL; 202 consumer_zone->member_ids.count = 0; 203 consumer_zone->member_ids.cmp = member_id_compare; 204 consumer_zone->mtime.tv_sec = 0; 205 consumer_zone->mtime.tv_nsec = 0; 206 207 consumer_zone->invalid = NULL; 208 rbtree_insert(xfrd->catalog_consumer_zones, 209 (rbnode_type*)consumer_zone); 210#ifndef MULTIPLE_CATALOG_CONSUMER_ZONES 211 if ((int)xfrd->catalog_consumer_zones->count > 1) { 212 log_msg(LOG_ERR, "catalog consumer processing disabled: " 213 "only one single catalog consumer zone allowed"); 214 } 215#endif 216 if(zone->pattern && zone->pattern->store_ixfr) { 217 /* Don't process ixfrs from xfrd */ 218 zone->pattern->store_ixfr = 0; 219 } 220} 221 222void 223xfrd_deinit_catalog_consumer_zone(xfrd_state_type* xfrd, 224 const dname_type* dname) 225{ 226 struct xfrd_catalog_consumer_zone* consumer_zone; 227 zone_type* zone; 228 229 if (!(consumer_zone =(struct xfrd_catalog_consumer_zone*)rbtree_delete( 230 xfrd->catalog_consumer_zones, dname))) { 231 log_msg(LOG_ERR, "cannot de-initialize catalog consumer zone:" 232 " '%s: it did not exist in xfrd's catalog " 233 " consumer zones index", 234 dname_to_string(dname, NULL)); 235 return; 236 } 237 if (consumer_zone->member_ids.count) 238 log_msg(LOG_WARNING, "de-initialize catalog consumer zone:" 239 " '%s: will cause all member zones to be " 240 " deleted", consumer_zone->options->name); 241 242 while (consumer_zone->member_ids.count) { 243 struct catalog_member_zone* cmz = (struct catalog_member_zone*) 244 rbtree_first(&consumer_zone->member_ids)->key; 245 246 log_msg(LOG_INFO, "deleting member zone '%s' on " 247 "de-initializing catalog consumer zone '%s'", 248 cmz->options.name, consumer_zone->options->name); 249 catalog_del_consumer_member_zone(consumer_zone, cmz); 250 } 251 if ((zone = namedb_find_zone(xfrd->nsd->db, dname))) { 252 namedb_zone_delete(xfrd->nsd->db, zone); 253 } 254 region_recycle(xfrd->region, consumer_zone, sizeof(*consumer_zone)); 255#ifndef MULTIPLE_CATALOG_CONSUMER_ZONES 256 if((consumer_zone = xfrd_one_catalog_consumer_zone()) 257 && consumer_zone->options && consumer_zone->options->node.key) { 258 xfrd_zone_type* zone = (xfrd_zone_type*)rbtree_search( 259 xfrd->zones, 260 (const dname_type*)consumer_zone->options->node.key); 261 262 if(zone) { 263 zone->soa_disk_acquired = 0; 264 zone->soa_nsd_acquired = 0; 265 xfrd_handle_notify_and_start_xfr(zone, NULL); 266 } 267 } 268#endif 269} 270 271/** make the catalog consumer zone invalid for given reason */ 272static void 273vmake_catalog_consumer_invalid( 274 struct xfrd_catalog_consumer_zone *consumer_zone, 275 const char *format, va_list args) 276{ 277 char message[MAXSYSLOGMSGLEN]; 278 if (!consumer_zone || consumer_zone->invalid) return; 279 vsnprintf(message, sizeof(message), format, args); 280 log_msg(LOG_ERR, "invalid catalog consumer zone '%s': %s", 281 consumer_zone->options->name, message); 282 consumer_zone->invalid = region_strdup(xfrd->region, message); 283} 284 285void 286make_catalog_consumer_invalid(struct xfrd_catalog_consumer_zone *consumer_zone, 287 const char *format, ...) 288{ 289 va_list args; 290 if (!consumer_zone || consumer_zone->invalid) return; 291 va_start(args, format); 292 vmake_catalog_consumer_invalid(consumer_zone, format, args); 293 va_end(args); 294} 295 296void 297make_catalog_consumer_valid(struct xfrd_catalog_consumer_zone *consumer_zone) 298{ 299 if (consumer_zone->invalid) { 300 region_recycle(xfrd->region, consumer_zone->invalid, 301 strlen(consumer_zone->invalid) + 1); 302 consumer_zone->invalid = NULL; 303 } 304} 305 306static dname_type* 307label_plus_dname(const char* label, const dname_type* dname) 308{ 309 static struct { 310 dname_type dname; 311 uint8_t bytes[MAXDOMAINLEN + 128 /* max number of labels */]; 312 } ATTR_PACKED name; 313 size_t i, ll; 314 315 if (!label || !dname || dname->label_count > 127) 316 return NULL; 317 ll = strlen(label); 318 if ((int)dname->name_size + ll + 1 > MAXDOMAINLEN) 319 return NULL; 320 321 /* In reversed order and first copy with memmove, so we can nest. 322 * i.e. label_plus_dname(label1, label_plus_dname(label2, dname)) 323 */ 324 memmove(name.bytes + dname->label_count 325 + 1 /* label_count increases by one */ 326 + 1 /* label type/length byte for label */ + ll, 327 ((void*)dname) + sizeof(dname_type) + dname->label_count, 328 dname->name_size); 329 memcpy(name.bytes + dname->label_count 330 + 1 /* label_count increases by one */ 331 + 1 /* label type/length byte for label */, label, ll); 332 name.bytes[dname->label_count + 1] = ll; /* label type/length byte */ 333 name.bytes[dname->label_count] = 0; /* first label follows last 334 * label_offsets element */ 335 for (i = 0; i < dname->label_count; i++) 336 name.bytes[i] = ((uint8_t*)(void*)dname)[sizeof(dname_type)+i] 337 + 1 /* label type/length byte for label */ + ll; 338 name.dname.label_count = dname->label_count + 1 /* label_count incr. */; 339 name.dname.name_size = dname->name_size + ll 340 + 1 /* label length */; 341 return &name.dname; 342} 343 344static void 345catalog_del_consumer_member_zone( 346 struct xfrd_catalog_consumer_zone* consumer_zone, 347 struct catalog_member_zone* consumer_member_zone) 348{ 349 const dname_type* dname = consumer_member_zone->options.node.key; 350 351 /* create deletion task */ 352 task_new_del_zone(xfrd->nsd->task[xfrd->nsd->mytask], 353 xfrd->last_task, dname); 354 xfrd_set_reload_now(xfrd); 355 /* delete it in xfrd */ 356 if(zone_is_slave(&consumer_member_zone->options)) { 357 xfrd_del_slave_zone(xfrd, dname); 358 } 359 xfrd_del_notify(xfrd, dname); 360#ifdef MULTIPLE_CATALOG_CONSUMER_ZONES 361 /* delete it in xfrd's catalog consumers list */ 362 if(zone_is_catalog_consumer(&consumer_member_zone->options)) { 363 xfrd_deinit_catalog_consumer_zone(xfrd, dname); 364 } 365#endif 366 if(consumer_member_zone->member_id) { 367 rbtree_delete(&consumer_zone->member_ids,consumer_member_zone); 368 consumer_member_zone->node = *RBTREE_NULL; 369 region_recycle( xfrd->nsd->options->region, 370 (void*)consumer_member_zone->member_id, 371 dname_total_size(consumer_member_zone->member_id)); 372 consumer_member_zone->member_id = NULL; 373 } 374 zone_options_delete(xfrd->nsd->options,&consumer_member_zone->options); 375} 376 377void xfrd_check_catalog_consumer_zonefiles(const dname_type* name) 378{ 379 struct xfrd_catalog_consumer_zone* consumer_zone; 380 381#ifndef MULTIPLE_CATALOG_CONSUMER_ZONES 382 consumer_zone = xfrd_one_catalog_consumer_zone(); 383 if (!consumer_zone) 384 return; 385 if (name && dname_compare(name, consumer_zone->node.key) != 0) 386 return; 387 name = consumer_zone->node.key; 388 DEBUG(DEBUG_XFRD,1, (LOG_INFO, "Mark %s " 389 "for checking", consumer_zone->options->name)); 390 make_catalog_consumer_valid(consumer_zone); 391 namedb_read_zonefile(xfrd->nsd, namedb_find_or_create_zone( 392 xfrd->nsd->db, name, consumer_zone->options), NULL, NULL); 393#else 394 if (!name) { 395 RBTREE_FOR(consumer_zone, struct xfrd_catalog_consumer_zone*, 396 xfrd->catalog_consumer_zones) { 397 make_catalog_consumer_valid(consumer_zone); 398 namedb_read_zonefile(xfrd->nsd, 399 namedb_find_or_create_zone(xfrd->nsd->db, 400 consumer_zone->options->node.key, 401 consumer_zone->options), 402 NULL, NULL); 403 } 404 } else if ((consumer_zone = (struct xfrd_catalog_consumer_zone*) 405 rbtree_search(xfrd->catalog_consumer_zones, name))) { 406 make_catalog_consumer_valid(consumer_zone); 407 namedb_read_zonefile(xfrd->nsd, 408 namedb_find_or_create_zone( 409 xfrd->nsd->db, name, consumer_zone->options), 410 NULL, NULL); 411 } 412#endif 413} 414 415const char *invalid_catalog_consumer_zone(struct zone_options* zone) 416{ 417 struct xfrd_catalog_consumer_zone* consumer_zone; 418 const char *msg; 419 420 if (!zone || !zone_is_catalog_consumer(zone)) 421 msg = NULL; 422 423 else if (!xfrd) 424 msg = "asked for catalog information outside of xfrd process"; 425 426 else if (!xfrd->catalog_consumer_zones) 427 msg = "zone not found: " 428 "xfrd's catalog consumer zones index is empty"; 429 430#ifndef MULTIPLE_CATALOG_CONSUMER_ZONES 431 else if (xfrd->catalog_consumer_zones->count > 1) 432 return "not processing: more than one catalog consumer zone " 433 "configured and only a single one allowed"; 434#endif 435 else if (!(consumer_zone = (struct xfrd_catalog_consumer_zone*) 436 rbtree_search(xfrd->catalog_consumer_zones, zone->node.key))) 437 msg = "zone not found in xfrd's catalog consumer zones index"; 438 else 439 return consumer_zone->invalid; 440 441 if (msg) 442 log_msg(LOG_ERR, "catalog consumer zone '%s': %s", 443 zone->name, msg); 444 445 return msg; 446} 447 448void xfrd_process_catalog_consumer_zones() 449{ 450 struct xfrd_catalog_consumer_zone* consumer_zone; 451 452#ifndef MULTIPLE_CATALOG_CONSUMER_ZONES 453 if((consumer_zone = xfrd_one_catalog_consumer_zone())) 454 xfrd_process_catalog_consumer_zone(consumer_zone); 455#else 456 RBTREE_FOR(consumer_zone, struct xfrd_catalog_consumer_zone*, 457 xfrd->catalog_consumer_zones) { 458 xfrd_process_catalog_consumer_zone(consumer_zone); 459 } 460#endif 461} 462 463static inline struct catalog_member_zone* cursor_cmz(rbnode_type* node) 464{ return node != RBTREE_NULL ? (struct catalog_member_zone*)node->key : NULL; } 465static inline const dname_type* cursor_member_id(rbnode_type* node) 466{ return cursor_cmz(node) ? cursor_cmz(node)->member_id : NULL; } 467 468#if !defined(NDEBUG) && 1 /* Only disable for seriously slow debugging */ 469static void debug_log_consumer_members( 470 struct xfrd_catalog_consumer_zone* consumer_zone) 471{ 472 rbnode_type* cursor; 473 size_t i; 474 475 for ( cursor = rbtree_first(&consumer_zone->member_ids), i = 0 476 ; cursor != RBTREE_NULL; i++, cursor = rbtree_next(cursor)) { 477 DEBUG(DEBUG_XFRD,1, (LOG_INFO, "Catalog member %.2zu: %s = %s", 478 i, dname_to_string(cursor_member_id(cursor), NULL), 479 cursor_cmz(cursor)->options.name)); 480 } 481} 482#else 483# define debug_log_consumer_members(x) /* nothing */ 484#endif 485 486static void 487xfrd_process_catalog_consumer_zone( 488 struct xfrd_catalog_consumer_zone* consumer_zone) 489{ 490 zone_type* zone; 491 const dname_type* dname; 492 domain_type *match, *closest_encloser, *member_id, *group; 493 rrset_type *rrset; 494 size_t i; 495 uint8_t version_2_found; 496 /* Currect catalog member zone */ 497 rbnode_type* cursor; 498 struct pattern_options *default_pattern = NULL; 499 /* A transfer of a catalog zone can contain deletion and adding of 500 * the same member zone. In such cases it can occur that the member 501 * is tried to be added before it is deleted. For these exceptional 502 * cases, we will rewalk the zone after the first pass, to retry 503 * adding those zones. 504 * 505 * Initial pass is mode "try_to_add". 506 * If a zone cannot be added, mode is set to "retry_to_add" 507 * If after the first pass the mode is "retry_to_add", 508 * mode will be set to "just_add", and a second pass is done. 509 */ 510 enum { try_to_add, retry_to_add, just_add } mode; 511 512 assert(consumer_zone); 513 if (!xfrd->nsd->db) { 514 xfrd->nsd->db = namedb_open(xfrd->nsd->options); 515 } 516 dname = (const dname_type*)consumer_zone->node.key; 517 if (dname->name_size > 247) { 518 make_catalog_consumer_invalid(consumer_zone, "name too long"); 519 return; 520 } 521 if (dname->label_count > 126) { 522 make_catalog_consumer_invalid(consumer_zone,"too many labels"); 523 return; 524 } 525 zone = namedb_find_zone(xfrd->nsd->db, dname); 526 if (!zone) { 527 zone = namedb_zone_create(xfrd->nsd->db, dname, 528 consumer_zone->options); 529 namedb_read_zonefile(xfrd->nsd, zone, NULL, NULL); 530 } 531 if (timespec_compare(&consumer_zone->mtime, &zone->mtime) == 0) { 532 /* Not processing unchanged catalog consumer zone */ 533 return; 534 } 535 consumer_zone->mtime = zone->mtime; 536 /* start processing */ 537 /* Lookup version.<consumer_zone> TXT and check that it is version 2 */ 538 if(!namedb_lookup(xfrd->nsd->db, label_plus_dname("version", dname), 539 &match, &closest_encloser) 540 || !(rrset = domain_find_rrset(match, zone, TYPE_TXT))) { 541 make_catalog_consumer_invalid(consumer_zone, 542 "'version.%s TXT RRset not found", 543 consumer_zone->options->name); 544 return; 545 } 546 version_2_found = 0; 547 for (i = 0; i < rrset->rr_count; i++) { 548 if (rrset->rrs[i].rdata_count != 1) 549 continue; 550 if (rrset->rrs[i].rdatas[0].data[0] == 2 551 && ((uint8_t*)(rrset->rrs[i].rdatas[0].data + 1))[0] == 1 552 && ((uint8_t*)(rrset->rrs[i].rdatas[0].data + 1))[1] == '2') { 553 version_2_found = 1; 554 break; 555 } 556 } 557 if (!version_2_found) { 558 make_catalog_consumer_invalid(consumer_zone, 559 "'version.%s' TXT RR with value \"2\" not found", 560 consumer_zone->options->name); 561 return; 562 } 563 /* Walk over all names under zones.<consumer_zone> */ 564 if(!namedb_lookup(xfrd->nsd->db, label_plus_dname("zones", dname), 565 &match, &closest_encloser)) { 566 /* zones.<consumer_zone> does not exist, so the catalog has no 567 * members. This is just fine. But there may be members that need 568 * to be deleted. 569 */ 570 cursor = rbtree_first(&consumer_zone->member_ids); 571 mode = just_add; 572 goto delete_members; 573 } 574 mode = consumer_zone->member_ids.count ? try_to_add : just_add; 575retry_adding: 576 cursor = rbtree_first(&consumer_zone->member_ids); 577 for ( member_id = domain_next(match) 578 ; member_id && domain_is_subdomain(member_id, match) 579 ; member_id = domain_next(member_id)) { 580 domain_type *member_domain; 581 char member_domain_str[5 * MAXDOMAINLEN]; 582 struct zone_options* zopt; 583 int valid_group_values; 584 struct pattern_options *pattern = NULL; 585 struct catalog_member_zone* to_add; 586 587 if (domain_dname(member_id)->label_count > dname->label_count+2 588 || !(rrset = domain_find_rrset(member_id, zone, TYPE_PTR))) 589 continue; 590 591 /* RFC9432 Section 4.1. Member Zones: 592 * 593 * `` This PTR record MUST be the only record in the PTR RRset 594 * with the same name. The presence of more than one record 595 * in the RRset indicates a broken catalog zone that MUST 596 * NOT be processed (see Section 5.1). 597 */ 598 if (rrset->rr_count != 1) { 599 make_catalog_consumer_invalid(consumer_zone, 600 "only a single PTR RR expected on '%s'", 601 domain_to_string(member_id)); 602 return; 603 } 604 /* A PTR rr always has 1 rdata element which is a dname */ 605 if (rrset->rrs[0].rdata_count != 1) 606 continue; 607 member_domain = rrset->rrs[0].rdatas[0].domain; 608 domain_to_string_buf(member_domain, member_domain_str); 609 /* remove trailing dot */ 610 member_domain_str[strlen(member_domain_str) - 1] = 0; 611 612 valid_group_values = 0; 613 /* Lookup group.<member_id> TXT for matching patterns */ 614 if(!namedb_lookup(xfrd->nsd->db, label_plus_dname("group", 615 domain_dname(member_id)), 616 &group, &closest_encloser) 617 || !(rrset = domain_find_rrset(group, zone, TYPE_TXT))) { 618 ; /* pass */ 619 620 } else for (i = 0; i < rrset->rr_count; i++) { 621 /* Max single TXT rdata field length + '\x00' == 256 */ 622 char group_value[256]; 623 624 /* Looking for a single TXT rdata field */ 625 if (rrset->rrs[i].rdata_count != 1 626 627 /* rdata field should be at least 1 char */ 628 || rrset->rrs[i].rdatas[0].data[0] < 2 629 630 /* single rdata atom with single TXT rdata field */ 631 || (uint16_t)(((uint8_t*)(rrset->rrs[i].rdatas[0].data + 1))[0]) 632 != (uint16_t) (rrset->rrs[i].rdatas[0].data[0]-1)) 633 continue; 634 635 memcpy( group_value 636 , (uint8_t*)(rrset->rrs[i].rdatas[0].data+1) + 1 637 ,((uint8_t*)(rrset->rrs[i].rdatas[0].data+1))[0] 638 ); 639 group_value[ 640 ((uint8_t*)(rrset->rrs[i].rdatas[0].data+1))[0] 641 ] = 0; 642 if ((pattern = pattern_options_find( 643 xfrd->nsd->options, group_value))) 644 valid_group_values += 1; 645 } 646 if (valid_group_values > 1) { 647 log_msg(LOG_ERR, "member zone '%s': only a single " 648 "group property that matches a pattern is " 649 "allowed." 650 "The pattern from \"catalog-member-pattern\" " 651 "will be used instead.", 652 domain_to_string(member_id)); 653 valid_group_values = 0; 654 655 } else if (valid_group_values == 1 && pattern 656 && pattern->catalog_producer_zone) { 657 log_msg(LOG_ERR, "member zone '%s': group property " 658 "'%s' matches a catalog producer member zone " 659 "pattern. In NSD, catalog member zones can be " 660 "either a member of a catalog consumer zone or" 661 " a catalog producer zone, but not both.", 662 domain_to_string(member_id), pattern->pname); 663 valid_group_values = 0; 664 } 665 if (valid_group_values == 1) { 666 /* pass: pattern is already set */ 667 assert(pattern); 668 669 } else if (default_pattern) 670 pattern = default_pattern; /* pass */ 671 672 else if (!(pattern = default_pattern = 673 catalog_member_pattern(consumer_zone))) { 674 make_catalog_consumer_invalid(consumer_zone, 675 "missing 'group.%s' TXT RR and no default " 676 "pattern from \"catalog-member-pattern\"", 677 domain_to_string(member_id)); 678 return; 679 } 680 if (cursor == RBTREE_NULL) 681 ; /* End of the current member zones list. 682 * From here onwards, zones will only be added. 683 */ 684 else { 685 int cmp = 0; 686#ifndef NDEBUG 687 char member_id_str[5 * MAXDOMAINLEN]; 688 domain_to_string_buf(member_id, member_id_str); 689#endif 690 DEBUG(DEBUG_XFRD,1, (LOG_INFO, "Comparing %s with %s", 691 member_id_str, 692 dname_to_string(cursor_member_id(cursor), 693 NULL))); 694 695 while (cursor != RBTREE_NULL && 696 (cmp = dname_compare( 697 domain_dname(member_id), 698 cursor_member_id(cursor))) > 0) { 699 /* member_id is ahead of the current catalog 700 * member zone pointed to by cursor. 701 * The member zone must be deleted. 702 */ 703 struct catalog_member_zone* to_delete = 704 cursor_cmz(cursor); 705#ifndef NDEBUG 706 const char *member_id_to_delete_str = 707 dname_to_string(to_delete->member_id, NULL); 708#endif 709 cursor = rbtree_next(cursor); 710 711 DEBUG(DEBUG_XFRD,1, (LOG_INFO, 712 "%s > %s: delete %s", 713 member_id_str, 714 member_id_to_delete_str, 715 member_id_to_delete_str)); 716 catalog_del_consumer_member_zone( 717 consumer_zone, to_delete); 718 if(cursor != RBTREE_NULL) 719 DEBUG(DEBUG_XFRD,1, (LOG_INFO, 720 "Comparing %s with %s", 721 member_id_str, 722 dname_to_string( 723 cursor_member_id(cursor), 724 NULL))); 725 } 726 if (cursor != RBTREE_NULL && cmp == 0) { 727 /* member_id is also in an current catalog 728 * member zone, and cursor is pointing 729 * to it. So, move along ... 730 */ 731 /* ... but first check if the pattern needs 732 * a change 733 */ 734 DEBUG(DEBUG_XFRD,1, (LOG_INFO, "%s == %s: " 735 "Compare pattern %s with %s", 736 member_id_str, member_id_str, 737 cursor_cmz(cursor)->options.pattern->pname, 738 pattern->pname)); 739 740 if (cursor_cmz(cursor)->options.pattern == 741 pattern) 742 ; /* pass: Pattern remains the same */ 743 else { 744 /* Changing patterns is basically 745 * deleting and adding the zone again 746 */ 747 zopt = &cursor_cmz(cursor)->options; 748 dname = (dname_type *)zopt->node.key; 749 task_new_del_zone( 750 xfrd->nsd->task[xfrd->nsd->mytask], 751 xfrd->last_task, 752 dname); 753 xfrd_set_reload_now(xfrd); 754 if(zone_is_slave(zopt)) { 755 xfrd_del_slave_zone( xfrd 756 , dname); 757 } 758 xfrd_del_notify(xfrd, dname); 759#ifdef MULTIPLE_CATALOG_CONSUMER_ZONES 760 if(zone_is_catalog_consumer(zopt)) { 761 xfrd_deinit_catalog_consumer_zone( 762 xfrd, dname); 763 } 764#endif 765 /* It is a catalog consumer member, 766 * so no need to check if it was a 767 * catalog producer member zone to 768 * delete and add 769 */ 770 zopt->pattern = pattern; 771 task_new_add_zone( 772 xfrd->nsd->task[xfrd->nsd->mytask], 773 xfrd->last_task, zopt->name, 774 pattern->pname, 775 getzonestatid( xfrd->nsd->options 776 , zopt)); 777 zonestat_inc_ifneeded(); 778 xfrd_set_reload_now(xfrd); 779#ifdef MULTIPLE_CATALOG_CONSUMER_ZONES 780 if(zone_is_catalog_consumer(zopt)) { 781 xfrd_init_catalog_consumer_zone( 782 xfrd, zopt); 783 } 784#endif 785 init_notify_send(xfrd->notify_zones, 786 xfrd->region, zopt); 787 if(zone_is_slave(zopt)) { 788 xfrd_init_slave_zone( 789 xfrd, zopt); 790 } 791 } 792 cursor = rbtree_next(cursor); 793 continue; 794 } 795 /* member_id is not in the current catalog member zone 796 * list, so it must be added 797 */ 798 assert(cursor == RBTREE_NULL || cmp < 0); 799 } 800 /* See if the zone already exists */ 801 zopt = zone_options_find(xfrd->nsd->options, 802 domain_dname(member_domain)); 803 if (zopt) { 804 /* Produce warning if zopt is from other catalog. 805 * Give debug message if zopt is not from this catalog. 806 */ 807 switch(mode) { 808 case try_to_add: 809 mode = retry_to_add; 810 break; 811 case just_add: 812 DEBUG(DEBUG_XFRD,1, (LOG_INFO, "Cannot add " 813 "catalog member zone %s (from %s): " 814 "zone already exists", 815 member_domain_str, 816 domain_to_string(member_id))); 817 break; 818 default: 819 break; 820 } 821 continue; 822 } 823 /* Add member zone if not already there */ 824 log_msg(LOG_INFO, "Adding '%s' PTR '%s'", 825 domain_to_string(member_id), 826 member_domain_str); 827 DEBUG(DEBUG_XFRD,1, (LOG_INFO, "Adding %s PTR %s", 828 domain_to_string(member_id), member_domain_str)); 829 to_add= catalog_member_zone_create(xfrd->nsd->options->region); 830 to_add->options.name = region_strdup( 831 xfrd->nsd->options->region, member_domain_str); 832 to_add->options.pattern = pattern; 833 if (!nsd_options_insert_zone(xfrd->nsd->options, 834 &to_add->options)) { 835 log_msg(LOG_ERR, "bad domain name '%s' pattern %s", 836 member_domain_str, 837 ( pattern->pname ? pattern->pname: "<NULL>")); 838 zone_options_delete(xfrd->nsd->options, 839 &to_add->options); 840 continue; 841 } 842 to_add->member_id = dname_copy( xfrd->nsd->options->region 843 , domain_dname(member_id)); 844 /* Insert into the members_id list */ 845 to_add->node.key = to_add; 846 if(!rbtree_insert( &consumer_zone->member_ids, &to_add->node)){ 847 log_msg(LOG_ERR, "Error adding '%s' PTR '%s' to " 848 "consumer_zone->member_ids", 849 domain_to_string(member_id), 850 member_domain_str); 851 break; 852 } else 853 cursor = rbtree_next(&to_add->node); 854 /* make addzone task and schedule reload */ 855 task_new_add_zone(xfrd->nsd->task[xfrd->nsd->mytask], 856 xfrd->last_task, member_domain_str, 857 pattern->pname, 858 getzonestatid(xfrd->nsd->options, &to_add->options)); 859 zonestat_inc_ifneeded(); 860 xfrd_set_reload_now(xfrd); 861#ifdef MULTIPLE_CATALOG_CONSUMER_ZONES 862 /* add to xfrd - catalog consumer zones */ 863 if(zone_is_catalog_consumer(&to_add->options)) { 864 xfrd_init_catalog_consumer_zone(xfrd,&to_add->options); 865 } 866#endif 867 /* add to xfrd - notify (for master and slaves) */ 868 init_notify_send(xfrd->notify_zones, xfrd->region, 869 &to_add->options); 870 /* add to xfrd - slave */ 871 if(zone_is_slave(&to_add->options)) { 872 xfrd_init_slave_zone(xfrd, &to_add->options); 873 } 874 DEBUG(DEBUG_XFRD,1, (LOG_INFO, "Added catalog " 875 "member zone %s (from %s)", 876 member_domain_str, domain_to_string(member_id))); 877 } 878delete_members: 879 while (cursor != RBTREE_NULL) { 880 /* Any current catalog member zones remaining, don't have an 881 * member_id in the catalog anymore, so should be deleted too. 882 */ 883 struct catalog_member_zone* to_delete = cursor_cmz(cursor); 884 885 cursor = rbtree_next(cursor); 886 catalog_del_consumer_member_zone(consumer_zone, to_delete); 887 } 888 if(mode == retry_to_add) { 889 mode = just_add; 890 goto retry_adding; 891 } 892 debug_log_consumer_members(consumer_zone); 893 make_catalog_consumer_valid(consumer_zone); 894} 895 896 897/****************** ****************** 898 ****************** catalog producer zone processing ****************** 899 ****************** ******************/ 900 901static int member_id_compare(const void *left, const void *right) 902{ 903 return dname_compare( ((struct catalog_member_zone*)left )->member_id 904 , ((struct catalog_member_zone*)right)->member_id); 905} 906 907static struct xfrd_catalog_producer_zone* 908xfrd_get_catalog_producer_zone(struct catalog_member_zone* cmz) 909{ 910 struct zone_options *producer_zopt; 911 struct xfrd_catalog_producer_zone* producer_zone; 912 const dname_type* producer_name; 913 const char* producer_name_str; 914 915 assert(xfrd); 916 if(!cmz || !cmz->options.pattern->catalog_producer_zone) 917 return NULL; 918 919 /* TODO: Store as dname in pattern->catalog_producer_zone */ 920 producer_name = dname_parse(xfrd->nsd->options->region, 921 cmz->options.pattern->catalog_producer_zone); 922 producer_zopt = zone_options_find(xfrd->nsd->options, producer_name); 923 producer_name_str = dname_to_string(producer_name, NULL); 924 region_recycle( xfrd->nsd->options->region, (void *)producer_name 925 , dname_total_size(producer_name)); 926 if(!producer_zopt) { 927 log_msg(LOG_ERR, "catalog producer zone '%s' not found for " 928 "zone '%s'", producer_name_str, cmz->options.name); 929 return NULL; 930 } 931 if(!zone_is_catalog_producer(producer_zopt)) { 932 log_msg(LOG_ERR, "cannot add catalog producer member " 933 "zone '%s' to non producer zone '%s'", 934 cmz->options.name, producer_zopt->name); 935 return NULL; 936 } 937 producer_name = (dname_type*)producer_zopt->node.key; 938 producer_zone = (struct xfrd_catalog_producer_zone*) 939 rbtree_search(xfrd->catalog_producer_zones, producer_name); 940 if (!producer_zone) { 941 /* Create a new one */ 942 DEBUG(DEBUG_XFRD, 1, (LOG_INFO,"creating catalog producer zone" 943 " '%s'", producer_zopt->name)); 944 producer_zone = (struct xfrd_catalog_producer_zone*) 945 region_alloc(xfrd->region, sizeof(*producer_zone)); 946 memset(producer_zone , 0, sizeof(*producer_zone)); 947 producer_zone->node.key = producer_zopt->node.key; 948 producer_zone->options = producer_zopt; 949 producer_zone->member_ids.region = xfrd->region; 950 producer_zone->member_ids.root = RBTREE_NULL; 951 producer_zone->member_ids.count = 0; 952 producer_zone->member_ids.cmp = member_id_compare; 953 producer_zone->serial = 0; 954 producer_zone->to_delete = NULL; 955 producer_zone->to_add = NULL; 956 producer_zone->latest_pxfr = NULL; 957 producer_zone->axfr = 1; 958 rbtree_insert(xfrd->catalog_producer_zones, 959 (rbnode_type*)producer_zone); 960 } 961 return producer_zone; 962} 963 964void 965xfrd_add_catalog_producer_member(struct catalog_member_zone* cmz) 966{ 967 struct xfrd_catalog_producer_zone* producer_zone; 968 const dname_type* producer_name; 969 struct xfrd_producer_member* to_add; 970 971 assert(xfrd); 972 if (!(producer_zone = xfrd_get_catalog_producer_zone(cmz))) { 973 return; 974 } 975 producer_name = producer_zone->node.key; 976 while(!cmz->member_id) { 977 /* Make new member_id with this catalog producer */ 978 char id_label[sizeof(uint32_t)*2+1]; 979 uint32_t new_id = (uint32_t)random_generate(0x7fffffff); 980 981 hex_ntop((void*)&new_id, sizeof(uint32_t), id_label, sizeof(id_label)); 982 id_label[sizeof(uint32_t)*2] = 0; 983 cmz->member_id = label_plus_dname(id_label, 984 label_plus_dname("zones", producer_name)); 985 DEBUG(DEBUG_XFRD, 1, (LOG_INFO, "does member_id %s exist?", 986 dname_to_string(cmz->member_id, NULL))); 987 if (!rbtree_search(&producer_zone->member_ids, cmz)) { 988 cmz->member_id = dname_copy(xfrd->nsd->options->region, 989 cmz->member_id); 990 break; 991 } 992 cmz->member_id = NULL; 993 } 994 cmz->node.key = cmz; 995 rbtree_insert(&producer_zone->member_ids, &cmz->node); 996 997 /* Put data to be added to the producer zone to the to_add stack */ 998 to_add = (struct xfrd_producer_member*)region_alloc(xfrd->region, 999 sizeof(struct xfrd_producer_member)); 1000 to_add->member_id = cmz->member_id; 1001 to_add->member_zone_name = (dname_type*)cmz->options.node.key; 1002 to_add->group_name = cmz->options.pattern->pname; 1003 to_add->next = producer_zone->to_add; 1004 producer_zone->to_add = to_add; 1005} 1006 1007int 1008xfrd_del_catalog_producer_member(struct xfrd_state* xfrd, 1009 const dname_type* member_zone_name) 1010{ 1011 struct xfrd_producer_member* to_delete; 1012 struct catalog_member_zone* cmz; 1013 struct xfrd_catalog_producer_zone* producer_zone; 1014 1015 if(!(cmz = as_catalog_member_zone(zone_options_find(xfrd->nsd->options, 1016 member_zone_name))) 1017 || !(producer_zone = xfrd_get_catalog_producer_zone(cmz)) 1018 || !rbtree_delete(&producer_zone->member_ids, cmz)) 1019 return 0; 1020 to_delete = (struct xfrd_producer_member*)region_alloc(xfrd->region, 1021 sizeof(struct xfrd_producer_member)); 1022 to_delete->member_id = cmz->member_id; cmz->member_id = NULL; 1023 cmz->node = *RBTREE_NULL; 1024 to_delete->member_zone_name = member_zone_name; 1025 to_delete->group_name = cmz->options.pattern->pname; 1026 to_delete->next = producer_zone->to_delete; 1027 producer_zone->to_delete = to_delete; 1028 return 1; 1029} 1030 1031static int 1032try_buffer_write_SOA(buffer_type* packet, const dname_type* owner, 1033 uint32_t serial) 1034{ 1035 size_t mark = buffer_position(packet); 1036 1037 if(try_buffer_write(packet, dname_name(owner), owner->name_size) 1038 && try_buffer_write_u16(packet, TYPE_SOA) 1039 && try_buffer_write_u16(packet, CLASS_IN) 1040 && try_buffer_write_u32(packet, 0) /* TTL*/ 1041 && try_buffer_write_u16(packet, 9 + 9 + 5 * sizeof(uint32_t)) 1042 && try_buffer_write(packet, "\007invalid\000", 9) /* primary */ 1043 && try_buffer_write(packet, "\007invalid\000", 9) /* mailbox */ 1044 && try_buffer_write_u32(packet, serial) /* serial */ 1045 && try_buffer_write_u32(packet, 3600) /* refresh*/ 1046 && try_buffer_write_u32(packet, 600) /* retry */ 1047 && try_buffer_write_u32(packet, 2147483646) /* expire */ 1048 && try_buffer_write_u32(packet, 0) /* minimum */) { 1049 ANCOUNT_SET(packet, ANCOUNT(packet) + 1); 1050 return 1; 1051 } 1052 buffer_set_position(packet, mark); 1053 return 0; 1054} 1055 1056static int 1057try_buffer_write_RR(buffer_type* packet, const dname_type* owner, 1058 uint16_t rr_type, uint16_t rdata_len, const void* rdata) 1059{ 1060 size_t mark = buffer_position(packet); 1061 1062 if(try_buffer_write(packet, dname_name(owner), owner->name_size) 1063 && try_buffer_write_u16(packet, rr_type) 1064 && try_buffer_write_u16(packet, CLASS_IN) 1065 && try_buffer_write_u32(packet, 0) /* TTL*/ 1066 && try_buffer_write_u16(packet, rdata_len) 1067 && try_buffer_write(packet, rdata, rdata_len)) { 1068 ANCOUNT_SET(packet, ANCOUNT(packet) + 1); 1069 return 1; 1070 } 1071 buffer_set_position(packet, mark); 1072 return 0; 1073} 1074 1075static inline int 1076try_buffer_write_PTR(buffer_type* packet, const dname_type* owner, 1077 const dname_type* name) 1078{ 1079 return try_buffer_write_RR(packet, owner, TYPE_PTR, 1080 name->name_size, dname_name(name)); 1081} 1082 1083static int 1084try_buffer_write_TXT(buffer_type* packet, const dname_type* name, 1085 const char *txt) 1086{ 1087 size_t mark = buffer_position(packet); 1088 size_t len = strlen(txt); 1089 1090 if(len > 255) { 1091 log_msg(LOG_ERR, "cannot make '%s 0 IN TXT \"%s\"': rdata " 1092 "field too long", dname_to_string(name, NULL), txt); 1093 return 1; 1094 } 1095 if(try_buffer_write(packet, dname_name(name), name->name_size) 1096 && try_buffer_write_u16(packet, TYPE_TXT) 1097 && try_buffer_write_u16(packet, CLASS_IN) 1098 && try_buffer_write_u32(packet, 0) /* TTL*/ 1099 && try_buffer_write_u16(packet, len + 1) 1100 && try_buffer_write_u8(packet, len) 1101 && try_buffer_write_string(packet, txt)) { 1102 ANCOUNT_SET(packet, ANCOUNT(packet) + 1); 1103 return 1; 1104 } 1105 buffer_set_position(packet, mark); 1106 return 0; 1107} 1108 1109static void 1110xfr_writer_init(struct xfrd_xfr_writer* xw, 1111 struct xfrd_catalog_producer_zone* producer_zone) 1112{ 1113 xw->producer_zone = producer_zone; 1114 buffer_create_from( &xw->packet, &xw->packet_space 1115 , sizeof(xw->packet_space)); 1116 buffer_write(&xw->packet, "\000\000\000\000\000\000" 1117 "\000\000\000\000\000\000", 12); /* header */ 1118 xw->seq_nr = 0; 1119 xw->old_serial = xw->producer_zone->serial; 1120 xw->new_serial = (uint32_t)xfrd_time(); 1121 if(xw->new_serial <= xw->old_serial) 1122 xw->new_serial = xw->old_serial + 1; 1123 if(producer_zone->axfr) { 1124 xw->old_serial = 0; 1125 producer_zone->axfr = 0; 1126 } 1127 xw->xfrfilenumber = xfrd->xfrfilenumber++; 1128} 1129 1130static void 1131xfr_writer_write_packet(struct xfrd_xfr_writer* xw) 1132{ 1133 const dname_type* producer_name = 1134 (const dname_type*)xw->producer_zone->options->node.key; 1135 1136 /* We want some content at least, so not just a header 1137 * This can occur when final SOA was already written. 1138 */ 1139 if(buffer_position(&xw->packet) == 12) 1140 return; 1141 buffer_flip(&xw->packet); 1142 diff_write_packet( dname_to_string(producer_name, NULL) 1143 , xw->producer_zone->options->pattern->pname 1144 , xw->old_serial, xw->new_serial, xw->seq_nr 1145 , buffer_begin(&xw->packet), buffer_limit(&xw->packet) 1146 , xfrd->nsd, xw->xfrfilenumber); 1147 xw->seq_nr += 1; 1148 buffer_clear(&xw->packet); 1149 buffer_write(&xw->packet, "\000\000\000\000\000\000" 1150 "\000\000\000\000\000\000", 12); /* header */ 1151} 1152 1153 1154static void 1155xfr_writer_commit(struct xfrd_xfr_writer* xw, const char *fmt, ...) 1156{ 1157 va_list args; 1158 char msg[1024]; 1159 const dname_type* producer_name = 1160 (const dname_type*)xw->producer_zone->options->node.key; 1161 1162 va_start(args, fmt); 1163 if (vsnprintf(msg, sizeof(msg), fmt, args) >= (int)sizeof(msg)) { 1164 log_msg(LOG_WARNING, "truncated diff commit message: '%s'", 1165 msg); 1166 } 1167 xfr_writer_write_packet(xw); /* Write remaining data */ 1168 diff_write_commit( dname_to_string(producer_name, NULL) 1169 , xw->old_serial, xw->new_serial 1170 , xw->seq_nr /* Number of packets */ 1171 , 1, msg, xfrd->nsd, xw->xfrfilenumber); 1172 task_new_apply_xfr( xfrd->nsd->task[xfrd->nsd->mytask], xfrd->last_task 1173 , producer_name 1174 , xw->old_serial, xw->new_serial, xw->xfrfilenumber); 1175 xfrd_set_reload_now(xfrd); 1176} 1177 1178static void 1179xfrd_process_catalog_producer_zone( 1180 struct xfrd_catalog_producer_zone* producer_zone) 1181{ 1182 struct xfrd_xfr_writer xw; 1183 dname_type* producer_name; 1184 struct xfrd_producer_xfr* pxfr; 1185 1186 if(!producer_zone->to_add && !producer_zone->to_delete) 1187 return; /* No changes */ 1188 1189 producer_name = (dname_type*)producer_zone->node.key; 1190 xfr_writer_init(&xw, producer_zone); 1191 xfr_writer_add_SOA(&xw, producer_name, xw.new_serial); 1192 1193 if(xw.old_serial == 0) { 1194 /* initial deployment */ 1195 assert(producer_zone->to_add && !producer_zone->to_delete); 1196 1197 xfr_writer_add_RR (&xw, producer_name 1198 , TYPE_NS, 9, "\007invalid\000"); 1199 xfr_writer_add_TXT(&xw, label_plus_dname("version" 1200 , producer_name), "2"); 1201 goto add_member_zones; 1202 } 1203 /* IXFR */ 1204 xfr_writer_add_SOA(&xw, producer_name, xw.old_serial); 1205 while(producer_zone->to_delete) { 1206 struct xfrd_producer_member* to_delete = 1207 producer_zone->to_delete; 1208 1209 /* Pop to_delete from stack */ 1210 producer_zone->to_delete = to_delete->next; 1211 to_delete->next = NULL; 1212 1213 /* Write <member_id> PTR <member_name> */ 1214 xfr_writer_add_PTR(&xw, to_delete->member_id 1215 , to_delete->member_zone_name); 1216 1217 /* Write group.<member_id> TXT <pattern> */ 1218 xfr_writer_add_TXT( &xw 1219 , label_plus_dname("group" 1220 , to_delete->member_id) 1221 , to_delete->group_name); 1222 1223 region_recycle( xfrd->nsd->options->region 1224 , (void *)to_delete->member_id 1225 , dname_total_size(to_delete->member_id)); 1226 region_recycle( xfrd->region /* allocated in perform_delzone */ 1227 , (void *)to_delete->member_zone_name 1228 , dname_total_size(to_delete->member_zone_name)); 1229 /* Don't recycle to_delete->group_name it's pattern->pname */ 1230 region_recycle( xfrd->region, to_delete, sizeof(*to_delete)); 1231 } 1232 xfr_writer_add_SOA(&xw, producer_name, xw.new_serial); 1233 1234add_member_zones: 1235 while(producer_zone->to_add) { 1236 struct xfrd_producer_member* to_add = producer_zone->to_add; 1237 1238 /* Pop to_add from stack */ 1239 producer_zone->to_add = to_add->next; 1240 to_add->next = NULL; 1241 1242 /* Write <member_id> PTR <member_name> */ 1243 xfr_writer_add_PTR(&xw, to_add->member_id, 1244 to_add->member_zone_name); 1245 1246 /* Write group.<member_id> TXT <pattern> */ 1247 xfr_writer_add_TXT( &xw 1248 , label_plus_dname("group" 1249 , to_add->member_id) 1250 , to_add->group_name); 1251 1252 /* Don't recycle any of the struct attributes as they come 1253 * from zone_option's that are in use 1254 */ 1255 region_recycle(xfrd->region, to_add, sizeof(*to_add)); 1256 } 1257 xfr_writer_add_SOA(&xw, producer_name, xw.new_serial); 1258 xfr_writer_commit(&xw, "xfr for catalog producer zone " 1259 "'%s' with %d members from %u to %u", 1260 dname_to_string(producer_name, NULL), 1261 producer_zone->member_ids.count, 1262 xw.old_serial, xw.new_serial); 1263 producer_zone->serial = xw.new_serial; 1264 1265 /* Hook up an xfrd_producer_xfr, to delete the xfr file when applied */ 1266 pxfr = (struct xfrd_producer_xfr*)region_alloc(xfrd->region, 1267 sizeof(struct xfrd_producer_xfr)); 1268 pxfr->serial = xw.new_serial; 1269 pxfr->xfrfilenumber = xw.xfrfilenumber; 1270 if((pxfr->next = producer_zone->latest_pxfr)) 1271 pxfr->next->prev_next_ptr = &pxfr->next; 1272 pxfr->prev_next_ptr = &producer_zone->latest_pxfr; 1273 producer_zone->latest_pxfr = pxfr; 1274} 1275 1276void xfrd_process_catalog_producer_zones() 1277{ 1278 struct xfrd_catalog_producer_zone* producer_zone; 1279 1280 RBTREE_FOR(producer_zone, struct xfrd_catalog_producer_zone*, 1281 xfrd->catalog_producer_zones) { 1282 xfrd_process_catalog_producer_zone(producer_zone); 1283 } 1284} 1285 1286