xref: /openbsd-src/usr.sbin/nsd/difffile.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1 /*
2  * difffile.c - DIFF file handling source code. Read and write diff files.
3  *
4  * Copyright (c) 2001-2011, NLnet Labs. All rights reserved.
5  *
6  * See LICENSE for the license.
7  *
8  */
9 
10 #include <config.h>
11 #include <assert.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <stdlib.h>
15 #include <errno.h>
16 #include "difffile.h"
17 #include "util.h"
18 #include "packet.h"
19 #include "rdata.h"
20 #include "nsec3.h"
21 
22 static int
23 write_32(FILE *out, uint32_t val)
24 {
25 	val = htonl(val);
26 	return write_data(out, &val, sizeof(val));
27 }
28 
29 static int
30 write_16(FILE *out, uint16_t val)
31 {
32 	val = htons(val);
33 	return write_data(out, &val, sizeof(val));
34 }
35 
36 static int
37 write_8(FILE *out, uint8_t val)
38 {
39 	return write_data(out, &val, sizeof(val));
40 }
41 
42 static int
43 write_str(FILE *out, const char* str)
44 {
45 	uint32_t len = strlen(str);
46 	if(!write_32(out, len))
47 		return 0;
48 	return write_data(out, str, len);
49 }
50 
51 void
52 diff_write_packet(const char* zone, uint32_t new_serial, uint16_t id,
53 	uint32_t seq_nr, uint8_t* data, size_t len, nsd_options_t* opt)
54 {
55 	const char* filename = opt->difffile;
56 	struct timeval tv;
57 	FILE *df;
58 	uint32_t file_len = sizeof(uint32_t) + strlen(zone) +
59 		sizeof(new_serial) + sizeof(id) + sizeof(seq_nr) + len;
60 
61 	if (gettimeofday(&tv, NULL) != 0) {
62 		log_msg(LOG_ERR, "could not set timestamp for %s: %s",
63 			filename, strerror(errno));
64 		return;
65 	}
66 
67 	df = fopen(filename, "a");
68 	if(!df) {
69 		log_msg(LOG_ERR, "could not open file %s for append: %s",
70 			filename, strerror(errno));
71 		return;
72 	}
73 
74 	if(!write_32(df, DIFF_PART_IXFR) ||
75 		!write_32(df, (uint32_t) tv.tv_sec) ||
76 		!write_32(df, (uint32_t) tv.tv_usec) ||
77 		!write_32(df, file_len) ||
78 		!write_str(df, zone) ||
79 		!write_32(df, new_serial) ||
80 		!write_16(df, id) ||
81 		!write_32(df, seq_nr) ||
82 		!write_data(df, data, len) ||
83 		!write_32(df, file_len))
84 	{
85 		log_msg(LOG_ERR, "could not write to file %s: %s",
86 			filename, strerror(errno));
87 	}
88 	fflush(df);
89 	fclose(df);
90 }
91 
92 void
93 diff_write_commit(const char* zone, uint32_t old_serial,
94 	uint32_t new_serial, uint16_t id, uint32_t num_parts,
95 	uint8_t commit, const char* log_str, nsd_options_t* opt)
96 {
97 	const char* filename = opt->difffile;
98 	struct timeval tv;
99 	FILE *df;
100 	uint32_t len;
101 
102 	if (gettimeofday(&tv, NULL) != 0) {
103 		log_msg(LOG_ERR, "could not set timestamp for %s: %s",
104 			filename, strerror(errno));
105 		return;
106 	}
107 
108 	df = fopen(filename, "a");
109 	if(!df) {
110 		log_msg(LOG_ERR, "could not open file %s for append: %s",
111 			filename, strerror(errno));
112 		return;
113 	}
114 
115 	len = strlen(zone) + sizeof(len) + sizeof(old_serial) +
116 		sizeof(new_serial) + sizeof(id) + sizeof(num_parts) +
117 		sizeof(commit) + strlen(log_str) + sizeof(len);
118 
119 	if(!write_32(df, DIFF_PART_SURE) ||
120 		!write_32(df, (uint32_t) tv.tv_sec) ||
121 		!write_32(df, (uint32_t) tv.tv_usec) ||
122 		!write_32(df, len) ||
123 		!write_str(df, zone) ||
124 		!write_32(df, old_serial) ||
125 		!write_32(df, new_serial) ||
126 		!write_16(df, id) ||
127 		!write_32(df, num_parts) ||
128 		!write_8(df, commit) ||
129 		!write_str(df, log_str) ||
130 		!write_32(df, len))
131 	{
132 		log_msg(LOG_ERR, "could not write to file %s: %s",
133 			filename, strerror(errno));
134 	}
135 	fflush(df);
136 	fclose(df);
137 }
138 
139 /*
140  * Checksum to signal no data change occured (for example, by a
141  * zonec run.
142  */
143 int
144 db_crc_different(namedb_type* db)
145 {
146 	FILE *fd = fopen(db->filename, "r");
147 	uint32_t crc_file;
148 	char buf[NAMEDB_MAGIC_SIZE];
149 	if(fd == NULL) {
150 		log_msg(LOG_ERR, "unable to load %s: %s",
151 			db->filename, strerror(errno));
152 		return -1;
153 	}
154 
155 	/* seek to position of CRC, check it and magic no */
156 	if(fseeko(fd, db->crc_pos, SEEK_SET)==-1) {
157 		log_msg(LOG_ERR, "unable to fseeko %s: %s. db changed?",
158 			db->filename, strerror(errno));
159 		fclose(fd);
160 		return -1;
161 	}
162 
163 	if(fread(&crc_file, sizeof(crc_file), 1, fd) != 1) {
164 		if(!feof(fd))
165 			log_msg(LOG_ERR, "could not read %s CRC: %s. "
166 				"db changed?", db->filename, strerror(errno));
167 		fclose(fd);
168 		return -1;
169 	}
170 	crc_file = ntohl(crc_file);
171 
172 	if(fread(buf, sizeof(char), sizeof(buf), fd) != sizeof(buf)) {
173 		if(!feof(fd))
174 			log_msg(LOG_ERR, "could not read %s magic: %s. "
175 				"db changed?", db->filename, strerror(errno));
176 		fclose(fd);
177 		return -1;
178 	}
179 	if(memcmp(buf, NAMEDB_MAGIC, NAMEDB_MAGIC_SIZE) != 0) {
180 		fclose(fd);
181 		return -1;
182 	}
183 
184 	fclose(fd);
185 
186 	if(db->crc == crc_file)
187 		return 0;
188 	return 1;
189 }
190 
191 int
192 diff_read_32(FILE *in, uint32_t* result)
193 {
194 	if (fread(result, sizeof(*result), 1, in) == 1) {
195 		*result = ntohl(*result);
196 		return 1;
197 	} else {
198 		return 0;
199 	}
200 }
201 
202 int
203 diff_read_16(FILE *in, uint16_t* result)
204 {
205         if (fread(result, sizeof(*result), 1, in) == 1) {
206                 *result = ntohs(*result);
207                 return 1;
208         } else {
209                 return 0;
210         }
211 }
212 
213 int
214 diff_read_8(FILE *in, uint8_t* result)
215 {
216         if (fread(result, sizeof(*result), 1, in) == 1) {
217                 return 1;
218         } else {
219                 return 0;
220         }
221 }
222 
223 int
224 diff_read_str(FILE* in, char* buf, size_t len)
225 {
226 	uint32_t disklen;
227 	if(!diff_read_32(in, &disklen))
228 		return 0;
229 	if(disklen >= len)
230 		return 0;
231 	if(fread(buf, disklen, 1, in) != 1)
232 		return 0;
233 	buf[disklen] = 0;
234 	return 1;
235 }
236 
237 static void
238 add_rdata_to_recyclebin(namedb_type* db, rr_type* rr)
239 {
240 	/* add rdatas to recycle bin. */
241 	size_t i;
242 	for(i=0; i<rr->rdata_count; i++)
243 	{
244 		if(!rdata_atom_is_domain(rr->type, i))
245 			region_recycle(db->region, rr->rdatas[i].data,
246 				rdata_atom_size(rr->rdatas[i])
247 				+ sizeof(uint16_t));
248 	}
249 	region_recycle(db->region, rr->rdatas,
250 		sizeof(rdata_atom_type)*rr->rdata_count);
251 }
252 
253 /* this routine determines if below a domain there exist names with
254  * data (is_existing) or no names below the domain have data.
255  */
256 static int
257 has_data_below(domain_type* top)
258 {
259 	domain_type* d = top;
260 	assert(d != NULL);
261 	/* in the canonical ordering subdomains are after this name */
262 	d = domain_next(d);
263 	while(d != NULL && dname_is_subdomain(domain_dname(d), domain_dname(top))) {
264 		if(d->is_existing)
265 			return 1;
266 		d = domain_next(d);
267 	}
268 	return 0;
269 }
270 
271 
272 /* this routine makes empty terminals non-existent.
273  * @domain the lowest empty terminal
274  * @ce the closest encloser
275  */
276 static domain_type*
277 rrset_delete_empty_terminals(domain_type* domain, domain_type* ce)
278 {
279 	assert(domain);
280 	if (domain->rrsets == 0) {
281 		/* if there is no data below it, it becomes non existing.
282 		   also empty nonterminals above it become nonexisting */
283 		/* check for data below this node. */
284 		if(!has_data_below(domain)) {
285 			/* nonexist this domain and all parent empty nonterminals */
286 			domain_type* p = domain;
287 			while(p != NULL && p->rrsets == 0) {
288 				if(p == ce || has_data_below(p))
289 					return p;
290 				p->is_existing = 0;
291 				p = p->parent;
292 			}
293 		}
294 	}
295 	return NULL;
296 }
297 
298 
299 static domain_type*
300 rrset_delete(namedb_type* db, domain_type* domain, rrset_type* rrset)
301 {
302 	int i;
303 	/* find previous */
304 	rrset_type** pp = &domain->rrsets;
305 	while(*pp && *pp != rrset) {
306 		pp = &( (*pp)->next );
307 	}
308 	if(!*pp) {
309 		/* rrset does not exist for domain */
310 		return NULL;
311 	}
312 	*pp = rrset->next;
313 
314 	DEBUG(DEBUG_XFRD,2, (LOG_INFO, "delete rrset of %s type %s",
315 		dname_to_string(domain_dname(domain),0),
316 		rrtype_to_string(rrset_rrtype(rrset))));
317 
318 	/* is this a SOA rrset ? */
319 	if(rrset->zone->soa_rrset == rrset) {
320 		rrset->zone->soa_rrset = 0;
321 		rrset->zone->updated = 1;
322 		domain->has_SOA = 0;
323 	}
324 	if(rrset->zone->ns_rrset == rrset) {
325 		rrset->zone->ns_rrset = 0;
326 	}
327 	if(domain == rrset->zone->apex && rrset_rrtype(rrset) == TYPE_RRSIG) {
328 		for (i = 0; i < rrset->rr_count; ++i) {
329 			if (rr_rrsig_type_covered(&rrset->rrs[i]) == TYPE_DNSKEY) {
330 				rrset->zone->is_secure = 0;
331 				break;
332 			}
333 		}
334 	}
335 
336 #ifdef NSEC3
337 #ifndef FULL_PREHASH
338 	if (rrset->rrs[0].type == TYPE_NSEC3) {
339 		namedb_del_nsec3_domain(db, domain, rrset->zone);
340 	}
341 #endif /* !FULL_PREHASH */
342 #endif /* NSEC3 */
343 
344 	/* recycle the memory space of the rrset */
345 	for (i = 0; i < rrset->rr_count; ++i)
346 		add_rdata_to_recyclebin(db, &rrset->rrs[i]);
347 	region_recycle(db->region, rrset->rrs,
348 		sizeof(rr_type) * rrset->rr_count);
349 	rrset->rr_count = 0;
350 	region_recycle(db->region, rrset, sizeof(rrset_type));
351 
352 	/* is the node now an empty node (completely deleted) */
353 	if (domain->rrsets == 0) {
354 		return domain;
355 	}
356 	return NULL;
357 }
358 
359 static int
360 rdatas_equal(rdata_atom_type *a, rdata_atom_type *b, int num, uint16_t type)
361 {
362 	int k;
363 	for(k = 0; k < num; k++)
364 	{
365 		if(rdata_atom_is_domain(type, k)) {
366 			if(dname_compare(domain_dname(a[k].domain),
367 				domain_dname(b[k].domain))!=0)
368 				return 0;
369 		} else {
370 			/* check length */
371 			if(a[k].data[0] != b[k].data[0])
372 				return 0;
373 			/* check data */
374 			if(memcmp(a[k].data+1, b[k].data+1, a[k].data[0])!=0)
375 				return 0;
376 		}
377 	}
378 	return 1;
379 }
380 
381 static int
382 find_rr_num(rrset_type* rrset,
383 	uint16_t type, uint16_t klass,
384 	rdata_atom_type *rdatas, ssize_t rdata_num)
385 {
386 	int i;
387 
388 	for(i=0; i < rrset->rr_count; ++i) {
389 		if(rrset->rrs[i].type == type &&
390 		   rrset->rrs[i].klass == klass &&
391 		   rrset->rrs[i].rdata_count == rdata_num &&
392 		   rdatas_equal(rdatas, rrset->rrs[i].rdatas, rdata_num, type))
393 		{
394 			return i;
395 		}
396 	}
397 
398 	return -1;
399 }
400 
401 static int
402 delete_RR(namedb_type* db, const dname_type* dname,
403 	uint16_t type, uint16_t klass,
404 	domain_type* prevdomain,
405 	buffer_type* packet, size_t rdatalen, zone_type *zone,
406 	region_type* temp_region, int is_axfr)
407 {
408 	domain_type *domain;
409 	rrset_type *rrset;
410 	domain = domain_table_find(db->domains, dname);
411 	if(!domain) {
412 		log_msg(LOG_WARNING, "diff: domain %s does not exist",
413 			dname_to_string(dname,0));
414 		buffer_skip(packet, rdatalen);
415 		return 1; /* not fatal error */
416 	}
417 	rrset = domain_find_rrset(domain, zone, type);
418 	if(!rrset) {
419 		log_msg(LOG_WARNING, "diff: rrset %s does not exist",
420 			dname_to_string(dname,0));
421 		buffer_skip(packet, rdatalen);
422 		return 1; /* not fatal error */
423 	} else {
424 		/* find the RR in the rrset */
425 		domain_table_type *temptable;
426 		rdata_atom_type *rdatas;
427 		ssize_t rdata_num;
428 		int rrnum;
429 		temptable = domain_table_create(temp_region);
430 		/* This will ensure that the dnames in rdata are
431 		 * normalized, conform RFC 4035, section 6.2
432 		 */
433 		rdata_num = rdata_wireformat_to_rdata_atoms(
434 			temp_region, temptable, type, rdatalen, packet, &rdatas);
435 		if(rdata_num == -1) {
436 			log_msg(LOG_ERR, "diff: bad rdata for %s",
437 				dname_to_string(dname,0));
438 			return 0;
439 		}
440 		rrnum = find_rr_num(rrset, type, klass, rdatas, rdata_num);
441 		if(rrnum == -1) {
442 			log_msg(LOG_WARNING, "diff: RR %s does not exist",
443 				dname_to_string(dname,0));
444 			return 1; /* not fatal error */
445 		}
446 #ifdef NSEC3
447 #ifndef FULL_PREHASH
448 		if (is_axfr == 0) {
449 			struct domain *parent = domain;
450 			do {
451 				if (0 != namedb_add_nsec3_mod_domain(db,
452 								    parent)) {
453 					return 0;
454 				}
455 				parent = parent->parent;
456 			} while (parent != zone->apex->parent);
457 		}
458 #else
459 		(void)is_axfr;
460 #endif /* !FULL_PREHASH */
461 #endif /* NSEC3 */
462 
463 		if(rrset->rr_count == 1) {
464 			/* delete entire rrset */
465 			domain = rrset_delete(db, domain, rrset);
466 			if (domain && domain != prevdomain && !domain->nextdiff) {
467 				/* this domain is not yet in the diff chain */
468 				prevdomain->nextdiff = domain;
469 			}
470 		} else {
471 			/* swap out the bad RR and decrease the count */
472 			rr_type* rrs_orig = rrset->rrs;
473 			add_rdata_to_recyclebin(db, &rrset->rrs[rrnum]);
474 			if(rrnum < rrset->rr_count-1)
475 				rrset->rrs[rrnum] = rrset->rrs[rrset->rr_count-1];
476 			memset(&rrset->rrs[rrset->rr_count-1], 0, sizeof(rr_type));
477 			/* realloc the rrs array one smaller */
478 			rrset->rrs = region_alloc_init(db->region, rrs_orig,
479 				sizeof(rr_type) * (rrset->rr_count-1));
480 			if(!rrset->rrs) {
481 				log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__);
482 				exit(1);
483 			}
484 			region_recycle(db->region, rrs_orig,
485 				sizeof(rr_type) * rrset->rr_count);
486 			rrset->rr_count --;
487 		}
488 	}
489 	return 1;
490 }
491 
492 static int
493 add_RR(namedb_type* db, const dname_type* dname,
494 	uint16_t type, uint16_t klass, uint32_t ttl,
495 	buffer_type* packet, size_t rdatalen, zone_type *zone,
496 	int is_axfr)
497 {
498 	domain_type* domain;
499 	rrset_type* rrset;
500 	rdata_atom_type *rdatas;
501 	rr_type *rrs_old;
502 	ssize_t rdata_num;
503 	int rrnum;
504 	domain = domain_table_find(db->domains, dname);
505 	if(!domain) {
506 		/* create the domain */
507 		domain = domain_table_insert(db->domains, dname);
508 	}
509 	rrset = domain_find_rrset(domain, zone, type);
510 	if(!rrset) {
511 		/* create the rrset */
512 		rrset = region_alloc(db->region, sizeof(rrset_type));
513 		if(!rrset) {
514 			log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__);
515 			exit(1);
516 		}
517 		rrset->zone = zone;
518 		rrset->rrs = 0;
519 		rrset->rr_count = 0;
520 		domain_add_rrset(domain, rrset);
521 	}
522 
523 	/* dnames in rdata are normalized, conform RFC 4035,
524 	 * Section 6.2
525 	 */
526 	rdata_num = rdata_wireformat_to_rdata_atoms(
527 		db->region, db->domains, type, rdatalen, packet, &rdatas);
528 	if(rdata_num == -1) {
529 		log_msg(LOG_ERR, "diff: bad rdata for %s",
530 			dname_to_string(dname,0));
531 		return 0;
532 	}
533 	rrnum = find_rr_num(rrset, type, klass, rdatas, rdata_num);
534 	if(rrnum != -1) {
535 		DEBUG(DEBUG_XFRD, 2, (LOG_ERR, "diff: RR %s already exists",
536 			dname_to_string(dname,0)));
537 		/* ignore already existing RR: lenient accepting of messages */
538 		return 1;
539 	}
540 
541 	/* re-alloc the rrs and add the new */
542 	rrs_old = rrset->rrs;
543 	rrset->rrs = region_alloc(db->region,
544 		(rrset->rr_count+1) * sizeof(rr_type));
545 	if(!rrset->rrs) {
546 		log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__);
547 		exit(1);
548 	}
549 	if(rrs_old)
550 		memcpy(rrset->rrs, rrs_old, rrset->rr_count * sizeof(rr_type));
551 	region_recycle(db->region, rrs_old, sizeof(rr_type) * rrset->rr_count);
552 	rrset->rr_count ++;
553 
554 	rrset->rrs[rrset->rr_count - 1].owner = domain;
555 	rrset->rrs[rrset->rr_count - 1].rdatas = rdatas;
556 	rrset->rrs[rrset->rr_count - 1].ttl = ttl;
557 	rrset->rrs[rrset->rr_count - 1].type = type;
558 	rrset->rrs[rrset->rr_count - 1].klass = klass;
559 	rrset->rrs[rrset->rr_count - 1].rdata_count = rdata_num;
560 
561 	/* see if it is a SOA */
562 	if(domain == zone->apex) {
563 		if(type == TYPE_SOA) {
564 			uint32_t soa_minimum;
565 			zone->soa_rrset = rrset;
566 			zone->updated = 1;
567 			/* BUG #103 tweaked SOA ttl value */
568 			if(zone->soa_nx_rrset == 0) {
569 				zone->soa_nx_rrset = region_alloc(db->region,
570 					sizeof(rrset_type));
571 				if(!zone->soa_nx_rrset) {
572 					log_msg(LOG_ERR, "out of memory, %s:%d",
573 						__FILE__, __LINE__);
574 					exit(1);
575 				}
576 				zone->soa_nx_rrset->rr_count = 1;
577 				zone->soa_nx_rrset->next = 0;
578 				zone->soa_nx_rrset->zone = zone;
579 				zone->soa_nx_rrset->rrs = region_alloc(db->region,
580 					sizeof(rr_type));
581 				if(!zone->soa_nx_rrset->rrs) {
582 					log_msg(LOG_ERR, "out of memory, %s:%d",
583 						__FILE__, __LINE__);
584 					exit(1);
585 				}
586 			}
587 			memcpy(zone->soa_nx_rrset->rrs, rrset->rrs, sizeof(rr_type));
588 			memcpy(&soa_minimum, rdata_atom_data(rrset->rrs->rdatas[6]),
589 				rdata_atom_size(rrset->rrs->rdatas[6]));
590 			if (rrset->rrs->ttl > ntohl(soa_minimum)) {
591 				rrset->zone->soa_nx_rrset->rrs[0].ttl = ntohl(soa_minimum);
592 			}
593 			domain->has_SOA = 1;
594 		}
595 		if(type == TYPE_NS) {
596 			zone->ns_rrset = rrset;
597 		}
598 		if(type == TYPE_RRSIG) {
599 			int i;
600 			for (i = 0; i < rrset->rr_count; ++i) {
601 				if (rr_rrsig_type_covered(&rrset->rrs[i]) == TYPE_DNSKEY) {
602 					zone->is_secure = 1;
603 					break;
604 				}
605 			}
606 		}
607 	}
608 
609 #ifdef NSEC3
610 #ifndef FULL_PREHASH
611 	if ((type == TYPE_NSEC3) &&
612 	    (rrset->rr_count == 1)) {
613 		/* NSEC3 RRset just added */
614 		if (0 != namedb_add_nsec3_domain(db, domain, zone))
615 			return 0;
616 	}
617 	if (is_axfr == 0) {
618 		struct domain *parent = domain;
619 		do {
620 			if (0 != namedb_add_nsec3_mod_domain(db, parent))
621 				return 0;
622 			parent = parent->parent;
623 		} while (parent != zone->apex->parent);
624 	}
625 #else
626 	(void)is_axfr;
627 #endif /* !FULL_PREHASH */
628 #endif /* NSEC3 */
629 
630 	return 1;
631 }
632 
633 static zone_type*
634 find_zone(namedb_type* db, const dname_type* zone_name, nsd_options_t* opt,
635 	size_t child_count)
636 {
637 	domain_type *domain;
638 	zone_type* zone;
639 	domain = domain_table_find(db->domains, zone_name);
640 	if(!domain) {
641 		DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfr: creating domain %s",
642 			dname_to_string(zone_name,0)));
643 		/* create the zone and domain of apex (zone has config options) */
644 		domain = domain_table_insert(db->domains, zone_name);
645 	} else {
646 		/* O(1) if SOA exists */
647 		zone = domain_find_zone(domain);
648 		/* if domain was empty (no rrsets, empty zone) search in zonelist */
649 		/* check apex to make sure we don't find a parent zone */
650 		if(!zone || zone->apex != domain)
651 			zone = namedb_find_zone(db, domain);
652 		if(zone) {
653 			assert(zone->apex == domain);
654 			return zone;
655 		}
656 	}
657 	/* create the zone */
658 	DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfr: creating zone_type %s",
659 		dname_to_string(zone_name,0)));
660 	zone = (zone_type *) region_alloc(db->region, sizeof(zone_type));
661 	if(!zone) {
662 		log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__);
663 		exit(1);
664 	}
665 	zone->next = db->zones;
666 	db->zones = zone;
667 	db->zone_count++;
668 	zone->apex = domain;
669 	zone->soa_rrset = 0;
670 	zone->soa_nx_rrset = 0;
671 	zone->ns_rrset = 0;
672 #ifdef NSEC3
673 	zone->nsec3_soa_rr = NULL;
674 	zone->nsec3_last = NULL;
675 #endif
676 	zone->dirty = region_alloc(db->region, sizeof(uint8_t)*child_count);
677 	if(!zone->dirty) {
678 		log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__);
679 		exit(1);
680 	}
681 	memset(zone->dirty, 0, sizeof(uint8_t)*child_count);
682 	zone->opts = zone_options_find(opt, domain_dname(zone->apex));
683 	if(!zone->opts) {
684 		log_msg(LOG_ERR, "xfr: zone %s not in config.",
685 			dname_to_string(zone_name,0));
686 		return 0;
687 	}
688 #ifdef NSEC3
689 #ifndef FULL_PREHASH
690 	zone->nsec3_domains = NULL;
691 
692 	if (0 != zone_nsec3_domains_create(db, zone)) {
693 		log_msg(LOG_ERR,
694 			"xfr: zone NSEC3 domains "
695 			"memory allocation failure");
696 		return 0;
697 	}
698 #endif /* !FULL_PREHASH */
699 #endif /* NSEC3 */
700 	zone->number = db->zone_count;
701 	zone->is_secure = 0;
702 	zone->updated = 1;
703 	zone->is_ok = 0;
704 	return zone;
705 }
706 
707 static void
708 delete_zone_rrs(namedb_type* db, zone_type* zone)
709 {
710 	rrset_type *rrset;
711 	domain_type *domain = zone->apex;
712 	domain_type *next = NULL;
713 	zone->updated = 1;
714 #ifdef NSEC3
715 #ifndef FULL_PREHASH
716 	zone_nsec3_domains_destroy(db, zone);
717 #endif /* !FULL_PREHASH */
718 #endif /* NSEC3 */
719 
720 	/* go through entire tree below the zone apex (incl subzones) */
721 	while(domain && dname_is_subdomain(
722 		domain_dname(domain), domain_dname(zone->apex)))
723 	{
724 		DEBUG(DEBUG_XFRD,2, (LOG_INFO, "delete zone visit %s",
725 			dname_to_string(domain_dname(domain),0)));
726 		/* delete all rrsets of the zone */
727 		while((rrset = domain_find_any_rrset(domain, zone))) {
728 			(void)rrset_delete(db, domain, rrset);
729 		}
730 		next = domain_next(domain);
731 		domain->nextdiff = next;
732 		domain = next;
733 	}
734 #ifdef NSEC3
735 #ifndef FULL_PREHASH
736 	if (0 != zone_nsec3_domains_create(db, zone)) {
737 		log_msg(LOG_ERR,
738 			"Zone %s: unable to create zone NSEC3 prehash table",
739 			dname_to_string(domain_dname(zone->apex),
740 			NULL));
741 	}
742 #endif /* !FULL_PREHASH */
743 #endif /* NSEC3 */
744 
745 	DEBUG(DEBUG_XFRD, 1, (LOG_INFO, "axfrdel: recyclebin holds %lu bytes",
746 		(unsigned long) region_get_recycle_size(db->region)));
747 #ifndef NDEBUG
748 	if(nsd_debug_level >= 1)
749 		region_log_stats(db->region);
750 #endif
751 
752 	assert(zone->soa_rrset == 0);
753 	/* keep zone->soa_nx_rrset alloced */
754 	assert(zone->ns_rrset == 0);
755 	assert(zone->is_secure == 0);
756 	assert(zone->updated == 1);
757 }
758 
759 /* fix empty terminals */
760 static void
761 fix_empty_terminals(zone_type* zone_db)
762 {
763 	domain_type* domain = zone_db->apex, *ce = NULL, *next = NULL;
764 	while (domain) {
765 		ce = rrset_delete_empty_terminals(domain, ce);
766 		next = domain->nextdiff;
767 		domain->nextdiff = NULL;
768 		domain = next;
769 	}
770 }
771 
772 /* return value 0: syntaxerror,badIXFR, 1:OK, 2:done_and_skip_it */
773 static int
774 apply_ixfr(namedb_type* db, FILE *in, const off_t* startpos,
775 	const char* zone, uint32_t serialno, nsd_options_t* opt,
776 	uint16_t id, uint32_t seq_nr, uint32_t seq_total,
777 	int* is_axfr, int* delete_mode, int* rr_count,
778 	size_t child_count)
779 {
780 	uint32_t filelen, msglen, pkttype, timestamp[2];
781 	int qcount, ancount, counter;
782 	buffer_type* packet;
783 	region_type* region;
784 	int i;
785 	uint16_t rrlen;
786 	const dname_type *dname_zone, *dname;
787 	zone_type* zone_db;
788 	domain_type* last_in_list;
789 	char file_zone_name[3072];
790 	uint32_t file_serial, file_seq_nr;
791 	uint16_t file_id;
792 	off_t mempos;
793 
794 	memmove(&mempos, startpos, sizeof(off_t));
795 	if(fseeko(in, mempos, SEEK_SET) == -1) {
796 		log_msg(LOG_INFO, "could not fseeko: %s.", strerror(errno));
797 		return 0;
798 	}
799 	/* read ixfr packet RRs and apply to in memory db */
800 
801 	if(!diff_read_32(in, &pkttype) || pkttype != DIFF_PART_IXFR) {
802 		log_msg(LOG_ERR, "could not read type or wrong type");
803 		return 0;
804 	}
805 	if(!diff_read_32(in, &timestamp[0]) ||
806 	   !diff_read_32(in, &timestamp[1])) {
807 		log_msg(LOG_ERR, "could not read timestamp");
808 		return 0;
809 	}
810 
811 	if(!diff_read_32(in, &filelen)) {
812 		log_msg(LOG_ERR, "could not read len");
813 		return 0;
814 	}
815 
816 	/* read header */
817 	if(filelen < QHEADERSZ + sizeof(uint32_t)*3 + sizeof(uint16_t)) {
818 		log_msg(LOG_ERR, "msg too short");
819 		return 0;
820 	}
821 
822 	region = region_create(xalloc, free);
823 	if(!region) {
824 		log_msg(LOG_ERR, "out of memory");
825 		return 0;
826 	}
827 
828 	if(!diff_read_str(in, file_zone_name, sizeof(file_zone_name)) ||
829 		!diff_read_32(in, &file_serial) ||
830 		!diff_read_16(in, &file_id) ||
831 		!diff_read_32(in, &file_seq_nr))
832 	{
833 		log_msg(LOG_ERR, "could not part data");
834 		region_destroy(region);
835 		return 0;
836 	}
837 
838 	if(strcmp(file_zone_name, zone) != 0 || serialno != file_serial ||
839 		id != file_id || seq_nr != file_seq_nr) {
840 		log_msg(LOG_ERR, "internal error: reading part with changed id");
841 		region_destroy(region);
842 		return 0;
843 	}
844 	msglen = filelen - sizeof(uint32_t)*3 - sizeof(uint16_t)
845 		- strlen(file_zone_name);
846 	packet = buffer_create(region, QIOBUFSZ);
847 	dname_zone = dname_parse(region, zone);
848 	zone_db = find_zone(db, dname_zone, opt, child_count);
849 	if(!zone_db) {
850 		log_msg(LOG_ERR, "no zone exists");
851 		region_destroy(region);
852 		/* break out and stop the IXFR, ignore it */
853 		return 2;
854 	}
855 
856 	if(msglen > QIOBUFSZ) {
857 		log_msg(LOG_ERR, "msg too long");
858 		region_destroy(region);
859 		return 0;
860 	}
861 	buffer_clear(packet);
862 	if(fread(buffer_begin(packet), msglen, 1, in) != 1) {
863 		log_msg(LOG_ERR, "short fread: %s", strerror(errno));
864 		region_destroy(region);
865 		return 0;
866 	}
867 	buffer_set_limit(packet, msglen);
868 
869 	/* only answer section is really used, question, additional and
870 	   authority section RRs are skipped */
871 	qcount = QDCOUNT(packet);
872 	ancount = ANCOUNT(packet);
873 	buffer_skip(packet, QHEADERSZ);
874 
875 	/* skip queries */
876 	for(i=0; i<qcount; ++i)
877 		if(!packet_skip_rr(packet, 1)) {
878 			log_msg(LOG_ERR, "bad RR in question section");
879 			region_destroy(region);
880 			return 0;
881 		}
882 
883 	DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: started packet for zone %s",
884 			dname_to_string(dname_zone, 0)));
885 	/* first RR: check if SOA and correct zone & serialno */
886 	if(*rr_count == 0) {
887 		DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: %s parse first RR",
888 			dname_to_string(dname_zone, 0)));
889 		dname = dname_make_from_packet(region, packet, 1, 1);
890 		if(!dname) {
891 			log_msg(LOG_ERR, "could not parse dname");
892 			region_destroy(region);
893 			return 0;
894 		}
895 		if(dname_compare(dname_zone, dname) != 0) {
896 			log_msg(LOG_ERR, "SOA dname %s not equal to zone",
897 				dname_to_string(dname,0));
898 			log_msg(LOG_ERR, "zone dname is %s",
899 				dname_to_string(dname_zone,0));
900 			region_destroy(region);
901 			return 0;
902 		}
903 		if(!buffer_available(packet, 10)) {
904 			log_msg(LOG_ERR, "bad SOA RR");
905 			region_destroy(region);
906 			return 0;
907 		}
908 		if(buffer_read_u16(packet) != TYPE_SOA ||
909 			buffer_read_u16(packet) != CLASS_IN) {
910 			log_msg(LOG_ERR, "first RR not SOA IN");
911 			region_destroy(region);
912 			return 0;
913 		}
914 		buffer_skip(packet, sizeof(uint32_t)); /* ttl */
915 		if(!buffer_available(packet, buffer_read_u16(packet)) ||
916 			!packet_skip_dname(packet) /* skip prim_ns */ ||
917 			!packet_skip_dname(packet) /* skip email */) {
918 			log_msg(LOG_ERR, "bad SOA RR");
919 			region_destroy(region);
920 			return 0;
921 		}
922 		if(buffer_read_u32(packet) != serialno) {
923 			buffer_skip(packet, -4);
924 			log_msg(LOG_ERR, "SOA serial %d different from commit %d",
925 				buffer_read_u32(packet), serialno);
926 			region_destroy(region);
927 			return 0;
928 		}
929 		buffer_skip(packet, sizeof(uint32_t)*4);
930 		counter = 1;
931 		*rr_count = 1;
932 		*is_axfr = 0;
933 		*delete_mode = 0;
934 
935 		DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: %s start count %d, ax %d, delmode %d",
936 			dname_to_string(dname_zone, 0), *rr_count, *is_axfr, *delete_mode));
937 	}
938 	else  counter = 0;
939 
940 	last_in_list = zone_db->apex;
941 	for(; counter < ancount; ++counter,++(*rr_count))
942 	{
943 		uint16_t type, klass;
944 		uint32_t ttl;
945 
946 		if(!(dname=dname_make_from_packet(region, packet, 1,1))) {
947 			log_msg(LOG_ERR, "bad xfr RR dname %d", *rr_count);
948 			region_destroy(region);
949 			return 0;
950 		}
951 		if(!buffer_available(packet, 10)) {
952 			log_msg(LOG_ERR, "bad xfr RR format %d", *rr_count);
953 			region_destroy(region);
954 			return 0;
955 		}
956 		type = buffer_read_u16(packet);
957 		klass = buffer_read_u16(packet);
958 		ttl = buffer_read_u32(packet);
959 		rrlen = buffer_read_u16(packet);
960 		if(!buffer_available(packet, rrlen)) {
961 			log_msg(LOG_ERR, "bad xfr RR rdata %d, len %d have %d",
962 				*rr_count, rrlen, (int)buffer_remaining(packet));
963 			region_destroy(region);
964 			return 0;
965 		}
966 		DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: %s parsed count %d, ax %d, delmode %d",
967 			dname_to_string(dname_zone, 0), *rr_count, *is_axfr, *delete_mode));
968 
969 		if(*rr_count == 1 && type != TYPE_SOA) {
970 			/* second RR: if not SOA: this is an AXFR; delete all zone contents */
971 			delete_zone_rrs(db, zone_db);
972 			/* add everything else (incl end SOA) */
973 			*delete_mode = 0;
974 			*is_axfr = 1;
975 			DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: %s sawAXFR count %d, ax %d, delmode %d",
976 				dname_to_string(dname_zone, 0), *rr_count, *is_axfr, *delete_mode));
977 		}
978 		if(*rr_count == 1 && type == TYPE_SOA) {
979 			/* if the serial no of the SOA equals the serialno, then AXFR */
980 			size_t bufpos = buffer_position(packet);
981 			uint32_t thisserial;
982 			if(!packet_skip_dname(packet) ||
983 				!packet_skip_dname(packet) ||
984 				buffer_remaining(packet) < sizeof(uint32_t)*5)
985 			{
986 				log_msg(LOG_ERR, "bad xfr SOA RR formerr.");
987 				region_destroy(region);
988 				return 0;
989 			}
990 			thisserial = buffer_read_u32(packet);
991 			if(thisserial == serialno) {
992 				/* AXFR */
993 				delete_zone_rrs(db, zone_db);
994 				*delete_mode = 0;
995 				*is_axfr = 1;
996 			}
997 			/* must have stuff in memory for a successful IXFR,
998 			 * the serial number of the SOA has been checked
999 			 * previously (by check_for_bad_serial) if it exists */
1000 			if(!*is_axfr && !domain_find_rrset(zone_db->apex,
1001 				zone_db, TYPE_SOA)) {
1002 				log_msg(LOG_ERR, "%s SOA serial %d is not "
1003 					"in memory, skip IXFR", zone, serialno);
1004 				region_destroy(region);
1005 				/* break out and stop the IXFR, ignore it */
1006 				return 2;
1007 			}
1008 			buffer_set_position(packet, bufpos);
1009 		}
1010 		if(type == TYPE_SOA && !*is_axfr) {
1011 			/* switch from delete-part to add-part and back again,
1012 			   just before soa - so it gets deleted and added too */
1013 			/* this means we switch to delete mode for the final SOA */
1014 			*delete_mode = !*delete_mode;
1015 			DEBUG(DEBUG_XFRD,2, (LOG_INFO, "diff: %s IXFRswapdel count %d, ax %d, delmode %d",
1016 				dname_to_string(dname_zone, 0), *rr_count, *is_axfr, *delete_mode));
1017 		}
1018 		if(type == TYPE_TSIG || type == TYPE_OPT) {
1019 			/* ignore pseudo RRs */
1020 			buffer_skip(packet, rrlen);
1021 			continue;
1022 		}
1023 
1024 		DEBUG(DEBUG_XFRD,2, (LOG_INFO, "xfr %s RR dname is %s type %s",
1025 			*delete_mode?"del":"add",
1026 			dname_to_string(dname,0), rrtype_to_string(type)));
1027 		if(*delete_mode) {
1028 			/* delete this rr */
1029 			if(!*is_axfr && type == TYPE_SOA && counter==ancount-1
1030 				&& seq_nr == seq_total-1) {
1031 				continue; /* do not delete final SOA RR for IXFR */
1032 			}
1033 			if(!delete_RR(db, dname, type, klass, last_in_list, packet,
1034 				rrlen, zone_db, region, *is_axfr)) {
1035 				region_destroy(region);
1036 				return 0;
1037 			}
1038 			if (!*is_axfr && last_in_list->nextdiff) {
1039 				last_in_list = last_in_list->nextdiff;
1040 			}
1041 		}
1042 		else
1043 		{
1044 			/* add this rr */
1045 			if(!add_RR(db, dname, type, klass, ttl, packet,
1046 				rrlen, zone_db, *is_axfr)) {
1047 				region_destroy(region);
1048 				return 0;
1049 			}
1050 		}
1051 	}
1052 	fix_empty_terminals(zone_db);
1053 	region_destroy(region);
1054 	return 1;
1055 }
1056 
1057 static int
1058 check_for_bad_serial(namedb_type* db, const char* zone_str, uint32_t old_serial)
1059 {
1060 	/* see if serial OK with in-memory serial */
1061 	domain_type* domain;
1062 	region_type* region = region_create(xalloc, free);
1063 	const dname_type* zone_name = dname_parse(region, zone_str);
1064 	zone_type* zone = 0;
1065 	domain = domain_table_find(db->domains, zone_name);
1066 	if(domain)
1067 		zone = domain_find_zone(domain);
1068 	if(zone && zone->apex == domain && zone->soa_rrset && old_serial)
1069 	{
1070 		uint32_t memserial;
1071 		memcpy(&memserial, rdata_atom_data(zone->soa_rrset->rrs[0].rdatas[2]),
1072 			sizeof(uint32_t));
1073 		if(old_serial != ntohl(memserial)) {
1074 			region_destroy(region);
1075 			return 1;
1076 		}
1077 	}
1078 	region_destroy(region);
1079 	return 0;
1080 }
1081 
1082 /* for multiple tcp packets use a data structure that has
1083  * a rbtree (zone_names) with for each zone:
1084  * 	has a rbtree by sequence number
1085  *		with inside a serial_number and ID (for checking only)
1086  *		and contains a off_t to the IXFR packet in the file.
1087  * so when you get a commit for a zone, get zone obj, find sequence,
1088  * then check if you have all sequence numbers available. Apply all packets.
1089  */
1090 struct diff_read_data {
1091 	/* rbtree of struct diff_zone*/
1092 	rbtree_t* zones;
1093 	/* region for allocation */
1094 	region_type* region;
1095 };
1096 struct diff_zone {
1097 	/* key is dname of zone */
1098 	rbnode_t node;
1099 	/* rbtree of struct diff_xfrpart */
1100 	rbtree_t* parts;
1101 };
1102 struct diff_xfrpart {
1103 	/* key is sequence number */
1104 	rbnode_t node;
1105 	uint32_t seq_nr;
1106 	uint32_t new_serial;
1107 	uint16_t id;
1108 	off_t file_pos;
1109 };
1110 
1111 static struct diff_read_data*
1112 diff_read_data_create()
1113 {
1114 	region_type* region = region_create(xalloc, free);
1115 	struct diff_read_data* data = (struct diff_read_data*)
1116 		region_alloc(region, sizeof(struct diff_read_data));
1117 	if(!data) {
1118 		log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__);
1119 		exit(1);
1120 	}
1121 	data->region = region;
1122 	data->zones = rbtree_create(region,
1123 		(int (*)(const void *, const void *)) dname_compare);
1124 	return data;
1125 }
1126 
1127 static struct diff_zone*
1128 diff_read_find_zone(struct diff_read_data* data, const char* name)
1129 {
1130 	const dname_type* dname = dname_parse(data->region, name);
1131 	struct diff_zone* zp = (struct diff_zone*)
1132 		rbtree_search(data->zones, dname);
1133 	return zp;
1134 }
1135 
1136 static int intcompf(const void* a, const void* b)
1137 {
1138 	if(*(uint32_t*)a < *(uint32_t*)b)
1139 		return -1;
1140 	if(*(uint32_t*)a > *(uint32_t*)b)
1141 		return +1;
1142 	return 0;
1143 }
1144 
1145 static struct diff_zone*
1146 diff_read_insert_zone(struct diff_read_data* data, const char* name)
1147 {
1148 	const dname_type* dname = dname_parse(data->region, name);
1149 	struct diff_zone* zp = region_alloc(data->region,
1150 		sizeof(struct diff_zone));
1151 	if(!zp) {
1152 		log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__);
1153 		exit(1);
1154 	}
1155 	zp->node = *RBTREE_NULL;
1156 	zp->node.key = dname;
1157 	zp->parts = rbtree_create(data->region, intcompf);
1158 	rbtree_insert(data->zones, (rbnode_t*)zp);
1159 	return zp;
1160 }
1161 
1162 static struct diff_xfrpart*
1163 diff_read_find_part(struct diff_zone* zp, uint32_t seq_nr)
1164 {
1165 	struct diff_xfrpart* xp = (struct diff_xfrpart*)
1166 		rbtree_search(zp->parts, &seq_nr);
1167 	return xp;
1168 }
1169 
1170 static struct diff_xfrpart*
1171 diff_read_insert_part(struct diff_read_data* data,
1172 	struct diff_zone* zp, uint32_t seq_nr)
1173 {
1174 	struct diff_xfrpart* xp = region_alloc(data->region,
1175 		sizeof(struct diff_xfrpart));
1176 	if(!xp) {
1177 		log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__);
1178 		exit(1);
1179 	}
1180 	xp->node = *RBTREE_NULL;
1181 	xp->node.key = &xp->seq_nr;
1182 	xp->seq_nr = seq_nr;
1183 	rbtree_insert(zp->parts, (rbnode_t*)xp);
1184 	return xp;
1185 }
1186 
1187 /* mark commit as rollback and close inputfile, fatal exits */
1188 static void
1189 mark_and_exit(nsd_options_t* opt, FILE* f, off_t commitpos, const char* desc)
1190 {
1191 	const char* filename = opt->difffile;
1192 	fclose(f);
1193 	if(!(f = fopen(filename, "r+"))) {
1194 		log_msg(LOG_ERR, "mark xfr, failed to re-open difffile %s: %s",
1195 			filename, strerror(errno));
1196 	} else if(fseeko(f, commitpos, SEEK_SET) == -1) {
1197 		log_msg(LOG_INFO, "could not fseeko: %s.", strerror(errno));
1198 		fclose(f);
1199 	} else {
1200 		uint8_t c = 0;
1201 		(void)write_data(f, &c, sizeof(c));
1202 		fclose(f);
1203 		log_msg(LOG_ERR, "marked xfr as failed: %s", desc);
1204 		log_msg(LOG_ERR, "marked xfr so that next reload can succeed");
1205 	}
1206 	exit(1);
1207 }
1208 
1209 static int
1210 read_sure_part(namedb_type* db, FILE *in, nsd_options_t* opt,
1211 	struct diff_read_data* data, struct diff_log** log,
1212 	size_t child_count)
1213 {
1214 	char zone_buf[3072];
1215 	char log_buf[5120];
1216 	uint32_t old_serial, new_serial, num_parts;
1217 	uint16_t id;
1218 	uint8_t committed;
1219 	struct diff_zone *zp;
1220 	uint32_t i;
1221 	int have_all_parts = 1;
1222 	struct diff_log* thislog = 0;
1223 	off_t commitpos;
1224 
1225 	/* read zone name and serial */
1226 	if(!diff_read_str(in, zone_buf, sizeof(zone_buf)) ||
1227 		!diff_read_32(in, &old_serial) ||
1228 		!diff_read_32(in, &new_serial) ||
1229 		!diff_read_16(in, &id) ||
1230 		!diff_read_32(in, &num_parts)) {
1231 		log_msg(LOG_ERR, "diff file bad commit part");
1232 		return 0;
1233 	}
1234 	commitpos = ftello(in); /* position of commit byte */
1235 	if(commitpos == -1) {
1236 		log_msg(LOG_INFO, "could not ftello: %s.", strerror(errno));
1237 		return 0;
1238 	}
1239 	if(!diff_read_8(in, &committed) ||
1240 		!diff_read_str(in, log_buf, sizeof(log_buf)) )
1241 	{
1242 		log_msg(LOG_ERR, "diff file bad commit part");
1243 		return 0;
1244 	}
1245 
1246 	if(log) {
1247 		thislog = (struct diff_log*)region_alloc(db->region, sizeof(struct diff_log));
1248 		if(!thislog) {
1249 			log_msg(LOG_ERR, "out of memory, %s:%d", __FILE__, __LINE__);
1250 			exit(1);
1251 		}
1252 		thislog->zone_name = region_strdup(db->region, zone_buf);
1253 		thislog->comment = region_strdup(db->region, log_buf);
1254 		thislog->error = 0;
1255 		thislog->next = *log;
1256 		*log = thislog;
1257 	}
1258 
1259 	/* has been read in completely */
1260 	zp = diff_read_find_zone(data, zone_buf);
1261 	if(!zp) {
1262 		log_msg(LOG_ERR, "diff file commit without IXFR");
1263 		if(thislog)
1264 			thislog->error = "error no IXFR parts";
1265 		return 1;
1266 	}
1267 	if(committed && check_for_bad_serial(db, zone_buf, old_serial)) {
1268 		DEBUG(DEBUG_XFRD,1, (LOG_ERR,
1269 			"skipping diff file commit with bad serial"));
1270 		zp->parts->root = RBTREE_NULL;
1271 		zp->parts->count = 0;
1272 		if(thislog)
1273 			thislog->error = "error bad serial";
1274 		return 1;
1275 	}
1276 	for(i=0; i<num_parts; i++) {
1277 		struct diff_xfrpart *xp = diff_read_find_part(zp, i);
1278 		if(!xp || xp->id != id || xp->new_serial != new_serial) {
1279 			have_all_parts = 0;
1280 		}
1281 	}
1282 	if(!have_all_parts) {
1283 		DEBUG(DEBUG_XFRD,1, (LOG_ERR,
1284 			"skipping diff file commit without all parts"));
1285 		if(thislog)
1286 			thislog->error = "error missing parts";
1287 	}
1288 
1289 	if(committed && have_all_parts)
1290 	{
1291 		int is_axfr=0, delete_mode=0, rr_count=0;
1292 		off_t resume_pos;
1293 
1294 		DEBUG(DEBUG_XFRD,1, (LOG_INFO, "processing xfr: %s", log_buf));
1295 
1296 #ifdef NSEC3
1297 #ifndef FULL_PREHASH
1298 		struct region *region;
1299 		dname_type const *zone_dname;
1300 		struct zone *zone;
1301 
1302 		region = region_create(xalloc, free);
1303 		if (region == NULL) {
1304 			log_msg(LOG_ERR, "out of memory");
1305 			return 0;
1306 		}
1307 		zone_dname = dname_parse(region, zone_buf);
1308 		if (zone_dname == NULL) {
1309 			log_msg(LOG_ERR, "out of memory");
1310 		        region_destroy(region);
1311 			return 0;
1312 		}
1313 		zone = find_zone(db, zone_dname, opt, child_count);
1314 		region_destroy(region);
1315 		if (zone == NULL) {
1316 			log_msg(LOG_ERR, "no zone exists");
1317 			/* just stop trying applying ixfr */
1318 			return 1;
1319 		}
1320 		if (0 != namedb_nsec3_mod_domains_create(db)) {
1321 			log_msg(LOG_ERR,
1322 				"unable to allocate space "
1323 				"for modified NSEC3 domains");
1324 			return 0;
1325 		}
1326 #endif /* !FULL_PREHASH */
1327 #endif /* NSEC3 */
1328 
1329 		resume_pos = ftello(in);
1330 		if(resume_pos == -1) {
1331 			log_msg(LOG_INFO, "could not ftello: %s.", strerror(errno));
1332 			return 0;
1333 		}
1334 		for(i=0; i<num_parts; i++) {
1335 			struct diff_xfrpart *xp = diff_read_find_part(zp, i);
1336 			int ret;
1337 			DEBUG(DEBUG_XFRD,2, (LOG_INFO, "processing xfr: apply part %d", (int)i));
1338 			ret = apply_ixfr(db, in, &xp->file_pos, zone_buf, new_serial, opt,
1339 				id, xp->seq_nr, num_parts, &is_axfr, &delete_mode,
1340 				&rr_count, child_count);
1341 			if(ret == 0) {
1342 				log_msg(LOG_ERR, "bad ixfr packet part %d in %s", (int)i,
1343 					opt->difffile);
1344 				mark_and_exit(opt, in, commitpos, log_buf);
1345 			} else if(ret == 2) {
1346 				break;
1347 			}
1348 		}
1349 #ifdef NSEC3
1350 #ifndef FULL_PREHASH
1351 		if (is_axfr != 0)
1352 			prehash_zone(db, zone);
1353 		else
1354 			prehash_zone_incremental(db, zone);
1355 #endif /* !FULL_PREHASH */
1356 #endif /* NSEC3 */
1357 
1358 		if(fseeko(in, resume_pos, SEEK_SET) == -1) {
1359 			log_msg(LOG_INFO, "could not fseeko: %s.", strerror(errno));
1360 			return 0;
1361 		}
1362 	}
1363 	else {
1364 	 	DEBUG(DEBUG_XFRD,1, (LOG_INFO, "skipping xfr: %s", log_buf));
1365 	}
1366 
1367 	/* clean out the parts for the zone after the commit/rollback */
1368 	zp->parts->root = RBTREE_NULL;
1369 	zp->parts->count = 0;
1370 	return 1;
1371 }
1372 
1373 static int
1374 store_ixfr_data(FILE *in, uint32_t len, struct diff_read_data* data, off_t* startpos)
1375 {
1376 	char zone_name[3072];
1377 	struct diff_zone* zp;
1378 	struct diff_xfrpart* xp;
1379 	uint32_t new_serial, seq;
1380 	uint16_t id;
1381 	if(!diff_read_str(in, zone_name, sizeof(zone_name)) ||
1382 		!diff_read_32(in, &new_serial) ||
1383 		!diff_read_16(in, &id) ||
1384 		!diff_read_32(in, &seq)) {
1385 		log_msg(LOG_INFO, "could not read ixfr store info: file format error");
1386 		return 0;
1387 	}
1388 	len -= sizeof(uint32_t)*3 + sizeof(uint16_t) + strlen(zone_name);
1389 	if(fseeko(in, len, SEEK_CUR) == -1)
1390 		log_msg(LOG_INFO, "fseek failed: %s", strerror(errno));
1391 	/* store the info */
1392 	zp = diff_read_find_zone(data, zone_name);
1393 	if(!zp)
1394 		zp = diff_read_insert_zone(data, zone_name);
1395 	xp = diff_read_find_part(zp, seq);
1396 	if(xp) {
1397 		log_msg(LOG_INFO, "discarding partial xfr part: %s %d", zone_name, seq);
1398 		/* overwrite with newer value (which probably relates to next commit) */
1399 	}
1400 	else {
1401 		xp = diff_read_insert_part(data, zp, seq);
1402 	}
1403 	xp->new_serial = new_serial;
1404 	xp->id = id;
1405 	memmove(&xp->file_pos, startpos, sizeof(off_t));
1406 	return 1;
1407 }
1408 
1409 static int
1410 read_process_part(namedb_type* db, FILE *in, uint32_t type,
1411 	nsd_options_t* opt, struct diff_read_data* data,
1412 	struct diff_log** log, size_t child_count, off_t* startpos)
1413 {
1414 	uint32_t len, len2;
1415 
1416 	/* read length */
1417 	if(!diff_read_32(in, &len))
1418 		return 1;
1419 	/* read content */
1420 	if(type == DIFF_PART_IXFR) {
1421 		DEBUG(DEBUG_XFRD,2, (LOG_INFO, "part IXFR len %d", len));
1422 		if(!store_ixfr_data(in, len, data, startpos))
1423 			return 0;
1424 	}
1425 	else if(type == DIFF_PART_SURE) {
1426 		DEBUG(DEBUG_XFRD,2, (LOG_INFO, "part SURE len %d", len));
1427 		if(!read_sure_part(db, in, opt, data, log, child_count))
1428 			return 0;
1429 	} else {
1430 		DEBUG(DEBUG_XFRD,1, (LOG_INFO, "unknown part %x len %d", type, len));
1431 		return 0;
1432 	}
1433 	/* read length */
1434 	if(!diff_read_32(in, &len2))
1435 		return 1; /* short read is OK */
1436 	/* verify length */
1437 	if(len != len2)
1438 		return 0; /* bad data is wrong */
1439 	return 1;
1440 }
1441 
1442 /*
1443  * Finds smallest offset in data structs
1444  * returns 0 if no offsets in the data structs.
1445  */
1446 static int
1447 find_smallest_offset(struct diff_read_data* data, off_t* offset)
1448 {
1449 	int found_any = 0;
1450 	struct diff_zone* dz;
1451 	struct diff_xfrpart* dx;
1452 	off_t mem_offset, mem_fpos;
1453 
1454 	if(!data || !data->zones)
1455 		return 0;
1456 	RBTREE_FOR(dz, struct diff_zone*, data->zones)
1457 	{
1458 		if(!dz->parts)
1459 			continue;
1460 		RBTREE_FOR(dx, struct diff_xfrpart*, dz->parts)
1461 		{
1462 			memmove(&mem_fpos, &dx->file_pos, sizeof(off_t));
1463 
1464 			if(found_any) {
1465 				memmove(&mem_offset, offset, sizeof(off_t));
1466 
1467 				if(mem_fpos < mem_offset)
1468 					memmove(offset, &mem_fpos, sizeof(off_t));
1469 			} else {
1470 				found_any = 1;
1471 				memmove(offset, &mem_fpos, sizeof(off_t));
1472 			}
1473 		}
1474 	}
1475 
1476 	return found_any;
1477 }
1478 
1479 int
1480 diff_read_file(namedb_type* db, nsd_options_t* opt, struct diff_log** log,
1481 	size_t child_count)
1482 {
1483 	const char* filename = opt->difffile;
1484 	FILE *df;
1485 	uint32_t type, timestamp[2], curr_timestamp[2];
1486 	struct diff_read_data* data = diff_read_data_create();
1487 	off_t startpos;
1488 
1489 	df = fopen(filename, "r");
1490 	if(!df) {
1491 		DEBUG(DEBUG_XFRD,1, (LOG_INFO, "could not open file %s for reading: %s",
1492 			filename, strerror(errno)));
1493 		region_destroy(data->region);
1494 		return 1;
1495 	}
1496 
1497 	/* check timestamp */
1498 	curr_timestamp[0] = (uint32_t) db->diff_timestamp.tv_sec;
1499 	curr_timestamp[1] = (uint32_t) db->diff_timestamp.tv_usec;
1500 
1501 	if(!diff_read_32(df, &type)) {
1502 		DEBUG(DEBUG_XFRD,1, (LOG_INFO, "difffile %s is empty",
1503 			filename));
1504 		db->diff_skip = 0;
1505 		db->diff_pos = 0;
1506 	}
1507 	else if (!diff_read_32(df, &timestamp[0]) ||
1508 		 !diff_read_32(df, &timestamp[1])) {
1509 		log_msg(LOG_ERR, "difffile %s bad first part: no timestamp",
1510 			filename);
1511 		region_destroy(data->region);
1512 		return 0;
1513 	}
1514 	else if (curr_timestamp[0] != timestamp[0] ||
1515 		 curr_timestamp[1] != timestamp[1]) {
1516 		/* new timestamp, no skipping */
1517 		db->diff_timestamp.tv_sec = (time_t) timestamp[0];
1518 		db->diff_timestamp.tv_usec = (suseconds_t) timestamp[1];
1519 
1520 		if (db->diff_skip) {
1521 			DEBUG(DEBUG_XFRD,1, (LOG_INFO, "new timestamp on "
1522 				"difffile %s, restoring diff_skip and diff_pos "
1523 				"[old timestamp: %u.%u; new timestamp: %u.%u]",
1524 				filename, curr_timestamp[0], curr_timestamp[1],
1525 				timestamp[0], timestamp[1]));
1526 			db->diff_skip = 0;
1527 			db->diff_pos = 0;
1528 		}
1529 	}
1530 
1531 	/* Always seek, to diff_pos or to beginning of the file. */
1532 	if (fseeko(df, 0, SEEK_SET)==-1) {
1533 		log_msg(LOG_INFO, "could not fseeko file %s: %s.", filename,
1534 				strerror(errno));
1535 		region_destroy(data->region);
1536 		return 0;
1537 	}
1538 	if(db->diff_skip) {
1539 		DEBUG(DEBUG_XFRD,1, (LOG_INFO, "skip diff file"));
1540 		if(fseeko(df, db->diff_pos, SEEK_SET)==-1) {
1541 			log_msg(LOG_INFO, "could not fseeko file %s: %s. "
1542 					  "Reread from start.", filename,
1543 				strerror(errno));
1544 		}
1545 	}
1546 
1547 	startpos = ftello(df);
1548 	if(startpos == -1) {
1549 		log_msg(LOG_INFO, "could not ftello: %s.", strerror(errno));
1550 		region_destroy(data->region);
1551 		return 0;
1552 	}
1553 
1554 	DEBUG(DEBUG_XFRD,1, (LOG_INFO, "start of diff file read at pos %u",
1555 		(uint32_t) db->diff_pos));
1556 	while(diff_read_32(df, &type))
1557 	{
1558 		DEBUG(DEBUG_XFRD,2, (LOG_INFO, "iter loop"));
1559 
1560 		/* read timestamp */
1561 		if(!diff_read_32(df, &timestamp[0]) ||
1562 			!diff_read_32(df, &timestamp[1])) {
1563 			log_msg(LOG_INFO, "could not read timestamp: %s.",
1564 				strerror(errno));
1565 			region_destroy(data->region);
1566 			return 0;
1567 		}
1568 
1569 		if(!read_process_part(db, df, type, opt, data, log,
1570 			child_count, &startpos))
1571 		{
1572 			log_msg(LOG_INFO, "error processing diff file");
1573 			region_destroy(data->region);
1574 			return 0;
1575 		}
1576 		startpos = ftello(df);
1577 		if(startpos == -1) {
1578 			log_msg(LOG_INFO, "could not ftello: %s.", strerror(errno));
1579 			region_destroy(data->region);
1580 			return 0;
1581 		}
1582 	}
1583 	DEBUG(DEBUG_XFRD,1, (LOG_INFO, "end of diff file read"));
1584 
1585 	if(find_smallest_offset(data, &db->diff_pos)) {
1586 		/* can skip to the first unused element */
1587 		DEBUG(DEBUG_XFRD,2, (LOG_INFO, "next time skip diff file"));
1588 		db->diff_skip = 1;
1589 	} else {
1590 		/* all processed, can skip to here next time */
1591 		DEBUG(DEBUG_XFRD,2, (LOG_INFO, "next time skip diff file"));
1592 		db->diff_skip = 1;
1593 		db->diff_pos = ftello(df);
1594 		if(db->diff_pos == -1) {
1595 			log_msg(LOG_INFO, "could not ftello: %s.",
1596 				strerror(errno));
1597 			db->diff_skip = 0;
1598 		}
1599 	}
1600 
1601 	region_destroy(data->region);
1602 	fclose(df);
1603 	return 1;
1604 }
1605 
1606 static int diff_broken(FILE *df, off_t* break_pos)
1607 {
1608 	uint32_t type, len, len2;
1609 	*break_pos = ftello(df);
1610 
1611 	/* try to read and validate parts of the file */
1612 	while(diff_read_32(df, &type)) /* cannot read type is no error, normal EOF */
1613 	{
1614 		/* check type */
1615 		if(type != DIFF_PART_IXFR && type != DIFF_PART_SURE)
1616 			return 1;
1617 		/* check length */
1618 		if(!diff_read_32(df, &len))
1619 			return 1; /* EOF inside the part is error */
1620 		if(fseeko(df, len, SEEK_CUR) == -1)
1621 		{
1622 			log_msg(LOG_INFO, "fseeko failed: %s", strerror(errno));
1623 			return 1;
1624 		}
1625 		/* fseek clears EOF flag, but try reading length value,
1626 		   if EOF, the part is truncated */
1627 		if(!diff_read_32(df, &len2))
1628 			return 1;
1629 		if(len != len2)
1630 			return 1; /* bad part, lengths must agree */
1631 		/* this part is ok */
1632 		*break_pos = ftello(df);
1633 	}
1634 	return 0;
1635 }
1636 
1637 void diff_snip_garbage(namedb_type* db, nsd_options_t* opt)
1638 {
1639 	off_t break_pos;
1640 	const char* filename = opt->difffile;
1641 	FILE *df;
1642 
1643 	/* open file here and keep open, so it cannot change under our nose */
1644 	df = fopen(filename, "r+");
1645 	if(!df) {
1646 		DEBUG(DEBUG_XFRD,1, (LOG_INFO, "could not open file %s for garbage collecting: %s",
1647 			filename, strerror(errno)));
1648 		return;
1649 	}
1650 	/* and skip into file, since nsd does not read anything before the pos */
1651 	if(db->diff_skip) {
1652 		DEBUG(DEBUG_XFRD,1, (LOG_INFO, "garbage collect skip diff file"));
1653 		if(fseeko(df, db->diff_pos, SEEK_SET)==-1) {
1654 			log_msg(LOG_INFO, "could not fseeko file %s: %s.",
1655 				filename, strerror(errno));
1656 			fclose(df);
1657 			return;
1658 		}
1659 	}
1660 
1661 	/* detect break point */
1662 	if(diff_broken(df, &break_pos))
1663 	{
1664 		/* snip off at break_pos */
1665 		DEBUG(DEBUG_XFRD,1, (LOG_INFO, "snipping off trailing partial part of %s",
1666 			filename));
1667 		if(ftruncate(fileno(df), break_pos) == -1)
1668 			log_msg(LOG_ERR, "ftruncate %s failed: %s",
1669 				filename, strerror(errno));
1670 	}
1671 
1672 	fclose(df);
1673 }
1674