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