1/*
2 * ixfrcreate.c -- generating IXFR differences from zone files.
3 *
4 * Copyright (c) 2021, NLnet Labs. All rights reserved.
5 *
6 * See LICENSE for the license.
7 *
8 */
9
10#include "config.h"
11#include <stdio.h>
12#include <errno.h>
13#include <unistd.h>
14#include "ixfrcreate.h"
15#include "namedb.h"
16#include "ixfr.h"
17#include "options.h"
18
19/* spool a uint16_t to file */
20static int spool_u16(FILE* out, uint16_t val)
21{
22	if(!fwrite(&val, sizeof(val), 1, out)) {
23		return 0;
24	}
25	return 1;
26}
27
28/* spool a uint32_t to file */
29static int spool_u32(FILE* out, uint32_t val)
30{
31	if(!fwrite(&val, sizeof(val), 1, out)) {
32		return 0;
33	}
34	return 1;
35}
36
37/* spool dname to file */
38static int spool_dname(FILE* out, dname_type* dname)
39{
40	uint16_t namelen = dname->name_size;
41	if(!fwrite(&namelen, sizeof(namelen), 1, out)) {
42		return 0;
43	}
44	if(!fwrite(dname_name(dname), namelen, 1, out)) {
45		return 0;
46	}
47	return 1;
48}
49
50/* calculate the rdatalen of an RR */
51static size_t rr_rdatalen_uncompressed(rr_type* rr)
52{
53	int i;
54	size_t rdlen_uncompressed = 0;
55	for(i=0; i<rr->rdata_count; i++) {
56		if(rdata_atom_is_domain(rr->type, i)) {
57			rdlen_uncompressed += domain_dname(rr->rdatas[i].domain)
58				->name_size;
59		} else {
60			rdlen_uncompressed += rr->rdatas[i].data[0];
61		}
62	}
63	return rdlen_uncompressed;
64}
65
66/* spool the data for one rr into the file */
67static int spool_rr_data(FILE* out, rr_type* rr)
68{
69	int i;
70	uint16_t rdlen;
71	if(!spool_u32(out, rr->ttl))
72		return 0;
73	rdlen = rr_rdatalen_uncompressed(rr);
74	if(!spool_u16(out, rdlen))
75		return 0;
76	for(i=0; i<rr->rdata_count; i++) {
77		if(rdata_atom_is_domain(rr->type, i)) {
78			if(!fwrite(dname_name(domain_dname(
79				rr->rdatas[i].domain)), domain_dname(
80				rr->rdatas[i].domain)->name_size, 1, out))
81				return 0;
82		} else {
83			if(!fwrite(&rr->rdatas[i].data[1],
84				rr->rdatas[i].data[0], 1, out))
85				return 0;
86		}
87	}
88	return 1;
89}
90
91/* spool one rrset to file */
92static int spool_rrset(FILE* out, rrset_type* rrset)
93{
94	int i;
95	if(rrset->rr_count == 0)
96		return 1;
97	if(!spool_u16(out, rrset->rrs[0].type))
98		return 0;
99	if(!spool_u16(out, rrset->rrs[0].klass))
100		return 0;
101	if(!spool_u16(out, rrset->rr_count))
102		return 0;
103	for(i=0; i<rrset->rr_count; i++) {
104		if(!spool_rr_data(out, &rrset->rrs[i]))
105			return 0;
106	}
107	return 1;
108}
109
110/* spool rrsets to file */
111static int spool_rrsets(FILE* out, rrset_type* rrsets, struct zone* zone)
112{
113	rrset_type* s;
114	for(s=rrsets; s; s=s->next) {
115		if(s->zone != zone)
116			continue;
117		if(!spool_rrset(out, s)) {
118			return 0;
119		}
120	}
121	return 1;
122}
123
124/* count number of rrsets for a domain */
125static size_t domain_count_rrsets(domain_type* domain, zone_type* zone)
126{
127	rrset_type* s;
128	size_t count = 0;
129	for(s=domain->rrsets; s; s=s->next) {
130		if(s->zone == zone)
131			count++;
132	}
133	return count;
134}
135
136/* spool the domain names to file, each one in turn. end with enddelimiter */
137static int spool_domains(FILE* out, struct zone* zone)
138{
139	domain_type* domain;
140	for(domain = zone->apex; domain && domain_is_subdomain(domain,
141		zone->apex); domain = domain_next(domain)) {
142		uint32_t count = domain_count_rrsets(domain, zone);
143		if(count == 0)
144			continue;
145		/* write the name */
146		if(!spool_dname(out, domain_dname(domain)))
147			return 0;
148		if(!spool_u32(out, count))
149			return 0;
150		/* write the rrsets */
151		if(!spool_rrsets(out, domain->rrsets, zone))
152			return 0;
153	}
154	/* the end delimiter is a 0 length. domain names are not zero length */
155	if(!spool_u16(out, 0))
156		return 0;
157	return 1;
158}
159
160/* spool the namedb zone to the file. print error on failure. */
161static int spool_zone_to_file(struct zone* zone, char* file_name,
162	uint32_t serial)
163{
164	FILE* out;
165	out = fopen(file_name, "w");
166	if(!out) {
167		log_msg(LOG_ERR, "could not open %s for writing: %s",
168			file_name, strerror(errno));
169		return 0;
170	}
171	if(!spool_dname(out, domain_dname(zone->apex))) {
172		log_msg(LOG_ERR, "could not write %s: %s",
173			file_name, strerror(errno));
174		fclose(out);
175		return 0;
176	}
177	if(!spool_u32(out, serial)) {
178		log_msg(LOG_ERR, "could not write %s: %s",
179			file_name, strerror(errno));
180		fclose(out);
181		return 0;
182	}
183	if(!spool_domains(out, zone)) {
184		log_msg(LOG_ERR, "could not write %s: %s",
185			file_name, strerror(errno));
186		fclose(out);
187		return 0;
188	}
189	fclose(out);
190	return 1;
191}
192
193/* create ixfr spool file name */
194static int create_ixfr_spool_name(struct ixfr_create* ixfrcr,
195	const char* zfile)
196{
197	char buf[1024];
198	snprintf(buf, sizeof(buf), "%s.spoolzone.%u", zfile,
199		(unsigned)getpid());
200	ixfrcr->file_name = strdup(buf);
201	if(!ixfrcr->file_name)
202		return 0;
203	return 1;
204}
205
206/* start ixfr creation */
207struct ixfr_create* ixfr_create_start(struct zone* zone, const char* zfile,
208	uint64_t ixfr_size, int errorcmdline)
209{
210	struct ixfr_create* ixfrcr = (struct ixfr_create*)calloc(1,
211		sizeof(*ixfrcr));
212	if(!ixfrcr) {
213		log_msg(LOG_ERR, "malloc failure");
214		return NULL;
215	}
216	ixfrcr->zone_name_len = domain_dname(zone->apex)->name_size;
217	ixfrcr->zone_name = (uint8_t*)malloc(ixfrcr->zone_name_len);
218	if(!ixfrcr->zone_name) {
219		free(ixfrcr);
220		log_msg(LOG_ERR, "malloc failure");
221		return NULL;
222	}
223	memmove(ixfrcr->zone_name, dname_name(domain_dname(zone->apex)),
224		ixfrcr->zone_name_len);
225
226	if(!create_ixfr_spool_name(ixfrcr, zfile)) {
227		ixfr_create_free(ixfrcr);
228		log_msg(LOG_ERR, "malloc failure");
229		return NULL;
230	}
231	ixfrcr->old_serial = zone_get_current_serial(zone);
232	if(!spool_zone_to_file(zone, ixfrcr->file_name, ixfrcr->old_serial)) {
233		ixfr_create_free(ixfrcr);
234		return NULL;
235	}
236	if(zone->opts && zone->opts->pattern)
237		ixfrcr->max_size = (size_t)zone->opts->pattern->ixfr_size;
238	else	ixfrcr->max_size = (size_t)ixfr_size;
239	ixfrcr->errorcmdline = errorcmdline;
240	return ixfrcr;
241}
242
243/* free ixfr create */
244void ixfr_create_free(struct ixfr_create* ixfrcr)
245{
246	if(!ixfrcr)
247		return;
248	free(ixfrcr->file_name);
249	free(ixfrcr->zone_name);
250	free(ixfrcr);
251}
252
253/* read uint16_t from spool */
254static int read_spool_u16(FILE* spool, uint16_t* val)
255{
256	if(fread(val, sizeof(*val), 1, spool) < 1)
257		return 0;
258	return 1;
259}
260
261/* read uint32_t from spool */
262static int read_spool_u32(FILE* spool, uint32_t* val)
263{
264	if(fread(val, sizeof(*val), 1, spool) < 1)
265		return 0;
266	return 1;
267}
268
269/* read dname from spool */
270static int read_spool_dname(FILE* spool, uint8_t* buf, size_t buflen,
271	size_t* dname_len)
272{
273	uint16_t len;
274	if(fread(&len, sizeof(len), 1, spool) < 1)
275		return 0;
276	if(len > buflen) {
277		log_msg(LOG_ERR, "dname too long");
278		return 0;
279	}
280	if(len > 0) {
281		if(fread(buf, len, 1, spool) < 1)
282			return 0;
283	}
284	*dname_len = len;
285	return 1;
286}
287
288/* read and check the spool file header */
289static int read_spool_header(FILE* spool, struct ixfr_create* ixfrcr)
290{
291	uint8_t dname[MAXDOMAINLEN+1];
292	size_t dname_len;
293	uint32_t serial;
294	/* read apex */
295	if(!read_spool_dname(spool, dname, sizeof(dname), &dname_len)) {
296		log_msg(LOG_ERR, "error reading file %s: %s",
297			ixfrcr->file_name, strerror(errno));
298		return 0;
299	}
300	/* read serial */
301	if(!read_spool_u32(spool, &serial)) {
302		log_msg(LOG_ERR, "error reading file %s: %s",
303			ixfrcr->file_name, strerror(errno));
304		return 0;
305	}
306
307	/* check */
308	if(ixfrcr->zone_name_len != dname_len ||
309		memcmp(ixfrcr->zone_name, dname, ixfrcr->zone_name_len) != 0) {
310		log_msg(LOG_ERR, "error file %s does not contain the correct zone apex",
311			ixfrcr->file_name);
312		return 0;
313	}
314	if(ixfrcr->old_serial != serial) {
315		log_msg(LOG_ERR, "error file %s does not contain the correct zone serial",
316			ixfrcr->file_name);
317		return 0;
318	}
319	return 1;
320}
321
322/* store the old soa record when we encounter it on the spool */
323static int process_store_oldsoa(struct ixfr_store* store, uint8_t* dname,
324	size_t dname_len, uint16_t tp, uint16_t kl, uint32_t ttl, uint8_t* buf,
325	uint16_t rdlen)
326{
327	if(store->data->oldsoa) {
328		log_msg(LOG_ERR, "error spool contains multiple SOA records");
329		return 0;
330	}
331	if(!ixfr_store_oldsoa_uncompressed(store, dname, dname_len, tp, kl,
332		ttl, buf, rdlen)) {
333		log_msg(LOG_ERR, "out of memory");
334		return 0;
335	}
336	return 1;
337}
338
339/* see if rdata matches, true if equal */
340static int rdata_match(struct rr* rr, uint8_t* rdata, uint16_t rdlen)
341{
342	size_t rdpos = 0;
343	int i;
344	for(i=0; i<rr->rdata_count; i++) {
345		if(rdata_atom_is_domain(rr->type, i)) {
346			if(rdpos + domain_dname(rr->rdatas[i].domain)->name_size
347				> rdlen)
348				return 0;
349			if(memcmp(rdata+rdpos,
350				dname_name(domain_dname(rr->rdatas[i].domain)),
351				domain_dname(rr->rdatas[i].domain)->name_size)
352				!= 0)
353				return 0;
354			rdpos += domain_dname(rr->rdatas[i].domain)->name_size;
355		} else {
356			if(rdpos + rr->rdatas[i].data[0] > rdlen)
357				return 0;
358			if(memcmp(rdata+rdpos, &rr->rdatas[i].data[1],
359				rr->rdatas[i].data[0]) != 0)
360				return 0;
361			rdpos += rr->rdatas[i].data[0];
362		}
363	}
364	if(rdpos != rdlen)
365		return 0;
366	return 1;
367}
368
369/* find an rdata in an rrset, true if found and sets index found */
370static int rrset_find_rdata(struct rrset* rrset, uint32_t ttl, uint8_t* rdata,
371	uint16_t rdlen, uint16_t* index)
372{
373	int i;
374	for(i=0; i<rrset->rr_count; i++) {
375		if(rrset->rrs[i].ttl != ttl)
376			continue;
377		if(rdata_match(&rrset->rrs[i], rdata, rdlen)) {
378			*index = i;
379			return 1;
380		}
381	}
382	return 0;
383}
384
385/* sort comparison for uint16 elements */
386static int sort_uint16(const void* x, const void* y)
387{
388	const uint16_t* ax = (const uint16_t*)x;
389	const uint16_t* ay = (const uint16_t*)y;
390	if(*ax < *ay)
391		return -1;
392	if(*ax > *ay)
393		return 1;
394	return 0;
395}
396
397/* spool read an rrset, it is a deleted RRset */
398static int process_diff_rrset(FILE* spool, struct ixfr_create* ixfrcr,
399	struct ixfr_store* store, struct domain* domain,
400	uint16_t tp, uint16_t kl, uint16_t rrcount, struct rrset* rrset)
401{
402	/* read RRs from file and see if they are added, deleted or in both */
403	uint8_t buf[MAX_RDLENGTH];
404	uint16_t marked[65536];
405	size_t marked_num = 0, atmarked;
406	int i;
407	for(i=0; i<rrcount; i++) {
408		uint16_t rdlen, index;
409		uint32_t ttl;
410		if(!read_spool_u32(spool, &ttl) ||
411		   !read_spool_u16(spool, &rdlen)) {
412			log_msg(LOG_ERR, "error reading file %s: %s",
413				ixfrcr->file_name, strerror(errno));
414			return 0;
415		}
416		/* because rdlen is uint16_t always smaller than sizeof(buf)*/
417#pragma GCC diagnostic push
418#pragma GCC diagnostic ignored "-Wtype-limits"
419		assert(rdlen <= sizeof(buf));
420#pragma GCC diagnostic pop
421		if(fread(buf, rdlen, 1, spool) < 1) {
422			log_msg(LOG_ERR, "error reading file %s: %s",
423				ixfrcr->file_name, strerror(errno));
424			return 0;
425		}
426		if(tp == TYPE_SOA) {
427			if(!process_store_oldsoa(store,
428				(void*)dname_name(domain_dname(domain)),
429				domain_dname(domain)->name_size, tp, kl, ttl,
430				buf, rdlen))
431				return 0;
432		}
433		/* see if the rr is in the RRset */
434		if(rrset_find_rdata(rrset, ttl, buf, rdlen, &index)) {
435			/* it is in both, mark it */
436			marked[marked_num++] = index;
437		} else {
438			/* not in new rrset, but only on spool, it is
439			 * a deleted RR */
440			if(!ixfr_store_delrr_uncompressed(store,
441				(void*)dname_name(domain_dname(domain)),
442				domain_dname(domain)->name_size,
443				tp, kl, ttl, buf, rdlen)) {
444				log_msg(LOG_ERR, "out of memory");
445				return 0;
446			}
447		}
448	}
449	/* now that we are done, see if RRs in the rrset are not marked,
450	 * and thus are new rrs that are added */
451	qsort(marked, marked_num, sizeof(marked[0]), &sort_uint16);
452	atmarked = 0;
453	for(i=0; i<rrset->rr_count; i++) {
454		if(atmarked < marked_num && marked[atmarked] == i) {
455			/* the item is in the marked list, skip it */
456			atmarked++;
457			continue;
458		}
459		/* not in the marked list, the RR is added */
460		if(!ixfr_store_addrr_rdatas(store, domain_dname(domain),
461			rrset->rrs[i].type, rrset->rrs[i].klass,
462			rrset->rrs[i].ttl, rrset->rrs[i].rdatas,
463			rrset->rrs[i].rdata_count)) {
464			log_msg(LOG_ERR, "out of memory");
465			return 0;
466		}
467	}
468	return 1;
469}
470
471/* spool read an rrset, it is a deleted RRset */
472static int process_spool_delrrset(FILE* spool, struct ixfr_create* ixfrcr,
473	struct ixfr_store* store, uint8_t* dname, size_t dname_len,
474	uint16_t tp, uint16_t kl, uint16_t rrcount)
475{
476	/* read the RRs from file and add to del list. */
477	uint8_t buf[MAX_RDLENGTH];
478	int i;
479	for(i=0; i<rrcount; i++) {
480		uint16_t rdlen;
481		uint32_t ttl;
482		if(!read_spool_u32(spool, &ttl) ||
483		   !read_spool_u16(spool, &rdlen)) {
484			log_msg(LOG_ERR, "error reading file %s: %s",
485				ixfrcr->file_name, strerror(errno));
486			return 0;
487		}
488		/* because rdlen is uint16_t always smaller than sizeof(buf)*/
489#pragma GCC diagnostic push
490#pragma GCC diagnostic ignored "-Wtype-limits"
491		assert(rdlen <= sizeof(buf));
492#pragma GCC diagnostic pop
493		if(fread(buf, rdlen, 1, spool) < 1) {
494			log_msg(LOG_ERR, "error reading file %s: %s",
495				ixfrcr->file_name, strerror(errno));
496			return 0;
497		}
498		if(tp == TYPE_SOA) {
499			if(!process_store_oldsoa(store, dname, dname_len,
500				tp, kl, ttl, buf, rdlen))
501				return 0;
502		}
503		if(!ixfr_store_delrr_uncompressed(store, dname, dname_len, tp,
504			kl, ttl, buf, rdlen)) {
505			log_msg(LOG_ERR, "out of memory");
506			return 0;
507		}
508	}
509	return 1;
510}
511
512/* add the rrset to the added list */
513static int process_add_rrset(struct ixfr_store* ixfr_store,
514	struct domain* domain, struct rrset* rrset)
515{
516	int i;
517	for(i=0; i<rrset->rr_count; i++) {
518		if(!ixfr_store_addrr_rdatas(ixfr_store, domain_dname(domain),
519			rrset->rrs[i].type, rrset->rrs[i].klass,
520			rrset->rrs[i].ttl, rrset->rrs[i].rdatas,
521			rrset->rrs[i].rdata_count)) {
522			log_msg(LOG_ERR, "out of memory");
523			return 0;
524		}
525	}
526	return 1;
527}
528
529/* add the RR types that are not in the marktypes list from the new zone */
530static int process_marktypes(struct ixfr_store* store, struct zone* zone,
531	struct domain* domain, uint16_t* marktypes, size_t marktypes_used)
532{
533	/* walk through the rrsets in the zone, if it is not in the
534	 * marktypes list, then it is new and an added RRset */
535	rrset_type* s;
536	qsort(marktypes, marktypes_used, sizeof(marktypes[0]), &sort_uint16);
537	for(s=domain->rrsets; s; s=s->next) {
538		uint16_t tp;
539		if(s->zone != zone)
540			continue;
541		tp = rrset_rrtype(s);
542		if(bsearch(&tp, marktypes, marktypes_used, sizeof(marktypes[0]), &sort_uint16)) {
543			/* the item is in the marked list, skip it */
544			continue;
545		}
546		if(!process_add_rrset(store, domain, s))
547			return 0;
548	}
549	return 1;
550}
551
552/* check the difference between the domain and RRs from spool */
553static int process_diff_domain(FILE* spool, struct ixfr_create* ixfrcr,
554	struct ixfr_store* store, struct zone* zone, struct domain* domain)
555{
556	/* Read the RR types from spool. Mark off the ones seen,
557	 * later, the notseen ones from the new zone are added RRsets.
558	 * For the ones not in the new zone, they are deleted RRsets.
559	 * If they exist in old and new, check for RR differences. */
560	uint32_t spool_type_count, i;
561	uint16_t marktypes[65536];
562	size_t marktypes_used = 0;
563	if(!read_spool_u32(spool, &spool_type_count)) {
564		log_msg(LOG_ERR, "error reading file %s: %s",
565			ixfrcr->file_name, strerror(errno));
566		return 0;
567	}
568	if(spool_type_count > sizeof(marktypes)) {
569		log_msg(LOG_ERR, "error reading file %s: spool type count "
570			"too large", ixfrcr->file_name);
571		return 0;
572	}
573	for(i=0; i<spool_type_count; i++) {
574		uint16_t tp, kl, rrcount;
575		struct rrset* rrset;
576		if(!read_spool_u16(spool, &tp) ||
577		   !read_spool_u16(spool, &kl) ||
578		   !read_spool_u16(spool, &rrcount)) {
579			log_msg(LOG_ERR, "error reading file %s: %s",
580				ixfrcr->file_name, strerror(errno));
581			return 0;
582		}
583		/* The rrcount is within limits of sizeof(marktypes), because
584		 * the uint16_t < 65536 */
585		rrset = domain_find_rrset(domain, zone, tp);
586		if(!rrset) {
587			/* rrset in spool but not in new zone, deleted RRset */
588			if(!process_spool_delrrset(spool, ixfrcr, store,
589				(void*)dname_name(domain_dname(domain)),
590				domain_dname(domain)->name_size, tp, kl,
591				rrcount))
592				return 0;
593		} else {
594			/* add to the marked types, this one is present in
595			 * spool */
596			marktypes[marktypes_used++] = tp;
597			/* rrset in old and in new zone, diff the RRset */
598			if(!process_diff_rrset(spool, ixfrcr, store, domain,
599				tp, kl, rrcount, rrset))
600				return 0;
601		}
602	}
603	/* process markoff to see if new zone has RRsets not in spool,
604	 * those are added RRsets. */
605	if(!process_marktypes(store, zone, domain, marktypes, marktypes_used))
606		return 0;
607	return 1;
608}
609
610/* add the RRs for the domain in new zone */
611static int process_domain_add_RRs(struct ixfr_store* store, struct zone* zone,
612	struct domain* domain)
613{
614	rrset_type* s;
615	for(s=domain->rrsets; s; s=s->next) {
616		if(s->zone != zone)
617			continue;
618		if(!process_add_rrset(store, domain, s))
619			return 0;
620	}
621	return 1;
622}
623
624/* del the RRs for the domain from the spool */
625static int process_domain_del_RRs(struct ixfr_create* ixfrcr,
626	struct ixfr_store* store, FILE* spool, uint8_t* dname,
627	size_t dname_len)
628{
629	uint32_t spool_type_count, i;
630	if(!read_spool_u32(spool, &spool_type_count)) {
631		log_msg(LOG_ERR, "error reading file %s: %s",
632			ixfrcr->file_name, strerror(errno));
633		return 0;
634	}
635	if(spool_type_count > 65536) {
636		log_msg(LOG_ERR, "error reading file %s: del RR spool type "
637			"count too large", ixfrcr->file_name);
638		return 0;
639	}
640	for(i=0; i<spool_type_count; i++) {
641		uint16_t tp, kl, rrcount;
642		if(!read_spool_u16(spool, &tp) ||
643		   !read_spool_u16(spool, &kl) ||
644		   !read_spool_u16(spool, &rrcount)) {
645			log_msg(LOG_ERR, "error reading file %s: %s",
646				ixfrcr->file_name, strerror(errno));
647			return 0;
648		}
649		/* The rrcount is within reasonable limits, because
650		 * the uint16_t < 65536 */
651		if(!process_spool_delrrset(spool, ixfrcr, store, dname,
652			dname_len, tp, kl, rrcount))
653			return 0;
654	}
655	return 1;
656}
657
658/* init the spool dname iterator */
659static void spool_dname_iter_init(struct spool_dname_iterator* iter,
660	FILE* spool, char* file_name)
661{
662	memset(iter, 0, sizeof(*iter));
663	iter->spool = spool;
664	iter->file_name = file_name;
665}
666
667/* read the dname element into the buffer for the spool dname iterator */
668static int spool_dname_iter_read(struct spool_dname_iterator* iter)
669{
670	if(!read_spool_dname(iter->spool, iter->dname, sizeof(iter->dname),
671		&iter->dname_len)) {
672		log_msg(LOG_ERR, "error reading file %s: %s",
673			iter->file_name, strerror(errno));
674		return 0;
675	}
676	return 1;
677}
678
679/* get the next name to operate on, that is not processed yet, 0 on failure
680 * returns okay on endoffile, check with eof for that.
681 * when done with an element, set iter->is_processed on the element. */
682static int spool_dname_iter_next(struct spool_dname_iterator* iter)
683{
684	if(iter->eof)
685		return 1;
686	if(!iter->read_first) {
687		/* read the first one */
688		if(!spool_dname_iter_read(iter))
689			return 0;
690		if(iter->dname_len == 0)
691			iter->eof = 1;
692		iter->read_first = 1;
693		iter->is_processed = 0;
694	}
695	if(!iter->is_processed) {
696		/* the current one needs processing */
697		return 1;
698	}
699	/* read the next one */
700	if(!spool_dname_iter_read(iter))
701		return 0;
702	if(iter->dname_len == 0)
703		iter->eof = 1;
704	iter->is_processed = 0;
705	return 1;
706}
707
708/* check if the ixfr is too large */
709static int ixfr_create_too_large(struct ixfr_create* ixfrcr,
710	struct ixfr_store* store)
711{
712	if(store->cancelled)
713		return 1;
714	if(ixfrcr->max_size != 0 &&
715		ixfr_data_size(store->data) > ixfrcr->max_size) {
716		if(ixfrcr->errorcmdline) {
717			log_msg(LOG_ERR, "the ixfr for %s exceeds size %u, it is not created",
718				wiredname2str(ixfrcr->zone_name),
719				(unsigned)ixfrcr->max_size);
720		} else {
721			VERBOSITY(2, (LOG_INFO, "the ixfr for %s exceeds size %u, it is not created",
722				wiredname2str(ixfrcr->zone_name),
723				(unsigned)ixfrcr->max_size));
724		}
725		ixfr_store_cancel(store);
726		return 1;
727	}
728	return 0;
729}
730
731/* process the spool input before the domain */
732static int process_spool_before_domain(FILE* spool, struct ixfr_create* ixfrcr,
733	struct ixfr_store* store, struct domain* domain,
734	struct spool_dname_iterator* iter, struct region* tmp_region)
735{
736	const dname_type* dname;
737	if(ixfr_create_too_large(ixfrcr, store))
738		return 0;
739	/* read the domains and rrsets before the domain and those are from
740	 * the old zone. If the domain is equal, return to have that processed
741	 * if we bypass, that means the domain does not exist, do that */
742	while(!iter->eof) {
743		if(!spool_dname_iter_next(iter))
744			return 0;
745		if(iter->eof)
746			break;
747		/* see if we are at, before or after the domain */
748		dname = dname_make(tmp_region, iter->dname, 1);
749		if(!dname) {
750			log_msg(LOG_ERR, "error in dname in %s",
751				iter->file_name);
752			return 0;
753		}
754		if(dname_compare(dname, domain_dname(domain)) < 0) {
755			/* the dname is smaller than the one from the zone.
756			 * it must be deleted, process it */
757			if(!process_domain_del_RRs(ixfrcr, store, spool,
758				iter->dname, iter->dname_len))
759				return 0;
760			iter->is_processed = 1;
761		} else {
762			/* we are at or after the domain we are looking for,
763			 * done here */
764			return 1;
765		}
766		if(ixfr_create_too_large(ixfrcr, store))
767			return 0;
768	}
769	/* no more domains on spool, done here */
770	return 1;
771}
772
773/* process the spool input for the domain */
774static int process_spool_for_domain(FILE* spool, struct ixfr_create* ixfrcr,
775	struct ixfr_store* store, struct zone* zone, struct domain* domain,
776	struct spool_dname_iterator* iter, struct region* tmp_region)
777{
778	/* process all the spool that is not the domain, that is before the
779	 * domain in the new zone */
780	if(!process_spool_before_domain(spool, ixfrcr, store, domain, iter,
781		tmp_region))
782		return 0;
783
784	if(ixfr_create_too_large(ixfrcr, store))
785		return 0;
786	/* are we at the correct domain now? */
787	if(iter->eof || iter->dname_len != domain_dname(domain)->name_size ||
788		memcmp(iter->dname, dname_name(domain_dname(domain)),
789			iter->dname_len) != 0) {
790		/* the domain from the new zone is not present in the old zone,
791		 * the content is in the added RRs set */
792		if(!process_domain_add_RRs(store, zone, domain))
793			return 0;
794		return 1;
795	}
796
797	/* process the domain */
798	/* the domain exists both in the old and new zone,
799	 * check for RR differences */
800	if(!process_diff_domain(spool, ixfrcr, store, zone, domain))
801		return 0;
802	iter->is_processed = 1;
803
804	return 1;
805}
806
807/* process remaining spool items */
808static int process_spool_remaining(FILE* spool, struct ixfr_create* ixfrcr,
809	struct ixfr_store* store, struct spool_dname_iterator* iter)
810{
811	/* the remaining domain names in the spool file, that is after
812	 * the last domain in the new zone. */
813	if(ixfr_create_too_large(ixfrcr, store))
814		return 0;
815	while(!iter->eof) {
816		if(!spool_dname_iter_next(iter))
817			return 0;
818		if(iter->eof)
819			break;
820		/* the domain only exists in the spool, the old zone,
821		 * and not in the new zone. That would be domains
822		 * after the new zone domains, or there are no new
823		 * zone domains */
824		if(!process_domain_del_RRs(ixfrcr, store, spool, iter->dname,
825			iter->dname_len))
826			return 0;
827		iter->is_processed = 1;
828		if(ixfr_create_too_large(ixfrcr, store))
829			return 0;
830	}
831	return 1;
832}
833
834/* walk through the zone and find the differences */
835static int ixfr_create_walk_zone(FILE* spool, struct ixfr_create* ixfrcr,
836	struct ixfr_store* store, struct zone* zone)
837{
838	struct domain* domain;
839	struct spool_dname_iterator iter;
840	struct region* tmp_region;
841	spool_dname_iter_init(&iter, spool, ixfrcr->file_name);
842	tmp_region = region_create(xalloc, free);
843	for(domain = zone->apex; domain && domain_is_subdomain(domain,
844		zone->apex); domain = domain_next(domain)) {
845		uint32_t count = domain_count_rrsets(domain, zone);
846		if(count == 0)
847			continue;
848
849		/* the domain is a domain in the new zone */
850		if(!process_spool_for_domain(spool, ixfrcr, store, zone,
851			domain, &iter, tmp_region)) {
852			region_destroy(tmp_region);
853			return 0;
854		}
855		region_free_all(tmp_region);
856		if(ixfr_create_too_large(ixfrcr, store))
857			return 0;
858	}
859	if(!process_spool_remaining(spool, ixfrcr, store, &iter)) {
860		region_destroy(tmp_region);
861		return 0;
862	}
863	region_destroy(tmp_region);
864	return 1;
865}
866
867/* see if the ixfr has already been created by reading the file header
868 * of the to-be-created file, if that file already exists */
869static int ixfr_create_already_done_serial(struct zone* zone,
870	const char* zfile, int checknew, uint32_t old_serial,
871	uint32_t new_serial)
872{
873	uint32_t file_oldserial = 0, file_newserial = 0;
874	size_t data_size = 0;
875	if(!ixfr_read_file_header(zone->opts->name, zfile, 1, &file_oldserial,
876		&file_newserial, &data_size, 0)) {
877		/* could not read, so it was not done */
878		return 0;
879	}
880	if(file_oldserial == old_serial &&
881		(!checknew || file_newserial == new_serial)) {
882		log_msg(LOG_INFO, "IXFR already exists in file %s.ixfr, nothing to do",
883			zfile);
884		return 1;
885	}
886	return 0;
887}
888
889/* See the data size of the ixfr by reading the file header of the ixfr file */
890static int ixfr_read_header_data_size(const char* zname,
891	const char* zfile, int file_num, size_t* data_size)
892{
893	uint32_t file_oldserial = 0, file_newserial = 0;
894	if(!ixfr_read_file_header(zname, zfile, file_num, &file_oldserial,
895		&file_newserial, data_size, 0)) {
896		/* could not read */
897		return 0;
898	}
899	return 1;
900}
901
902/* see if the ixfr has already been created by reading the file header
903 * of the to-be-created file, if that file already exists */
904static int ixfr_create_already_done(struct ixfr_create* ixfrcr,
905	struct zone* zone, const char* zfile, int checknew)
906{
907	return ixfr_create_already_done_serial(zone, zfile, checknew,
908		ixfrcr->old_serial, ixfrcr->new_serial);
909}
910
911/* store the new soa record for the ixfr */
912static int ixfr_create_store_newsoa(struct ixfr_store* store,
913	struct zone* zone)
914{
915	if(!zone || !zone->soa_rrset) {
916		log_msg(LOG_ERR, "error no SOA rrset");
917		return 0;
918	}
919	if(zone->soa_rrset->rr_count == 0) {
920		log_msg(LOG_ERR, "error empty SOA rrset");
921		return 0;
922	}
923	if(!ixfr_store_add_newsoa_rdatas(store, domain_dname(zone->apex),
924		zone->soa_rrset->rrs[0].type, zone->soa_rrset->rrs[0].klass,
925		zone->soa_rrset->rrs[0].ttl, zone->soa_rrset->rrs[0].rdatas,
926		zone->soa_rrset->rrs[0].rdata_count)) {
927		log_msg(LOG_ERR, "out of memory");
928		return 0;
929	}
930	return 1;
931}
932
933/* initialise ixfr_create perform, open spool, read header, get serial */
934static int ixfr_perform_init(struct ixfr_create* ixfrcr, struct zone* zone,
935	struct ixfr_store* store_mem, struct ixfr_store** store, FILE** spool)
936{
937	*spool = fopen(ixfrcr->file_name, "r");
938	if(!*spool) {
939		log_msg(LOG_ERR, "could not open %s for reading: %s",
940			ixfrcr->file_name, strerror(errno));
941		return 0;
942	}
943	if(!read_spool_header(*spool, ixfrcr)) {
944		fclose(*spool);
945		return 0;
946	}
947	ixfrcr->new_serial = zone_get_current_serial(zone);
948	*store = ixfr_store_start(zone, store_mem);
949	if(!ixfr_create_store_newsoa(*store, zone)) {
950		fclose(*spool);
951		ixfr_store_free(*store);
952		return 0;
953	}
954	return 1;
955}
956
957/* rename the other ixfr files */
958static int ixfr_create_rename_and_delete_files(const char* zname,
959	const char* zoptsname, const char* zfile, uint32_t ixfr_number,
960	size_t ixfr_size, size_t cur_data_size)
961{
962	size_t size_in_use = cur_data_size;
963	int dest_nr_files = (int)ixfr_number, maxsizehit = 0;
964	int num = 1;
965	while(ixfr_file_exists(zfile, num)) {
966		size_t fsize = 0;
967		if(!maxsizehit) {
968			if(!ixfr_read_header_data_size(zoptsname, zfile, num,
969				&fsize) || size_in_use + fsize > ixfr_size) {
970				/* no more than this because of storage size */
971				dest_nr_files = num;
972				maxsizehit = 1;
973			}
974			size_in_use += fsize;
975		}
976		num++;
977	}
978	num--;
979	/* num is now the number of ixfr files that exist */
980	while(num > 0) {
981		if(num+1 > dest_nr_files) {
982			(void)ixfr_unlink_it(zname, zfile, num, 0);
983		} else {
984			if(!ixfr_rename_it(zname, zfile, num, 0, num+1, 0))
985				return 0;
986		}
987		num--;
988	}
989	return 1;
990}
991
992/* finish up ixfr create processing */
993static void ixfr_create_finishup(struct ixfr_create* ixfrcr,
994	struct ixfr_store* store, struct zone* zone, int append_mem,
995	struct nsd* nsd, const char* zfile, uint32_t ixfr_number)
996{
997	char log_buf[1024], nowstr[128];
998	/* create the log message */
999	time_t now = time(NULL);
1000	if(store->cancelled || ixfr_create_too_large(ixfrcr, store)) {
1001		/* remove unneeded files.
1002		 * since this ixfr cannot be created the others are useless. */
1003		ixfr_delete_superfluous_files(zone, zfile, 0);
1004		return;
1005	}
1006	snprintf(nowstr, sizeof(nowstr), "%s", ctime(&now));
1007	if(strchr(nowstr, '\n'))
1008		*strchr(nowstr, '\n') = 0;
1009	snprintf(log_buf, sizeof(log_buf),
1010		"IXFR created by NSD %s for %s %u to %u of %u bytes at time %s",
1011		PACKAGE_VERSION, wiredname2str(ixfrcr->zone_name),
1012		(unsigned)ixfrcr->old_serial, (unsigned)ixfrcr->new_serial,
1013		(unsigned)ixfr_data_size(store->data), nowstr);
1014	store->data->log_str = strdup(log_buf);
1015	if(!store->data->log_str) {
1016		log_msg(LOG_ERR, "out of memory");
1017		ixfr_store_free(store);
1018		return;
1019	}
1020	if(!ixfr_create_rename_and_delete_files(
1021		wiredname2str(ixfrcr->zone_name), zone->opts->name, zfile,
1022		ixfr_number, ixfrcr->max_size, ixfr_data_size(store->data))) {
1023		log_msg(LOG_ERR, "could not rename other ixfr files");
1024		ixfr_store_free(store);
1025		return;
1026	}
1027	if(!ixfr_write_file(zone, store->data, zfile, 1)) {
1028		log_msg(LOG_ERR, "could not write to file");
1029		ixfr_store_free(store);
1030		return;
1031	}
1032	if(append_mem) {
1033		ixfr_store_finish(store, nsd, log_buf);
1034	}
1035}
1036
1037void ixfr_readup_exist(struct zone* zone, struct nsd* nsd,
1038	const char* zfile)
1039{
1040	/* the .ixfr file already exists with the correct serial numbers
1041	 * on the disk. Read up the ixfr files from the drive and put them
1042	 * in memory. To match the zone that has just been read.
1043	 * We can skip ixfr creation, and read up the files from the drive.
1044	 * If the files on the drive are consistent, we end up with exactly
1045	 * those ixfrs and that zone in memory.
1046	 * Presumably, the user has used nsd-checkzone to create an IXFR
1047	 * file and has put a new zone file, so we read up the data that
1048	 * we should have now.
1049	 * This also takes into account the config on number and size. */
1050	ixfr_read_from_file(nsd, zone, zfile);
1051}
1052
1053int ixfr_create_perform(struct ixfr_create* ixfrcr, struct zone* zone,
1054	int append_mem, struct nsd* nsd, const char* zfile,
1055	uint32_t ixfr_number)
1056{
1057	struct ixfr_store store_mem, *store;
1058	FILE* spool;
1059	if(!ixfr_perform_init(ixfrcr, zone, &store_mem, &store, &spool)) {
1060		(void)unlink(ixfrcr->file_name);
1061		return 0;
1062	}
1063	if(ixfrcr->new_serial == ixfrcr->old_serial ||
1064		compare_serial(ixfrcr->new_serial, ixfrcr->old_serial)<0) {
1065		log_msg(LOG_ERR, "zone %s ixfr could not be created because the serial is the same or moves backwards, from %u to %u",
1066			wiredname2str(ixfrcr->zone_name),
1067			(unsigned)ixfrcr->old_serial,
1068			(unsigned)ixfrcr->new_serial);
1069		ixfr_store_cancel(store);
1070		fclose(spool);
1071		ixfr_store_free(store);
1072		(void)unlink(ixfrcr->file_name);
1073		ixfr_delete_superfluous_files(zone, zfile, 0);
1074		if(append_mem)
1075			ixfr_store_delixfrs(zone);
1076		return 0;
1077	}
1078	if(ixfr_create_already_done(ixfrcr, zone, zfile, 1)) {
1079		ixfr_store_cancel(store);
1080		fclose(spool);
1081		ixfr_store_free(store);
1082		(void)unlink(ixfrcr->file_name);
1083		if(append_mem) {
1084			ixfr_readup_exist(zone, nsd, zfile);
1085		}
1086		return 0;
1087	}
1088
1089	if(!ixfr_create_walk_zone(spool, ixfrcr, store, zone)) {
1090		fclose(spool);
1091		ixfr_store_free(store);
1092		(void)unlink(ixfrcr->file_name);
1093		ixfr_delete_superfluous_files(zone, zfile, 0);
1094		return 0;
1095	}
1096	if(store->data && !store->data->oldsoa) {
1097		log_msg(LOG_ERR, "error spool file did not contain a SOA record");
1098		fclose(spool);
1099		ixfr_store_free(store);
1100		(void)unlink(ixfrcr->file_name);
1101		return 0;
1102	}
1103	if(!store->cancelled)
1104		ixfr_store_finish_data(store);
1105	fclose(spool);
1106	(void)unlink(ixfrcr->file_name);
1107
1108	ixfr_create_finishup(ixfrcr, store, zone, append_mem, nsd, zfile,
1109		ixfr_number);
1110	return 1;
1111}
1112
1113void ixfr_create_cancel(struct ixfr_create* ixfrcr)
1114{
1115	if(!ixfrcr)
1116		return;
1117	(void)unlink(ixfrcr->file_name);
1118	ixfr_create_free(ixfrcr);
1119}
1120
1121int ixfr_create_from_difference(struct zone* zone, const char* zfile,
1122	int* ixfr_create_already_done_flag)
1123{
1124	uint32_t old_serial;
1125	*ixfr_create_already_done_flag = 0;
1126	/* only if the zone is ixfr enabled */
1127	if(!zone_is_ixfr_enabled(zone))
1128		return 0;
1129	/* only if ixfr create is enabled */
1130	if(!zone->opts->pattern->create_ixfr)
1131		return 0;
1132	/* only if there is a zone in memory to compare with */
1133	if(!zone->soa_rrset || !zone->apex)
1134		return 0;
1135
1136	old_serial = zone_get_current_serial(zone);
1137	if(ixfr_create_already_done_serial(zone, zfile, 0, old_serial, 0)) {
1138		*ixfr_create_already_done_flag = 1;
1139		return 0;
1140	}
1141
1142	return 1;
1143}
1144