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