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