ixfrcreate.c revision 1.1.1.1
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	size_t atmarktype = 0;
537	qsort(marktypes, marktypes_used, sizeof(marktypes[0]), &sort_uint16);
538	for(s=domain->rrsets; s; s=s->next) {
539		uint16_t tp;
540		if(s->zone != zone)
541			continue;
542		tp = rrset_rrtype(s);
543		if(atmarktype < marktypes_used && marktypes[atmarktype]==tp) {
544			/* the item is in the marked list, skip it */
545			atmarktype++;
546			continue;
547		}
548		if(!process_add_rrset(store, domain, s))
549			return 0;
550	}
551	return 1;
552}
553
554/* check the difference between the domain and RRs from spool */
555static int process_diff_domain(FILE* spool, struct ixfr_create* ixfrcr,
556	struct ixfr_store* store, struct zone* zone, struct domain* domain)
557{
558	/* Read the RR types from spool. Mark off the ones seen,
559	 * later, the notseen ones from the new zone are added RRsets.
560	 * For the ones not in the new zone, they are deleted RRsets.
561	 * If they exist in old and new, check for RR differences. */
562	uint32_t spool_type_count, i;
563	uint16_t marktypes[65536];
564	size_t marktypes_used = 0;
565	if(!read_spool_u32(spool, &spool_type_count)) {
566		log_msg(LOG_ERR, "error reading file %s: %s",
567			ixfrcr->file_name, strerror(errno));
568		return 0;
569	}
570	if(spool_type_count > sizeof(marktypes)) {
571		log_msg(LOG_ERR, "error reading file %s: spool type count "
572			"too large", ixfrcr->file_name);
573		return 0;
574	}
575	for(i=0; i<spool_type_count; i++) {
576		uint16_t tp, kl, rrcount;
577		struct rrset* rrset;
578		if(!read_spool_u16(spool, &tp) ||
579		   !read_spool_u16(spool, &kl) ||
580		   !read_spool_u16(spool, &rrcount)) {
581			log_msg(LOG_ERR, "error reading file %s: %s",
582				ixfrcr->file_name, strerror(errno));
583			return 0;
584		}
585		/* The rrcount is within limits of sizeof(marktypes), because
586		 * the uint16_t < 65536 */
587		rrset = domain_find_rrset(domain, zone, tp);
588		if(!rrset) {
589			/* rrset in spool but not in new zone, deleted RRset */
590			if(!process_spool_delrrset(spool, ixfrcr, store,
591				(void*)dname_name(domain_dname(domain)),
592				domain_dname(domain)->name_size, tp, kl,
593				rrcount))
594				return 0;
595		} else {
596			/* add to the marked types, this one is present in
597			 * spool */
598			marktypes[marktypes_used++] = tp;
599			/* rrset in old and in new zone, diff the RRset */
600			if(!process_diff_rrset(spool, ixfrcr, store, domain,
601				tp, kl, rrcount, rrset))
602				return 0;
603		}
604	}
605	/* process markoff to see if new zone has RRsets not in spool,
606	 * those are added RRsets. */
607	if(!process_marktypes(store, zone, domain, marktypes, marktypes_used))
608		return 0;
609	return 1;
610}
611
612/* add the RRs for the domain in new zone */
613static int process_domain_add_RRs(struct ixfr_store* store, struct zone* zone,
614	struct domain* domain)
615{
616	rrset_type* s;
617	for(s=domain->rrsets; s; s=s->next) {
618		if(s->zone != zone)
619			continue;
620		if(!process_add_rrset(store, domain, s))
621			return 0;
622	}
623	return 1;
624}
625
626/* del the RRs for the domain from the spool */
627static int process_domain_del_RRs(struct ixfr_create* ixfrcr,
628	struct ixfr_store* store, FILE* spool, uint8_t* dname,
629	size_t dname_len)
630{
631	uint32_t spool_type_count, i;
632	if(!read_spool_u32(spool, &spool_type_count)) {
633		log_msg(LOG_ERR, "error reading file %s: %s",
634			ixfrcr->file_name, strerror(errno));
635		return 0;
636	}
637	if(spool_type_count > 65536) {
638		log_msg(LOG_ERR, "error reading file %s: del RR spool type "
639			"count too large", ixfrcr->file_name);
640		return 0;
641	}
642	for(i=0; i<spool_type_count; i++) {
643		uint16_t tp, kl, rrcount;
644		if(!read_spool_u16(spool, &tp) ||
645		   !read_spool_u16(spool, &kl) ||
646		   !read_spool_u16(spool, &rrcount)) {
647			log_msg(LOG_ERR, "error reading file %s: %s",
648				ixfrcr->file_name, strerror(errno));
649			return 0;
650		}
651		/* The rrcount is within reasonable limits, because
652		 * the uint16_t < 65536 */
653		if(!process_spool_delrrset(spool, ixfrcr, store, dname,
654			dname_len, tp, kl, rrcount))
655			return 0;
656	}
657	return 1;
658}
659
660/* init the spool dname iterator */
661static void spool_dname_iter_init(struct spool_dname_iterator* iter,
662	FILE* spool, char* file_name)
663{
664	memset(iter, 0, sizeof(*iter));
665	iter->spool = spool;
666	iter->file_name = file_name;
667}
668
669/* read the dname element into the buffer for the spool dname iterator */
670static int spool_dname_iter_read(struct spool_dname_iterator* iter)
671{
672	if(!read_spool_dname(iter->spool, iter->dname, sizeof(iter->dname),
673		&iter->dname_len)) {
674		log_msg(LOG_ERR, "error reading file %s: %s",
675			iter->file_name, strerror(errno));
676		return 0;
677	}
678	return 1;
679}
680
681/* get the next name to operate on, that is not processed yet, 0 on failure
682 * returns okay on endoffile, check with eof for that.
683 * when done with an element, set iter->is_processed on the element. */
684static int spool_dname_iter_next(struct spool_dname_iterator* iter)
685{
686	if(iter->eof)
687		return 1;
688	if(!iter->read_first) {
689		/* read the first one */
690		if(!spool_dname_iter_read(iter))
691			return 0;
692		if(iter->dname_len == 0)
693			iter->eof = 1;
694		iter->read_first = 1;
695		iter->is_processed = 0;
696	}
697	if(!iter->is_processed) {
698		/* the current one needs processing */
699		return 1;
700	}
701	/* read the next one */
702	if(!spool_dname_iter_read(iter))
703		return 0;
704	if(iter->dname_len == 0)
705		iter->eof = 1;
706	iter->is_processed = 0;
707	return 1;
708}
709
710/* check if the ixfr is too large */
711static int ixfr_create_too_large(struct ixfr_create* ixfrcr,
712	struct ixfr_store* store)
713{
714	if(store->cancelled)
715		return 1;
716	if(ixfrcr->max_size != 0 &&
717		ixfr_data_size(store->data) > ixfrcr->max_size) {
718		if(ixfrcr->errorcmdline) {
719			log_msg(LOG_ERR, "the ixfr for %s exceeds size %u, it is not created",
720				wiredname2str(ixfrcr->zone_name),
721				(unsigned)ixfrcr->max_size);
722		} else {
723			VERBOSITY(2, (LOG_INFO, "the ixfr for %s exceeds size %u, it is not created",
724				wiredname2str(ixfrcr->zone_name),
725				(unsigned)ixfrcr->max_size));
726		}
727		ixfr_store_cancel(store);
728		return 1;
729	}
730	return 0;
731}
732
733/* process the spool input before the domain */
734static int process_spool_before_domain(FILE* spool, struct ixfr_create* ixfrcr,
735	struct ixfr_store* store, struct domain* domain,
736	struct spool_dname_iterator* iter, struct region* tmp_region)
737{
738	const dname_type* dname;
739	if(ixfr_create_too_large(ixfrcr, store))
740		return 0;
741	/* read the domains and rrsets before the domain and those are from
742	 * the old zone. If the domain is equal, return to have that processed
743	 * if we bypass, that means the domain does not exist, do that */
744	while(!iter->eof) {
745		if(!spool_dname_iter_next(iter))
746			return 0;
747		if(iter->eof)
748			break;
749		/* see if we are at, before or after the domain */
750		dname = dname_make(tmp_region, iter->dname, 1);
751		if(!dname) {
752			log_msg(LOG_ERR, "error in dname in %s",
753				iter->file_name);
754			return 0;
755		}
756		if(dname_compare(dname, domain_dname(domain)) < 0) {
757			/* the dname is smaller than the one from the zone.
758			 * it must be deleted, process it */
759			if(!process_domain_del_RRs(ixfrcr, store, spool,
760				iter->dname, iter->dname_len))
761				return 0;
762			iter->is_processed = 1;
763		} else {
764			/* we are at or after the domain we are looking for,
765			 * done here */
766			return 1;
767		}
768		if(ixfr_create_too_large(ixfrcr, store))
769			return 0;
770	}
771	/* no more domains on spool, done here */
772	return 1;
773}
774
775/* process the spool input for the domain */
776static int process_spool_for_domain(FILE* spool, struct ixfr_create* ixfrcr,
777	struct ixfr_store* store, struct zone* zone, struct domain* domain,
778	struct spool_dname_iterator* iter, struct region* tmp_region)
779{
780	/* process all the spool that is not the domain, that is before the
781	 * domain in the new zone */
782	if(!process_spool_before_domain(spool, ixfrcr, store, domain, iter,
783		tmp_region))
784		return 0;
785
786	if(ixfr_create_too_large(ixfrcr, store))
787		return 0;
788	/* are we at the correct domain now? */
789	if(iter->eof || iter->dname_len != domain_dname(domain)->name_size ||
790		memcmp(iter->dname, dname_name(domain_dname(domain)),
791			iter->dname_len) != 0) {
792		/* the domain from the new zone is not present in the old zone,
793		 * the content is in the added RRs set */
794		if(!process_domain_add_RRs(store, zone, domain))
795			return 0;
796		return 1;
797	}
798
799	/* process the domain */
800	/* the domain exists both in the old and new zone,
801	 * check for RR differences */
802	if(!process_diff_domain(spool, ixfrcr, store, zone, domain))
803		return 0;
804	iter->is_processed = 1;
805
806	return 1;
807}
808
809/* process remaining spool items */
810static int process_spool_remaining(FILE* spool, struct ixfr_create* ixfrcr,
811	struct ixfr_store* store, struct spool_dname_iterator* iter)
812{
813	/* the remaining domain names in the spool file, that is after
814	 * the last domain in the new zone. */
815	if(ixfr_create_too_large(ixfrcr, store))
816		return 0;
817	while(!iter->eof) {
818		if(!spool_dname_iter_next(iter))
819			return 0;
820		if(iter->eof)
821			break;
822		/* the domain only exists in the spool, the old zone,
823		 * and not in the new zone. That would be domains
824		 * after the new zone domains, or there are no new
825		 * zone domains */
826		if(!process_domain_del_RRs(ixfrcr, store, spool, iter->dname,
827			iter->dname_len))
828			return 0;
829		iter->is_processed = 1;
830		if(ixfr_create_too_large(ixfrcr, store))
831			return 0;
832	}
833	return 1;
834}
835
836/* walk through the zone and find the differences */
837static int ixfr_create_walk_zone(FILE* spool, struct ixfr_create* ixfrcr,
838	struct ixfr_store* store, struct zone* zone)
839{
840	struct domain* domain;
841	struct spool_dname_iterator iter;
842	struct region* tmp_region;
843	spool_dname_iter_init(&iter, spool, ixfrcr->file_name);
844	tmp_region = region_create(xalloc, free);
845	for(domain = zone->apex; domain && domain_is_subdomain(domain,
846		zone->apex); domain = domain_next(domain)) {
847		uint32_t count = domain_count_rrsets(domain, zone);
848		if(count == 0)
849			continue;
850
851		/* the domain is a domain in the new zone */
852		if(!process_spool_for_domain(spool, ixfrcr, store, zone,
853			domain, &iter, tmp_region)) {
854			region_destroy(tmp_region);
855			return 0;
856		}
857		region_free_all(tmp_region);
858		if(ixfr_create_too_large(ixfrcr, store))
859			return 0;
860	}
861	if(!process_spool_remaining(spool, ixfrcr, store, &iter)) {
862		region_destroy(tmp_region);
863		return 0;
864	}
865	region_destroy(tmp_region);
866	return 1;
867}
868
869/* see if the ixfr has already been created by reading the file header
870 * of the to-be-created file, if that file already exists */
871static int ixfr_create_already_done_serial(struct zone* zone,
872	const char* zfile, int checknew, uint32_t old_serial,
873	uint32_t new_serial)
874{
875	uint32_t file_oldserial = 0, file_newserial = 0;
876	size_t data_size = 0;
877	if(!ixfr_read_file_header(zone->opts->name, zfile, 1, &file_oldserial,
878		&file_newserial, &data_size, 0)) {
879		/* could not read, so it was not done */
880		return 0;
881	}
882	if(file_oldserial == old_serial &&
883		(!checknew || file_newserial == new_serial)) {
884		log_msg(LOG_INFO, "IXFR already exists in file %s.ixfr, nothing to do",
885			zfile);
886		return 1;
887	}
888	return 0;
889}
890
891/* See the data size of the ixfr by reading the file header of the ixfr file */
892static int ixfr_read_header_data_size(const char* zname,
893	const char* zfile, int file_num, size_t* data_size)
894{
895	uint32_t file_oldserial = 0, file_newserial = 0;
896	if(!ixfr_read_file_header(zname, zfile, file_num, &file_oldserial,
897		&file_newserial, data_size, 0)) {
898		/* could not read */
899		return 0;
900	}
901	return 1;
902}
903
904/* see if the ixfr has already been created by reading the file header
905 * of the to-be-created file, if that file already exists */
906static int ixfr_create_already_done(struct ixfr_create* ixfrcr,
907	struct zone* zone, const char* zfile, int checknew)
908{
909	return ixfr_create_already_done_serial(zone, zfile, checknew,
910		ixfrcr->old_serial, ixfrcr->new_serial);
911}
912
913/* store the new soa record for the ixfr */
914static int ixfr_create_store_newsoa(struct ixfr_store* store,
915	struct zone* zone)
916{
917	if(!zone || !zone->soa_rrset) {
918		log_msg(LOG_ERR, "error no SOA rrset");
919		return 0;
920	}
921	if(zone->soa_rrset->rr_count == 0) {
922		log_msg(LOG_ERR, "error empty SOA rrset");
923		return 0;
924	}
925	if(!ixfr_store_add_newsoa_rdatas(store, domain_dname(zone->apex),
926		zone->soa_rrset->rrs[0].type, zone->soa_rrset->rrs[0].klass,
927		zone->soa_rrset->rrs[0].ttl, zone->soa_rrset->rrs[0].rdatas,
928		zone->soa_rrset->rrs[0].rdata_count)) {
929		log_msg(LOG_ERR, "out of memory");
930		return 0;
931	}
932	return 1;
933}
934
935/* initialise ixfr_create perform, open spool, read header, get serial */
936static int ixfr_perform_init(struct ixfr_create* ixfrcr, struct zone* zone,
937	struct ixfr_store* store_mem, struct ixfr_store** store, FILE** spool)
938{
939	*spool = fopen(ixfrcr->file_name, "r");
940	if(!*spool) {
941		log_msg(LOG_ERR, "could not open %s for reading: %s",
942			ixfrcr->file_name, strerror(errno));
943		return 0;
944	}
945	if(!read_spool_header(*spool, ixfrcr)) {
946		fclose(*spool);
947		return 0;
948	}
949	ixfrcr->new_serial = zone_get_current_serial(zone);
950	*store = ixfr_store_start(zone, store_mem, ixfrcr->old_serial,
951		ixfrcr->new_serial);
952	if(!ixfr_create_store_newsoa(*store, zone)) {
953		fclose(*spool);
954		ixfr_store_free(*store);
955		return 0;
956	}
957	return 1;
958}
959
960/* rename the other ixfr files */
961static int ixfr_create_rename_and_delete_files(const char* zname,
962	const char* zoptsname, const char* zfile, uint32_t ixfr_number,
963	size_t ixfr_size, size_t cur_data_size)
964{
965	size_t size_in_use = cur_data_size;
966	int dest_nr_files = (int)ixfr_number, maxsizehit = 0;
967	int num = 1;
968	while(ixfr_file_exists(zfile, num)) {
969		size_t fsize = 0;
970		if(!maxsizehit) {
971			if(!ixfr_read_header_data_size(zoptsname, zfile, num,
972				&fsize) || size_in_use + fsize > ixfr_size) {
973				/* no more than this because of storage size */
974				dest_nr_files = num;
975				maxsizehit = 1;
976			}
977			size_in_use += fsize;
978		}
979		num++;
980	}
981	num--;
982	/* num is now the number of ixfr files that exist */
983	while(num > 0) {
984		if(num+1 > dest_nr_files) {
985			(void)ixfr_unlink_it(zname, zfile, num, 0);
986		} else {
987			if(!ixfr_rename_it(zname, zfile, num, 0, num+1, 0))
988				return 0;
989		}
990		num--;
991	}
992	return 1;
993}
994
995/* finish up ixfr create processing */
996static void ixfr_create_finishup(struct ixfr_create* ixfrcr,
997	struct ixfr_store* store, struct zone* zone, int append_mem,
998	struct nsd* nsd, const char* zfile, uint32_t ixfr_number)
999{
1000	char log_buf[1024], nowstr[128];
1001	/* create the log message */
1002	time_t now = time(NULL);
1003	if(store->cancelled || ixfr_create_too_large(ixfrcr, store)) {
1004		/* remove unneeded files.
1005		 * since this ixfr cannot be created the others are useless. */
1006		ixfr_delete_superfluous_files(zone, zfile, 0);
1007		return;
1008	}
1009	snprintf(nowstr, sizeof(nowstr), "%s", ctime(&now));
1010	if(strchr(nowstr, '\n'))
1011		*strchr(nowstr, '\n') = 0;
1012	snprintf(log_buf, sizeof(log_buf),
1013		"IXFR created by NSD %s for %s %u to %u of %u bytes at time %s",
1014		PACKAGE_VERSION, wiredname2str(ixfrcr->zone_name),
1015		(unsigned)ixfrcr->old_serial, (unsigned)ixfrcr->new_serial,
1016		(unsigned)ixfr_data_size(store->data), nowstr);
1017	store->data->log_str = strdup(log_buf);
1018	if(!store->data->log_str) {
1019		log_msg(LOG_ERR, "out of memory");
1020		ixfr_store_free(store);
1021		return;
1022	}
1023	if(!ixfr_create_rename_and_delete_files(
1024		wiredname2str(ixfrcr->zone_name), zone->opts->name, zfile,
1025		ixfr_number, ixfrcr->max_size, ixfr_data_size(store->data))) {
1026		log_msg(LOG_ERR, "could not rename other ixfr files");
1027		ixfr_store_free(store);
1028		return;
1029	}
1030	if(!ixfr_write_file(zone, store->data, zfile, 1)) {
1031		log_msg(LOG_ERR, "could not write to file");
1032		ixfr_store_free(store);
1033		return;
1034	}
1035	if(append_mem) {
1036		ixfr_store_finish(store, nsd, log_buf);
1037	}
1038}
1039
1040void ixfr_readup_exist(struct zone* zone, struct nsd* nsd,
1041	const char* zfile)
1042{
1043	/* the .ixfr file already exists with the correct serial numbers
1044	 * on the disk. Read up the ixfr files from the drive and put them
1045	 * in memory. To match the zone that has just been read.
1046	 * We can skip ixfr creation, and read up the files from the drive.
1047	 * If the files on the drive are consistent, we end up with exactly
1048	 * those ixfrs and that zone in memory.
1049	 * Presumably, the user has used nsd-checkzone to create an IXFR
1050	 * file and has put a new zone file, so we read up the data that
1051	 * we should have now.
1052	 * This also takes into account the config on number and size. */
1053	ixfr_read_from_file(nsd, zone, zfile);
1054}
1055
1056int ixfr_create_perform(struct ixfr_create* ixfrcr, struct zone* zone,
1057	int append_mem, struct nsd* nsd, const char* zfile,
1058	uint32_t ixfr_number)
1059{
1060	struct ixfr_store store_mem, *store;
1061	FILE* spool;
1062	if(!ixfr_perform_init(ixfrcr, zone, &store_mem, &store, &spool)) {
1063		(void)unlink(ixfrcr->file_name);
1064		return 0;
1065	}
1066	if(ixfrcr->new_serial == ixfrcr->old_serial ||
1067		compare_serial(ixfrcr->new_serial, ixfrcr->old_serial)<0) {
1068		log_msg(LOG_ERR, "zone %s ixfr could not be created because the serial is the same or moves backwards, from %u to %u",
1069			wiredname2str(ixfrcr->zone_name),
1070			(unsigned)ixfrcr->old_serial,
1071			(unsigned)ixfrcr->new_serial);
1072		ixfr_store_cancel(store);
1073		fclose(spool);
1074		ixfr_store_free(store);
1075		(void)unlink(ixfrcr->file_name);
1076		ixfr_delete_superfluous_files(zone, zfile, 0);
1077		if(append_mem)
1078			ixfr_store_delixfrs(zone);
1079		return 0;
1080	}
1081	if(ixfr_create_already_done(ixfrcr, zone, zfile, 1)) {
1082		ixfr_store_cancel(store);
1083		fclose(spool);
1084		ixfr_store_free(store);
1085		(void)unlink(ixfrcr->file_name);
1086		if(append_mem) {
1087			ixfr_readup_exist(zone, nsd, zfile);
1088		}
1089		return 0;
1090	}
1091
1092	if(!ixfr_create_walk_zone(spool, ixfrcr, store, zone)) {
1093		fclose(spool);
1094		ixfr_store_free(store);
1095		(void)unlink(ixfrcr->file_name);
1096		ixfr_delete_superfluous_files(zone, zfile, 0);
1097		return 0;
1098	}
1099	if(store->data && !store->data->oldsoa) {
1100		log_msg(LOG_ERR, "error spool file did not contain a SOA record");
1101		fclose(spool);
1102		ixfr_store_free(store);
1103		(void)unlink(ixfrcr->file_name);
1104		return 0;
1105	}
1106	if(!store->cancelled)
1107		ixfr_store_finish_data(store);
1108	fclose(spool);
1109	(void)unlink(ixfrcr->file_name);
1110
1111	ixfr_create_finishup(ixfrcr, store, zone, append_mem, nsd, zfile,
1112		ixfr_number);
1113	return 1;
1114}
1115
1116void ixfr_create_cancel(struct ixfr_create* ixfrcr)
1117{
1118	if(!ixfrcr)
1119		return;
1120	(void)unlink(ixfrcr->file_name);
1121	ixfr_create_free(ixfrcr);
1122}
1123
1124int ixfr_create_from_difference(struct zone* zone, const char* zfile,
1125	int* ixfr_create_already_done_flag)
1126{
1127	uint32_t old_serial;
1128	*ixfr_create_already_done_flag = 0;
1129	/* only if the zone is ixfr enabled */
1130	if(!zone_is_ixfr_enabled(zone))
1131		return 0;
1132	/* only if ixfr create is enabled */
1133	if(!zone->opts->pattern->create_ixfr)
1134		return 0;
1135	/* only if there is a zone in memory to compare with */
1136	if(!zone->soa_rrset || !zone->apex)
1137		return 0;
1138
1139	old_serial = zone_get_current_serial(zone);
1140	if(ixfr_create_already_done_serial(zone, zfile, 0, old_serial, 0)) {
1141		*ixfr_create_already_done_flag = 1;
1142		return 0;
1143	}
1144
1145	return 1;
1146}
1147