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