xref: /openbsd-src/usr.sbin/nsd/xfrd-disk.c (revision bf87c3c07c3ad89262e2b8cae09f17e70aa9e1ee)
162ac0c33Sjakob /*
262ac0c33Sjakob  * xfrd-disk.c - XFR (transfer) Daemon TCP system source file. Read/Write state to disk.
362ac0c33Sjakob  *
4dd5b221eSsthen  * Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
562ac0c33Sjakob  *
662ac0c33Sjakob  * See LICENSE for the license.
762ac0c33Sjakob  *
862ac0c33Sjakob  */
962ac0c33Sjakob 
10aee1b7aaSsthen #include "config.h"
1162ac0c33Sjakob #include <stdio.h>
1262ac0c33Sjakob #include <stdlib.h>
1362ac0c33Sjakob #include <ctype.h>
1462ac0c33Sjakob #include <errno.h>
15dd5b221eSsthen #include <sys/stat.h>
16dd5b221eSsthen #include <sys/types.h>
17dd5b221eSsthen #include <unistd.h>
1862ac0c33Sjakob #include "xfrd-disk.h"
1962ac0c33Sjakob #include "xfrd.h"
2062ac0c33Sjakob #include "buffer.h"
2162ac0c33Sjakob #include "nsd.h"
2262ac0c33Sjakob #include "options.h"
2362ac0c33Sjakob 
2462ac0c33Sjakob /* quick tokenizer, reads words separated by whitespace.
2562ac0c33Sjakob    No quoted strings. Comments are skipped (#... eol). */
2662ac0c33Sjakob static char*
xfrd_read_token(FILE * in)2762ac0c33Sjakob xfrd_read_token(FILE* in)
2862ac0c33Sjakob {
2962ac0c33Sjakob 	static char buf[4000];
3062ac0c33Sjakob 	buf[sizeof(buf)-1]=0;
3162ac0c33Sjakob 	while(1) {
3262ac0c33Sjakob 		if(fscanf(in, " %3990s", buf) != 1)
3362ac0c33Sjakob 			return 0;
3462ac0c33Sjakob 
3562ac0c33Sjakob 		if(buf[0] != '#')
3662ac0c33Sjakob 			return buf;
3762ac0c33Sjakob 
3862ac0c33Sjakob 		if(!fgets(buf, sizeof(buf), in))
3962ac0c33Sjakob 			return 0;
4062ac0c33Sjakob 	}
4162ac0c33Sjakob }
4262ac0c33Sjakob 
4362ac0c33Sjakob static int
xfrd_read_i16(FILE * in,uint16_t * v)4462ac0c33Sjakob xfrd_read_i16(FILE *in, uint16_t* v)
4562ac0c33Sjakob {
4662ac0c33Sjakob 	char* p = xfrd_read_token(in);
4762ac0c33Sjakob 	if(!p)
4862ac0c33Sjakob 		return 0;
4962ac0c33Sjakob 
5062ac0c33Sjakob 	*v=atoi(p);
5162ac0c33Sjakob 	return 1;
5262ac0c33Sjakob }
5362ac0c33Sjakob 
5462ac0c33Sjakob static int
xfrd_read_i32(FILE * in,uint32_t * v)5562ac0c33Sjakob xfrd_read_i32(FILE *in, uint32_t* v)
5662ac0c33Sjakob {
5762ac0c33Sjakob 	char* p = xfrd_read_token(in);
5862ac0c33Sjakob 	if(!p)
5962ac0c33Sjakob 		return 0;
6062ac0c33Sjakob 
6162ac0c33Sjakob 	*v=atoi(p);
6262ac0c33Sjakob 	return 1;
6362ac0c33Sjakob }
6462ac0c33Sjakob 
6562ac0c33Sjakob static int
xfrd_read_time_t(FILE * in,time_t * v)6662ac0c33Sjakob xfrd_read_time_t(FILE *in, time_t* v)
6762ac0c33Sjakob {
6862ac0c33Sjakob 	char* p = xfrd_read_token(in);
6962ac0c33Sjakob 	if(!p)
7062ac0c33Sjakob 		return 0;
7162ac0c33Sjakob 
7262ac0c33Sjakob 	*v=atol(p);
7362ac0c33Sjakob 	return 1;
7462ac0c33Sjakob }
7562ac0c33Sjakob 
7662ac0c33Sjakob static int
xfrd_read_check_str(FILE * in,const char * str)7762ac0c33Sjakob xfrd_read_check_str(FILE* in, const char* str)
7862ac0c33Sjakob {
7962ac0c33Sjakob 	char *p = xfrd_read_token(in);
8062ac0c33Sjakob 	if(!p)
8162ac0c33Sjakob 		return 0;
8262ac0c33Sjakob 
8362ac0c33Sjakob 	if(strcmp(p, str) != 0)
8462ac0c33Sjakob 		return 0;
8562ac0c33Sjakob 
8662ac0c33Sjakob 	return 1;
8762ac0c33Sjakob }
8862ac0c33Sjakob 
8962ac0c33Sjakob static int
xfrd_read_state_soa(FILE * in,const char * id_acquired,const char * id,xfrd_soa_type * soa,time_t * soatime)9062ac0c33Sjakob xfrd_read_state_soa(FILE* in, const char* id_acquired,
91fe5fe5f6Sflorian 	const char* id, xfrd_soa_type* soa, time_t* soatime)
9262ac0c33Sjakob {
9362ac0c33Sjakob 	char *p;
9462ac0c33Sjakob 
9562ac0c33Sjakob 	if(!xfrd_read_check_str(in, id_acquired) ||
9662ac0c33Sjakob 	   !xfrd_read_time_t(in, soatime)) {
9762ac0c33Sjakob 		return 0;
9862ac0c33Sjakob 	}
9962ac0c33Sjakob 
10062ac0c33Sjakob 	if(*soatime == 0)
10162ac0c33Sjakob 		return 1;
10262ac0c33Sjakob 
10362ac0c33Sjakob 	if(!xfrd_read_check_str(in, id) ||
10462ac0c33Sjakob 	   !xfrd_read_i16(in, &soa->type) ||
10562ac0c33Sjakob 	   !xfrd_read_i16(in, &soa->klass) ||
10662ac0c33Sjakob 	   !xfrd_read_i32(in, &soa->ttl) ||
10762ac0c33Sjakob 	   !xfrd_read_i16(in, &soa->rdata_count))
10862ac0c33Sjakob 	{
10962ac0c33Sjakob 		return 0;
11062ac0c33Sjakob 	}
11162ac0c33Sjakob 
11262ac0c33Sjakob 	soa->type = htons(soa->type);
11362ac0c33Sjakob 	soa->klass = htons(soa->klass);
11462ac0c33Sjakob 	soa->ttl = htonl(soa->ttl);
11562ac0c33Sjakob 	soa->rdata_count = htons(soa->rdata_count);
11662ac0c33Sjakob 
11762ac0c33Sjakob 	if(!(p=xfrd_read_token(in)) ||
11862ac0c33Sjakob 	   !(soa->prim_ns[0] = dname_parse_wire(soa->prim_ns+1, p)))
11962ac0c33Sjakob 		return 0;
12062ac0c33Sjakob 
12162ac0c33Sjakob 	if(!(p=xfrd_read_token(in)) ||
12262ac0c33Sjakob 	   !(soa->email[0] = dname_parse_wire(soa->email+1, p)))
12362ac0c33Sjakob 		return 0;
12462ac0c33Sjakob 
12562ac0c33Sjakob 	if(!xfrd_read_i32(in, &soa->serial) ||
12662ac0c33Sjakob 	   !xfrd_read_i32(in, &soa->refresh) ||
12762ac0c33Sjakob 	   !xfrd_read_i32(in, &soa->retry) ||
12862ac0c33Sjakob 	   !xfrd_read_i32(in, &soa->expire) ||
12962ac0c33Sjakob 	   !xfrd_read_i32(in, &soa->minimum))
13062ac0c33Sjakob 	{
13162ac0c33Sjakob 		return 0;
13262ac0c33Sjakob 	}
13362ac0c33Sjakob 
13462ac0c33Sjakob 	soa->serial = htonl(soa->serial);
13562ac0c33Sjakob 	soa->refresh = htonl(soa->refresh);
13662ac0c33Sjakob 	soa->retry = htonl(soa->retry);
13762ac0c33Sjakob 	soa->expire = htonl(soa->expire);
13862ac0c33Sjakob 	soa->minimum = htonl(soa->minimum);
13962ac0c33Sjakob 	return 1;
14062ac0c33Sjakob }
14162ac0c33Sjakob 
14262ac0c33Sjakob void
xfrd_read_state(struct xfrd_state * xfrd)14362ac0c33Sjakob xfrd_read_state(struct xfrd_state* xfrd)
14462ac0c33Sjakob {
14562ac0c33Sjakob 	const char* statefile = xfrd->nsd->options->xfrdfile;
14662ac0c33Sjakob 	FILE *in;
14762ac0c33Sjakob 	uint32_t filetime = 0;
14862ac0c33Sjakob 	uint32_t numzones, i;
14962ac0c33Sjakob 	region_type *tempregion;
1506e9bf1eeSflorian 	time_t soa_refresh;
15162ac0c33Sjakob 
15262ac0c33Sjakob 	tempregion = region_create(xalloc, free);
15362ac0c33Sjakob 	if(!tempregion)
15462ac0c33Sjakob 		return;
15562ac0c33Sjakob 
15662ac0c33Sjakob 	in = fopen(statefile, "r");
15762ac0c33Sjakob 	if(!in) {
15862ac0c33Sjakob 		if(errno != ENOENT) {
15962ac0c33Sjakob 			log_msg(LOG_ERR, "xfrd: Could not open file %s for reading: %s",
16062ac0c33Sjakob 				statefile, strerror(errno));
16162ac0c33Sjakob 		} else {
16262ac0c33Sjakob 			DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: no file %s. refreshing all zones.",
16362ac0c33Sjakob 				statefile));
16462ac0c33Sjakob 		}
16562ac0c33Sjakob 		region_destroy(tempregion);
16662ac0c33Sjakob 		return;
16762ac0c33Sjakob 	}
1686a6b9a23Sflorian 	if(!xfrd_read_check_str(in, XFRD_FILE_MAGIC)) {
1696a6b9a23Sflorian 		/* older file version; reset everything */
1706a6b9a23Sflorian 		DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: file %s is old version. refreshing all zones.",
1716a6b9a23Sflorian 			statefile));
1726a6b9a23Sflorian 		fclose(in);
1736a6b9a23Sflorian 		region_destroy(tempregion);
1746a6b9a23Sflorian 		return;
1756a6b9a23Sflorian 	}
1766a6b9a23Sflorian 	if(!xfrd_read_check_str(in, "filetime:") ||
17762ac0c33Sjakob 	   !xfrd_read_i32(in, &filetime) ||
17862ac0c33Sjakob 	   (time_t)filetime > xfrd_time()+15 ||
17962ac0c33Sjakob 	   !xfrd_read_check_str(in, "numzones:") ||
18062ac0c33Sjakob 	   !xfrd_read_i32(in, &numzones))
18162ac0c33Sjakob 	{
182e3932aeeSsthen 		log_msg(LOG_ERR, "xfrd: corrupt state file %s dated %d (now=%lld)",
183e3932aeeSsthen 			statefile, (int)filetime, (long long)xfrd_time());
18462ac0c33Sjakob 		fclose(in);
18562ac0c33Sjakob 		region_destroy(tempregion);
18662ac0c33Sjakob 		return;
18762ac0c33Sjakob 	}
18862ac0c33Sjakob 
18962ac0c33Sjakob 	for(i=0; i<numzones; i++) {
19062ac0c33Sjakob 		char *p;
191fe5fe5f6Sflorian 		xfrd_zone_type* zone;
19262ac0c33Sjakob 		const dname_type* dname;
1936a6b9a23Sflorian 		uint32_t state, masnum, nextmas, round_num, timeout, backoff;
194fe5fe5f6Sflorian 		xfrd_soa_type soa_nsd_read, soa_disk_read, soa_notified_read;
19562ac0c33Sjakob 		time_t soa_nsd_acquired_read,
19662ac0c33Sjakob 			soa_disk_acquired_read, soa_notified_acquired_read;
197fe5fe5f6Sflorian 		xfrd_soa_type incoming_soa;
19862ac0c33Sjakob 		time_t incoming_acquired;
19962ac0c33Sjakob 
200533110e2Sbrad 		if(nsd.signal_hint_shutdown) {
201533110e2Sbrad 			fclose(in);
202533110e2Sbrad 			region_destroy(tempregion);
203533110e2Sbrad 			return;
204533110e2Sbrad 		}
205533110e2Sbrad 
20662ac0c33Sjakob 		memset(&soa_nsd_read, 0, sizeof(soa_nsd_read));
20762ac0c33Sjakob 		memset(&soa_disk_read, 0, sizeof(soa_disk_read));
20862ac0c33Sjakob 		memset(&soa_notified_read, 0, sizeof(soa_notified_read));
20962ac0c33Sjakob 
21062ac0c33Sjakob 		if(!xfrd_read_check_str(in, "zone:") ||
21162ac0c33Sjakob 		   !xfrd_read_check_str(in, "name:")  ||
21262ac0c33Sjakob 		   !(p=xfrd_read_token(in)) ||
21362ac0c33Sjakob 		   !(dname = dname_parse(tempregion, p)) ||
21462ac0c33Sjakob 		   !xfrd_read_check_str(in, "state:") ||
21562ac0c33Sjakob 		   !xfrd_read_i32(in, &state) || (state>2) ||
21662ac0c33Sjakob 		   !xfrd_read_check_str(in, "master:") ||
21762ac0c33Sjakob 		   !xfrd_read_i32(in, &masnum) ||
21862ac0c33Sjakob 		   !xfrd_read_check_str(in, "next_master:") ||
21962ac0c33Sjakob 		   !xfrd_read_i32(in, &nextmas) ||
22062ac0c33Sjakob 		   !xfrd_read_check_str(in, "round_num:") ||
22162ac0c33Sjakob 		   !xfrd_read_i32(in, &round_num) ||
22262ac0c33Sjakob 		   !xfrd_read_check_str(in, "next_timeout:") ||
22362ac0c33Sjakob 		   !xfrd_read_i32(in, &timeout) ||
2246a6b9a23Sflorian 		   !xfrd_read_check_str(in, "backoff:") ||
2256a6b9a23Sflorian 		   !xfrd_read_i32(in, &backoff) ||
22662ac0c33Sjakob 		   !xfrd_read_state_soa(in, "soa_nsd_acquired:", "soa_nsd:",
22762ac0c33Sjakob 			&soa_nsd_read, &soa_nsd_acquired_read) ||
22862ac0c33Sjakob 		   !xfrd_read_state_soa(in, "soa_disk_acquired:", "soa_disk:",
22962ac0c33Sjakob 			&soa_disk_read, &soa_disk_acquired_read) ||
23062ac0c33Sjakob 		   !xfrd_read_state_soa(in, "soa_notify_acquired:", "soa_notify:",
23162ac0c33Sjakob 			&soa_notified_read, &soa_notified_acquired_read))
23262ac0c33Sjakob 		{
233e3932aeeSsthen 			log_msg(LOG_ERR, "xfrd: corrupt state file %s dated %d (now=%lld)",
234e3932aeeSsthen 				statefile, (int)filetime, (long long)xfrd_time());
23562ac0c33Sjakob 			fclose(in);
23662ac0c33Sjakob 			region_destroy(tempregion);
23762ac0c33Sjakob 			return;
23862ac0c33Sjakob 		}
23962ac0c33Sjakob 
240fe5fe5f6Sflorian 		zone = (xfrd_zone_type*)rbtree_search(xfrd->zones, dname);
24162ac0c33Sjakob 		if(!zone) {
24262ac0c33Sjakob 			DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: state file has info for not configured zone %s", p));
24362ac0c33Sjakob 			continue;
24462ac0c33Sjakob 		}
24562ac0c33Sjakob 
24662ac0c33Sjakob 		if(soa_nsd_acquired_read>xfrd_time()+15 ||
24762ac0c33Sjakob 			soa_disk_acquired_read>xfrd_time()+15 ||
24862ac0c33Sjakob 			soa_notified_acquired_read>xfrd_time()+15)
24962ac0c33Sjakob 		{
25062ac0c33Sjakob 			log_msg(LOG_ERR, "xfrd: statefile %s contains"
25162ac0c33Sjakob 				" times in the future for zone %s. Ignoring.",
25262ac0c33Sjakob 				statefile, zone->apex_str);
25362ac0c33Sjakob 			continue;
25462ac0c33Sjakob 		}
25562ac0c33Sjakob 		zone->state = state;
25662ac0c33Sjakob 		zone->master_num = masnum;
25762ac0c33Sjakob 		zone->next_master = nextmas;
25862ac0c33Sjakob 		zone->round_num = round_num;
25962ac0c33Sjakob 		zone->timeout.tv_sec = timeout;
260dd5b221eSsthen 		zone->timeout.tv_usec = 0;
2616a6b9a23Sflorian 		zone->fresh_xfr_timeout = backoff*XFRD_TRANSFER_TIMEOUT_START;
26262ac0c33Sjakob 
26362ac0c33Sjakob 		/* read the zone OK, now set the master properly */
264dd5b221eSsthen 		zone->master = acl_find_num(zone->zone_options->pattern->
265dd5b221eSsthen 			request_xfr, zone->master_num);
26662ac0c33Sjakob 		if(!zone->master) {
267*bf87c3c0Sflorian 			DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: primaries changed for zone %s",
26862ac0c33Sjakob 				zone->apex_str));
269dd5b221eSsthen 			zone->master = zone->zone_options->pattern->request_xfr;
27062ac0c33Sjakob 			zone->master_num = 0;
27162ac0c33Sjakob 			zone->round_num = 0;
27262ac0c33Sjakob 		}
27362ac0c33Sjakob 
27462ac0c33Sjakob 		/*
27562ac0c33Sjakob 		 * There is no timeout,
27662ac0c33Sjakob 		 * or there is a notification,
27762ac0c33Sjakob 		 * or there is a soa && current time is past refresh point
27862ac0c33Sjakob 		 */
2796e9bf1eeSflorian 		soa_refresh = ntohl(soa_disk_read.refresh);
280c1e73312Sflorian 		if (soa_refresh > (time_t)zone->zone_options->pattern->max_refresh_time)
2816e9bf1eeSflorian 			soa_refresh = zone->zone_options->pattern->max_refresh_time;
282c1e73312Sflorian 		else if (soa_refresh < (time_t)zone->zone_options->pattern->min_refresh_time)
2836e9bf1eeSflorian 			soa_refresh = zone->zone_options->pattern->min_refresh_time;
28462ac0c33Sjakob 		if(timeout == 0 || soa_notified_acquired_read != 0 ||
28562ac0c33Sjakob 			(soa_disk_acquired_read != 0 &&
28662ac0c33Sjakob 			(uint32_t)xfrd_time() - soa_disk_acquired_read
287c1e73312Sflorian 				> (uint32_t)soa_refresh))
28862ac0c33Sjakob 		{
28962ac0c33Sjakob 			zone->state = xfrd_zone_refreshing;
29062ac0c33Sjakob 			xfrd_set_refresh_now(zone);
29162ac0c33Sjakob 		}
292eab1363eSsthen 		if(timeout != 0 && filetime + timeout < (uint32_t)xfrd_time()) {
293eab1363eSsthen 			/* timeout is in the past, refresh the zone */
294eab1363eSsthen 			timeout = 0;
295eab1363eSsthen 			if(zone->state == xfrd_zone_ok)
296eab1363eSsthen 				zone->state = xfrd_zone_refreshing;
297eab1363eSsthen 			xfrd_set_refresh_now(zone);
298eab1363eSsthen 		}
29962ac0c33Sjakob 
30062ac0c33Sjakob 		/* There is a soa && current time is past expiry point */
30162ac0c33Sjakob 		if(soa_disk_acquired_read!=0 &&
30262ac0c33Sjakob 			(uint32_t)xfrd_time() - soa_disk_acquired_read
30362ac0c33Sjakob 				> ntohl(soa_disk_read.expire))
30462ac0c33Sjakob 		{
30562ac0c33Sjakob 			zone->state = xfrd_zone_expired;
30662ac0c33Sjakob 			xfrd_set_refresh_now(zone);
30762ac0c33Sjakob 		}
30862ac0c33Sjakob 
309533110e2Sbrad 		/* there is a zone read and it matches what we had before */
310533110e2Sbrad 		if(zone->soa_nsd_acquired && zone->state != xfrd_zone_expired
311533110e2Sbrad 			&& zone->soa_nsd.serial == soa_nsd_read.serial) {
312533110e2Sbrad 			xfrd_deactivate_zone(zone);
313533110e2Sbrad 			zone->state = state;
314ac5517e4Sflorian 			xfrd_set_timer(zone,
315ac5517e4Sflorian 				within_refresh_bounds(zone, timeout));
316533110e2Sbrad 		}
3176a6b9a23Sflorian 		if((zone->soa_nsd_acquired == 0 && soa_nsd_acquired_read == 0 &&
3186a6b9a23Sflorian 			soa_disk_acquired_read == 0) ||
3196a6b9a23Sflorian 			(zone->state != xfrd_zone_ok && timeout != 0)) {
3206a6b9a23Sflorian 			/* but don't check now, because that would mean a
3216a6b9a23Sflorian 			 * storm of attempts on some master servers */
3226a6b9a23Sflorian 			xfrd_deactivate_zone(zone);
3236a6b9a23Sflorian 			zone->state = state;
324ac5517e4Sflorian 			xfrd_set_timer(zone,
325ac5517e4Sflorian 				within_retry_bounds(zone, timeout));
326533110e2Sbrad 		}
327533110e2Sbrad 
32862ac0c33Sjakob 		/* handle as an incoming SOA. */
32962ac0c33Sjakob 		incoming_soa = zone->soa_nsd;
33062ac0c33Sjakob 		incoming_acquired = zone->soa_nsd_acquired;
33162ac0c33Sjakob 		zone->soa_nsd = soa_nsd_read;
33262ac0c33Sjakob 		zone->soa_nsd_acquired = soa_nsd_acquired_read;
3333efee2e1Sflorian 		/* use soa and soa_acquired from starting NSD, not what is stored in
3343efee2e1Sflorian 		 * the state file, because the actual zone contents trumps the contents
3353efee2e1Sflorian 		 * of this cache */
3363efee2e1Sflorian 		zone->soa_disk = incoming_soa;
3373efee2e1Sflorian 		zone->soa_disk_acquired = incoming_acquired;
3383efee2e1Sflorian 		zone->soa_notified = soa_notified_read;
33962ac0c33Sjakob 		zone->soa_notified_acquired = soa_notified_acquired_read;
34003739794Sbrad 		if (zone->state == xfrd_zone_expired)
34103739794Sbrad 		{
34203739794Sbrad 			xfrd_send_expire_notification(zone);
34303739794Sbrad 		}
3446a6b9a23Sflorian 		if(incoming_acquired != 0)
34562ac0c33Sjakob 			xfrd_handle_incoming_soa(zone, &incoming_soa, incoming_acquired);
34662ac0c33Sjakob 	}
34762ac0c33Sjakob 
34862ac0c33Sjakob 	if(!xfrd_read_check_str(in, XFRD_FILE_MAGIC)) {
349e3932aeeSsthen 		log_msg(LOG_ERR, "xfrd: corrupt state file %s dated %d (now=%lld)",
350e3932aeeSsthen 			statefile, (int)filetime, (long long)xfrd_time());
35162ac0c33Sjakob 		region_destroy(tempregion);
35262ac0c33Sjakob 		fclose(in);
35362ac0c33Sjakob 		return;
35462ac0c33Sjakob 	}
35562ac0c33Sjakob 
35612455795Ssthen 	DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: read %d zones from state file", (int)numzones));
35762ac0c33Sjakob 	fclose(in);
35862ac0c33Sjakob 	region_destroy(tempregion);
35962ac0c33Sjakob }
36062ac0c33Sjakob 
36162ac0c33Sjakob /* prints neato days hours and minutes. */
36262ac0c33Sjakob static void
neato_timeout(FILE * out,const char * str,time_t secs)363e3932aeeSsthen neato_timeout(FILE* out, const char* str, time_t secs)
36462ac0c33Sjakob {
36562ac0c33Sjakob 	fprintf(out, "%s", str);
36662ac0c33Sjakob 	if(secs <= 0) {
367e3932aeeSsthen 		fprintf(out, " %llds", (long long)secs);
36862ac0c33Sjakob 		return;
36962ac0c33Sjakob 	}
37062ac0c33Sjakob 	if(secs >= 3600*24) {
371e3932aeeSsthen 		fprintf(out, " %lldd", (long long)(secs/(3600*24)));
37262ac0c33Sjakob 		secs = secs % (3600*24);
37362ac0c33Sjakob 	}
37462ac0c33Sjakob 	if(secs >= 3600) {
375e3932aeeSsthen 		fprintf(out, " %lldh", (long long)(secs/3600));
37662ac0c33Sjakob 		secs = secs%3600;
37762ac0c33Sjakob 	}
37862ac0c33Sjakob 	if(secs >= 60) {
379e3932aeeSsthen 		fprintf(out, " %lldm", (long long)(secs/60));
38062ac0c33Sjakob 		secs = secs%60;
38162ac0c33Sjakob 	}
38262ac0c33Sjakob 	if(secs > 0) {
383e3932aeeSsthen 		fprintf(out, " %llds", (long long)secs);
38462ac0c33Sjakob 	}
38562ac0c33Sjakob }
38662ac0c33Sjakob 
xfrd_write_dname(FILE * out,uint8_t * dname)38762ac0c33Sjakob static void xfrd_write_dname(FILE* out, uint8_t* dname)
38862ac0c33Sjakob {
38962ac0c33Sjakob 	uint8_t* d= dname+1;
39062ac0c33Sjakob 	uint8_t len = *d++;
39162ac0c33Sjakob 	uint8_t i;
39262ac0c33Sjakob 
39362ac0c33Sjakob 	if(dname[0]<=1) {
39462ac0c33Sjakob 		fprintf(out, ".");
39562ac0c33Sjakob 		return;
39662ac0c33Sjakob 	}
39762ac0c33Sjakob 
39862ac0c33Sjakob 	while(len)
39962ac0c33Sjakob 	{
40062ac0c33Sjakob 		assert(d - (dname+1) <= dname[0]);
40162ac0c33Sjakob 		for(i=0; i<len; i++)
40262ac0c33Sjakob 		{
40362ac0c33Sjakob 			uint8_t ch = *d++;
404c1404d4fSbrad 			if (isalnum((unsigned char)ch) || ch == '-' || ch == '_') {
40562ac0c33Sjakob 				fprintf(out, "%c", ch);
40662ac0c33Sjakob 			} else if (ch == '.' || ch == '\\') {
40762ac0c33Sjakob 				fprintf(out, "\\%c", ch);
40862ac0c33Sjakob 			} else {
40962ac0c33Sjakob 				fprintf(out, "\\%03u", (unsigned int)ch);
41062ac0c33Sjakob 			}
41162ac0c33Sjakob 		}
41262ac0c33Sjakob 		fprintf(out, ".");
41362ac0c33Sjakob 		len = *d++;
41462ac0c33Sjakob 	}
41562ac0c33Sjakob }
41662ac0c33Sjakob 
41762ac0c33Sjakob static void
xfrd_write_state_soa(FILE * out,const char * id,xfrd_soa_type * soa,time_t soatime,const dname_type * ATTR_UNUSED (apex))41862ac0c33Sjakob xfrd_write_state_soa(FILE* out, const char* id,
419fe5fe5f6Sflorian 	xfrd_soa_type* soa, time_t soatime, const dname_type* ATTR_UNUSED(apex))
42062ac0c33Sjakob {
42162ac0c33Sjakob 	fprintf(out, "\t%s_acquired: %d", id, (int)soatime);
42262ac0c33Sjakob 	if(!soatime) {
42362ac0c33Sjakob 		fprintf(out, "\n");
42462ac0c33Sjakob 		return;
42562ac0c33Sjakob 	}
42662ac0c33Sjakob 	neato_timeout(out, "\t# was", xfrd_time()-soatime);
42762ac0c33Sjakob 	fprintf(out, " ago\n");
42862ac0c33Sjakob 
42962ac0c33Sjakob 	fprintf(out, "\t%s: %u %u %u %u", id,
43012455795Ssthen 		(unsigned)ntohs(soa->type), (unsigned)ntohs(soa->klass),
43112455795Ssthen 		(unsigned)ntohl(soa->ttl), (unsigned)ntohs(soa->rdata_count));
43262ac0c33Sjakob 	fprintf(out, " ");
43362ac0c33Sjakob 	xfrd_write_dname(out, soa->prim_ns);
43462ac0c33Sjakob 	fprintf(out, " ");
43562ac0c33Sjakob 	xfrd_write_dname(out, soa->email);
43612455795Ssthen 	fprintf(out, " %u", (unsigned)ntohl(soa->serial));
43712455795Ssthen 	fprintf(out, " %u", (unsigned)ntohl(soa->refresh));
43812455795Ssthen 	fprintf(out, " %u", (unsigned)ntohl(soa->retry));
43912455795Ssthen 	fprintf(out, " %u", (unsigned)ntohl(soa->expire));
44012455795Ssthen 	fprintf(out, " %u\n", (unsigned)ntohl(soa->minimum));
44162ac0c33Sjakob 	fprintf(out, "\t#");
44262ac0c33Sjakob 	neato_timeout(out, " refresh =", ntohl(soa->refresh));
44362ac0c33Sjakob 	neato_timeout(out, " retry =", ntohl(soa->retry));
44462ac0c33Sjakob 	neato_timeout(out, " expire =", ntohl(soa->expire));
44562ac0c33Sjakob 	neato_timeout(out, " minimum =", ntohl(soa->minimum));
44662ac0c33Sjakob 	fprintf(out, "\n");
44762ac0c33Sjakob }
44862ac0c33Sjakob 
44962ac0c33Sjakob void
xfrd_write_state(struct xfrd_state * xfrd)45062ac0c33Sjakob xfrd_write_state(struct xfrd_state* xfrd)
45162ac0c33Sjakob {
452fe5fe5f6Sflorian 	rbnode_type* p;
45362ac0c33Sjakob 	const char* statefile = xfrd->nsd->options->xfrdfile;
45462ac0c33Sjakob 	FILE *out;
45562ac0c33Sjakob 	time_t now = xfrd_time();
45662ac0c33Sjakob 
45762ac0c33Sjakob 	DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: write file %s", statefile));
45862ac0c33Sjakob 	out = fopen(statefile, "w");
45962ac0c33Sjakob 	if(!out) {
46062ac0c33Sjakob 		log_msg(LOG_ERR, "xfrd: Could not open file %s for writing: %s",
46162ac0c33Sjakob 				statefile, strerror(errno));
46262ac0c33Sjakob 		return;
46362ac0c33Sjakob 	}
46462ac0c33Sjakob 
46562ac0c33Sjakob 	fprintf(out, "%s\n", XFRD_FILE_MAGIC);
46662ac0c33Sjakob 	fprintf(out, "# This file is written on exit by nsd xfr daemon.\n");
467*bf87c3c0Sflorian 	fprintf(out, "# This file contains secondary zone information:\n");
46862ac0c33Sjakob 	fprintf(out, "# 	* timeouts (when was zone data acquired)\n");
46962ac0c33Sjakob 	fprintf(out, "# 	* state (OK, refreshing, expired)\n");
470*bf87c3c0Sflorian 	fprintf(out, "# 	* which primary transfer to attempt next\n");
47162ac0c33Sjakob 	fprintf(out, "# The file is read on start (but not on reload) by nsd xfr daemon.\n");
47262ac0c33Sjakob 	fprintf(out, "# You can edit; but do not change statement order\n");
47362ac0c33Sjakob 	fprintf(out, "# and no fancy stuff (like quoted \"strings\").\n");
47462ac0c33Sjakob 	fprintf(out, "#\n");
47562ac0c33Sjakob 	fprintf(out, "# If you remove a zone entry, it will be refreshed.\n");
47662ac0c33Sjakob 	fprintf(out, "# This can be useful for an expired zone; it revives\n");
47762ac0c33Sjakob 	fprintf(out, "# the zone temporarily, from refresh-expiry time.\n");
478*bf87c3c0Sflorian 	fprintf(out, "# If you delete the file all secondary zones are updated.\n");
47962ac0c33Sjakob 	fprintf(out, "#\n");
48062ac0c33Sjakob 	fprintf(out, "# Note: if you edit this file while nsd is running,\n");
48162ac0c33Sjakob 	fprintf(out, "#       it will be overwritten on exit by nsd.\n");
48262ac0c33Sjakob 	fprintf(out, "\n");
483e3932aeeSsthen 	fprintf(out, "filetime: %lld\t# %s\n", (long long)now, ctime(&now));
48462ac0c33Sjakob 	fprintf(out, "# The number of zone entries in this file\n");
48562ac0c33Sjakob 	fprintf(out, "numzones: %d\n", (int)xfrd->zones->count);
48662ac0c33Sjakob 	fprintf(out, "\n");
48762ac0c33Sjakob 	for(p = rbtree_first(xfrd->zones); p && p!=RBTREE_NULL; p=rbtree_next(p))
48862ac0c33Sjakob 	{
489fe5fe5f6Sflorian 		xfrd_zone_type* zone = (xfrd_zone_type*)p;
49062ac0c33Sjakob 		fprintf(out, "zone: \tname: %s\n", zone->apex_str);
49162ac0c33Sjakob 		fprintf(out, "\tstate: %d", (int)zone->state);
49262ac0c33Sjakob 		fprintf(out, " # %s", zone->state==xfrd_zone_ok?"OK":(
49362ac0c33Sjakob 			zone->state==xfrd_zone_refreshing?"refreshing":"expired"));
49462ac0c33Sjakob 		fprintf(out, "\n");
49562ac0c33Sjakob 		fprintf(out, "\tmaster: %d\n", zone->master_num);
49662ac0c33Sjakob 		fprintf(out, "\tnext_master: %d\n", zone->next_master);
49762ac0c33Sjakob 		fprintf(out, "\tround_num: %d\n", zone->round_num);
49862ac0c33Sjakob 		fprintf(out, "\tnext_timeout: %d",
499dd5b221eSsthen 			(zone->zone_handler_flags&EV_TIMEOUT)?(int)zone->timeout.tv_sec:0);
500dd5b221eSsthen 		if((zone->zone_handler_flags&EV_TIMEOUT)) {
501a302926fSbrad 			neato_timeout(out, "\t# =", zone->timeout.tv_sec);
50262ac0c33Sjakob 		}
50362ac0c33Sjakob 		fprintf(out, "\n");
5046a6b9a23Sflorian 		fprintf(out, "\tbackoff: %d\n", zone->fresh_xfr_timeout/XFRD_TRANSFER_TIMEOUT_START);
50562ac0c33Sjakob 		xfrd_write_state_soa(out, "soa_nsd", &zone->soa_nsd,
50662ac0c33Sjakob 			zone->soa_nsd_acquired, zone->apex);
50762ac0c33Sjakob 		xfrd_write_state_soa(out, "soa_disk", &zone->soa_disk,
50862ac0c33Sjakob 			zone->soa_disk_acquired, zone->apex);
50962ac0c33Sjakob 		xfrd_write_state_soa(out, "soa_notify", &zone->soa_notified,
51062ac0c33Sjakob 			zone->soa_notified_acquired, zone->apex);
51162ac0c33Sjakob 		fprintf(out, "\n");
51262ac0c33Sjakob 	}
51362ac0c33Sjakob 
51462ac0c33Sjakob 	fprintf(out, "%s\n", XFRD_FILE_MAGIC);
51562ac0c33Sjakob 	DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: written %d zones to state file",
51662ac0c33Sjakob 		(int)xfrd->zones->count));
51762ac0c33Sjakob 	fclose(out);
51862ac0c33Sjakob }
519dd5b221eSsthen 
520dd5b221eSsthen /* return tempdirname */
521dd5b221eSsthen static void
tempdirname(char * buf,size_t sz,struct nsd * nsd)522dd5b221eSsthen tempdirname(char* buf, size_t sz, struct nsd* nsd)
523dd5b221eSsthen {
524dd5b221eSsthen 	snprintf(buf, sz, "%snsd-xfr-%d",
525dd5b221eSsthen 		nsd->options->xfrdir, (int)nsd->pid);
526dd5b221eSsthen }
527dd5b221eSsthen 
528dd5b221eSsthen void
xfrd_make_tempdir(struct nsd * nsd)529dd5b221eSsthen xfrd_make_tempdir(struct nsd* nsd)
530dd5b221eSsthen {
531dd5b221eSsthen 	char tnm[1024];
532dd5b221eSsthen 	tempdirname(tnm, sizeof(tnm), nsd);
533dd5b221eSsthen 	/* create parent directories if needed (0750 permissions) */
534dd5b221eSsthen 	if(!create_dirs(tnm)) {
535dd5b221eSsthen 		log_msg(LOG_ERR, "parentdirs of %s failed", tnm);
536dd5b221eSsthen 	}
537dd5b221eSsthen 	/* restrictive permissions here, because this may be in /tmp */
538dd5b221eSsthen 	if(mkdir(tnm, 0700)==-1) {
539dd5b221eSsthen 		if(errno != EEXIST) {
540dd5b221eSsthen 			log_msg(LOG_ERR, "mkdir %s failed: %s",
541dd5b221eSsthen 				tnm, strerror(errno));
542dd5b221eSsthen 		}
543dd5b221eSsthen 	}
544dd5b221eSsthen }
545dd5b221eSsthen 
546dd5b221eSsthen void
xfrd_del_tempdir(struct nsd * nsd)547dd5b221eSsthen xfrd_del_tempdir(struct nsd* nsd)
548dd5b221eSsthen {
549dd5b221eSsthen 	char tnm[1024];
550dd5b221eSsthen 	tempdirname(tnm, sizeof(tnm), nsd);
551dd5b221eSsthen 	/* ignore parent directories, they are likely /var/tmp, /tmp or
552dd5b221eSsthen 	 * /var/cache/nsd and do not have to be deleted */
553dd5b221eSsthen 	if(rmdir(tnm)==-1 && errno != ENOENT) {
554dd5b221eSsthen 		log_msg(LOG_WARNING, "rmdir %s failed: %s", tnm,
555dd5b221eSsthen 			strerror(errno));
556dd5b221eSsthen 	}
557dd5b221eSsthen }
558dd5b221eSsthen 
559dd5b221eSsthen /* return name of xfrfile in tempdir */
560dd5b221eSsthen static void
tempxfrname(char * buf,size_t sz,struct nsd * nsd,uint64_t number)561dd5b221eSsthen tempxfrname(char* buf, size_t sz, struct nsd* nsd, uint64_t number)
562dd5b221eSsthen {
563dd5b221eSsthen 	char tnm[1024];
564dd5b221eSsthen 	tempdirname(tnm, sizeof(tnm), nsd);
565dd5b221eSsthen 	snprintf(buf, sz, "%s/xfr.%lld", tnm, (long long)number);
566dd5b221eSsthen }
567dd5b221eSsthen 
568dd5b221eSsthen FILE*
xfrd_open_xfrfile(struct nsd * nsd,uint64_t number,char * mode)569dd5b221eSsthen xfrd_open_xfrfile(struct nsd* nsd, uint64_t number, char* mode)
570dd5b221eSsthen {
571bfd0b123Sflorian 	char fname[1200];
572dd5b221eSsthen 	FILE* xfr;
573dd5b221eSsthen 	tempxfrname(fname, sizeof(fname), nsd, number);
574dd5b221eSsthen 	xfr = fopen(fname, mode);
575dd5b221eSsthen 	if(!xfr && errno == ENOENT) {
576dd5b221eSsthen 		/* directory may not exist */
577dd5b221eSsthen 		xfrd_make_tempdir(nsd);
578dd5b221eSsthen 		xfr = fopen(fname, mode);
579dd5b221eSsthen 	}
580dd5b221eSsthen 	if(!xfr) {
581dd5b221eSsthen 		log_msg(LOG_ERR, "open %s for %s failed: %s", fname, mode,
582dd5b221eSsthen 			strerror(errno));
583dd5b221eSsthen 		return NULL;
584dd5b221eSsthen 	}
585dd5b221eSsthen 	return xfr;
586dd5b221eSsthen }
587dd5b221eSsthen 
588dd5b221eSsthen void
xfrd_unlink_xfrfile(struct nsd * nsd,uint64_t number)589dd5b221eSsthen xfrd_unlink_xfrfile(struct nsd* nsd, uint64_t number)
590dd5b221eSsthen {
591bfd0b123Sflorian 	char fname[1200];
592dd5b221eSsthen 	tempxfrname(fname, sizeof(fname), nsd, number);
593dd5b221eSsthen 	if(unlink(fname) == -1) {
594dd5b221eSsthen 		log_msg(LOG_WARNING, "could not unlink %s: %s", fname,
595dd5b221eSsthen 			strerror(errno));
596dd5b221eSsthen 	}
597dd5b221eSsthen }
5986e9bf1eeSflorian 
5996e9bf1eeSflorian uint64_t
xfrd_get_xfrfile_size(struct nsd * nsd,uint64_t number)6006e9bf1eeSflorian xfrd_get_xfrfile_size(struct nsd* nsd, uint64_t number )
6016e9bf1eeSflorian {
602bfd0b123Sflorian 	char fname[1200];
6036e9bf1eeSflorian 	struct stat tempxfr_stat;
6046e9bf1eeSflorian 	tempxfrname(fname, sizeof(fname), nsd, number);
6056e9bf1eeSflorian 	if( stat( fname, &tempxfr_stat ) < 0 ) {
6066e9bf1eeSflorian 	    log_msg(LOG_WARNING, "could not get file size %s: %s", fname,
6076e9bf1eeSflorian 		    strerror(errno));
6086e9bf1eeSflorian 	    return 0;
6096e9bf1eeSflorian 	}
6106e9bf1eeSflorian 	return (uint64_t)tempxfr_stat.st_size;
6116e9bf1eeSflorian }
612