Home | History | Annotate | Line # | Download | only in dist
      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