xref: /openbsd-src/usr.sbin/nsd/xfrd-disk.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /*
2  * xfrd-disk.c - XFR (transfer) Daemon TCP system source file. Read/Write state to disk.
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 <stdio.h>
12 #include <stdlib.h>
13 #include <ctype.h>
14 #include <errno.h>
15 #include "xfrd-disk.h"
16 #include "xfrd.h"
17 #include "buffer.h"
18 #include "nsd.h"
19 #include "options.h"
20 
21 /* quick tokenizer, reads words separated by whitespace.
22    No quoted strings. Comments are skipped (#... eol). */
23 static char*
24 xfrd_read_token(FILE* in)
25 {
26 	static char buf[4000];
27 	buf[sizeof(buf)-1]=0;
28 	while(1) {
29 		if(fscanf(in, " %3990s", buf) != 1)
30 			return 0;
31 
32 		if(buf[0] != '#')
33 			return buf;
34 
35 		if(!fgets(buf, sizeof(buf), in))
36 			return 0;
37 	}
38 }
39 
40 static int
41 xfrd_read_i16(FILE *in, uint16_t* v)
42 {
43 	char* p = xfrd_read_token(in);
44 	if(!p)
45 		return 0;
46 
47 	*v=atoi(p);
48 	return 1;
49 }
50 
51 static int
52 xfrd_read_i32(FILE *in, uint32_t* v)
53 {
54 	char* p = xfrd_read_token(in);
55 	if(!p)
56 		return 0;
57 
58 	*v=atoi(p);
59 	return 1;
60 }
61 
62 static int
63 xfrd_read_time_t(FILE *in, time_t* v)
64 {
65 	char* p = xfrd_read_token(in);
66 	if(!p)
67 		return 0;
68 
69 	*v=atol(p);
70 	return 1;
71 }
72 
73 static int
74 xfrd_read_check_str(FILE* in, const char* str)
75 {
76 	char *p = xfrd_read_token(in);
77 	if(!p)
78 		return 0;
79 
80 	if(strcmp(p, str) != 0)
81 		return 0;
82 
83 	return 1;
84 }
85 
86 static int
87 xfrd_read_state_soa(FILE* in, const char* id_acquired,
88 	const char* id, xfrd_soa_t* soa, time_t* soatime)
89 {
90 	char *p;
91 
92 	if(!xfrd_read_check_str(in, id_acquired) ||
93 	   !xfrd_read_time_t(in, soatime)) {
94 		return 0;
95 	}
96 
97 	if(*soatime == 0)
98 		return 1;
99 
100 	if(!xfrd_read_check_str(in, id) ||
101 	   !xfrd_read_i16(in, &soa->type) ||
102 	   !xfrd_read_i16(in, &soa->klass) ||
103 	   !xfrd_read_i32(in, &soa->ttl) ||
104 	   !xfrd_read_i16(in, &soa->rdata_count))
105 	{
106 		return 0;
107 	}
108 
109 	soa->type = htons(soa->type);
110 	soa->klass = htons(soa->klass);
111 	soa->ttl = htonl(soa->ttl);
112 	soa->rdata_count = htons(soa->rdata_count);
113 
114 	if(!(p=xfrd_read_token(in)) ||
115 	   !(soa->prim_ns[0] = dname_parse_wire(soa->prim_ns+1, p)))
116 		return 0;
117 
118 	if(!(p=xfrd_read_token(in)) ||
119 	   !(soa->email[0] = dname_parse_wire(soa->email+1, p)))
120 		return 0;
121 
122 	if(!xfrd_read_i32(in, &soa->serial) ||
123 	   !xfrd_read_i32(in, &soa->refresh) ||
124 	   !xfrd_read_i32(in, &soa->retry) ||
125 	   !xfrd_read_i32(in, &soa->expire) ||
126 	   !xfrd_read_i32(in, &soa->minimum))
127 	{
128 		return 0;
129 	}
130 
131 	soa->serial = htonl(soa->serial);
132 	soa->refresh = htonl(soa->refresh);
133 	soa->retry = htonl(soa->retry);
134 	soa->expire = htonl(soa->expire);
135 	soa->minimum = htonl(soa->minimum);
136 	return 1;
137 }
138 
139 void
140 xfrd_read_state(struct xfrd_state* xfrd)
141 {
142 	const char* statefile = xfrd->nsd->options->xfrdfile;
143 	FILE *in;
144 	uint32_t filetime = 0;
145 	uint32_t numzones, i;
146 	region_type *tempregion;
147 
148 	tempregion = region_create(xalloc, free);
149 	if(!tempregion)
150 		return;
151 
152 	in = fopen(statefile, "r");
153 	if(!in) {
154 		if(errno != ENOENT) {
155 			log_msg(LOG_ERR, "xfrd: Could not open file %s for reading: %s",
156 				statefile, strerror(errno));
157 		} else {
158 			DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: no file %s. refreshing all zones.",
159 				statefile));
160 		}
161 		region_destroy(tempregion);
162 		return;
163 	}
164 	if(!xfrd_read_check_str(in, XFRD_FILE_MAGIC) ||
165 	   !xfrd_read_check_str(in, "filetime:") ||
166 	   !xfrd_read_i32(in, &filetime) ||
167 	   (time_t)filetime > xfrd_time()+15 ||
168 	   !xfrd_read_check_str(in, "numzones:") ||
169 	   !xfrd_read_i32(in, &numzones))
170 	{
171 		log_msg(LOG_ERR, "xfrd: corrupt state file %s dated %d (now=%d)",
172 			statefile, (int)filetime, (int)xfrd_time());
173 		fclose(in);
174 		region_destroy(tempregion);
175 		return;
176 	}
177 
178 	for(i=0; i<numzones; i++) {
179 		char *p;
180 		xfrd_zone_t* zone;
181 		const dname_type* dname;
182 		uint32_t state, masnum, nextmas, round_num, timeout;
183 		xfrd_soa_t soa_nsd_read, soa_disk_read, soa_notified_read;
184 		time_t soa_nsd_acquired_read,
185 			soa_disk_acquired_read, soa_notified_acquired_read;
186 		xfrd_soa_t incoming_soa;
187 		time_t incoming_acquired;
188 
189 		memset(&soa_nsd_read, 0, sizeof(soa_nsd_read));
190 		memset(&soa_disk_read, 0, sizeof(soa_disk_read));
191 		memset(&soa_notified_read, 0, sizeof(soa_notified_read));
192 
193 		if(!xfrd_read_check_str(in, "zone:") ||
194 		   !xfrd_read_check_str(in, "name:")  ||
195 		   !(p=xfrd_read_token(in)) ||
196 		   !(dname = dname_parse(tempregion, p)) ||
197 		   !xfrd_read_check_str(in, "state:") ||
198 		   !xfrd_read_i32(in, &state) || (state>2) ||
199 		   !xfrd_read_check_str(in, "master:") ||
200 		   !xfrd_read_i32(in, &masnum) ||
201 		   !xfrd_read_check_str(in, "next_master:") ||
202 		   !xfrd_read_i32(in, &nextmas) ||
203 		   !xfrd_read_check_str(in, "round_num:") ||
204 		   !xfrd_read_i32(in, &round_num) ||
205 		   !xfrd_read_check_str(in, "next_timeout:") ||
206 		   !xfrd_read_i32(in, &timeout) ||
207 		   !xfrd_read_state_soa(in, "soa_nsd_acquired:", "soa_nsd:",
208 			&soa_nsd_read, &soa_nsd_acquired_read) ||
209 		   !xfrd_read_state_soa(in, "soa_disk_acquired:", "soa_disk:",
210 			&soa_disk_read, &soa_disk_acquired_read) ||
211 		   !xfrd_read_state_soa(in, "soa_notify_acquired:", "soa_notify:",
212 			&soa_notified_read, &soa_notified_acquired_read))
213 		{
214 			log_msg(LOG_ERR, "xfrd: corrupt state file %s dated %d (now=%d)",
215 				statefile, (int)filetime, (int)xfrd_time());
216 			fclose(in);
217 			region_destroy(tempregion);
218 			return;
219 		}
220 
221 		zone = (xfrd_zone_t*)rbtree_search(xfrd->zones, dname);
222 		if(!zone) {
223 			DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: state file has info for not configured zone %s", p));
224 			continue;
225 		}
226 
227 		if(soa_nsd_acquired_read>xfrd_time()+15 ||
228 			soa_disk_acquired_read>xfrd_time()+15 ||
229 			soa_notified_acquired_read>xfrd_time()+15)
230 		{
231 			log_msg(LOG_ERR, "xfrd: statefile %s contains"
232 				" times in the future for zone %s. Ignoring.",
233 				statefile, zone->apex_str);
234 			continue;
235 		}
236 		zone->state = state;
237 		zone->master_num = masnum;
238 		zone->next_master = nextmas;
239 		zone->round_num = round_num;
240 		zone->timeout.tv_sec = timeout;
241 		zone->timeout.tv_nsec = 0;
242 
243 		/* read the zone OK, now set the master properly */
244 		zone->master = acl_find_num(
245 			zone->zone_options->request_xfr, zone->master_num);
246 		if(!zone->master) {
247 			DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: masters changed for zone %s",
248 				zone->apex_str));
249 			zone->master = zone->zone_options->request_xfr;
250 			zone->master_num = 0;
251 			zone->round_num = 0;
252 		}
253 
254 		/*
255 		 * There is no timeout,
256 		 * or there is a notification,
257 		 * or there is a soa && current time is past refresh point
258 		 */
259 		if(timeout == 0 || soa_notified_acquired_read != 0 ||
260 			(soa_disk_acquired_read != 0 &&
261 			(uint32_t)xfrd_time() - soa_disk_acquired_read
262 				> ntohl(soa_disk_read.refresh)))
263 		{
264 			zone->state = xfrd_zone_refreshing;
265 			xfrd_set_refresh_now(zone);
266 		}
267 
268 		/* There is a soa && current time is past expiry point */
269 		if(soa_disk_acquired_read!=0 &&
270 			(uint32_t)xfrd_time() - soa_disk_acquired_read
271 				> ntohl(soa_disk_read.expire))
272 		{
273 			zone->state = xfrd_zone_expired;
274 			xfrd_set_refresh_now(zone);
275 		}
276 
277 		/* handle as an incoming SOA. */
278 		incoming_soa = zone->soa_nsd;
279 		incoming_acquired = zone->soa_nsd_acquired;
280 		zone->soa_nsd = soa_nsd_read;
281 		zone->soa_disk = soa_disk_read;
282 		zone->soa_notified = soa_notified_read;
283 		zone->soa_nsd_acquired = soa_nsd_acquired_read;
284 		zone->soa_disk_acquired = soa_disk_acquired_read;
285 		zone->soa_notified_acquired = soa_notified_acquired_read;
286 		xfrd_handle_incoming_soa(zone, &incoming_soa, incoming_acquired);
287 	}
288 
289 	if(!xfrd_read_check_str(in, XFRD_FILE_MAGIC)) {
290 		log_msg(LOG_ERR, "xfrd: corrupt state file %s dated %d (now=%d)",
291 			statefile, (int)filetime, (int)xfrd_time());
292 		region_destroy(tempregion);
293 		fclose(in);
294 		return;
295 	}
296 
297 	DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: read %d zones from state file", numzones));
298 	fclose(in);
299 	region_destroy(tempregion);
300 }
301 
302 /* prints neato days hours and minutes. */
303 static void
304 neato_timeout(FILE* out, const char* str, uint32_t secs)
305 {
306 	fprintf(out, "%s", str);
307 	if(secs <= 0) {
308 		fprintf(out, " %ds", secs);
309 		return;
310 	}
311 	if(secs >= 3600*24) {
312 		fprintf(out, " %dd", secs/(3600*24));
313 		secs = secs % (3600*24);
314 	}
315 	if(secs >= 3600) {
316 		fprintf(out, " %dh", secs/3600);
317 		secs = secs%3600;
318 	}
319 	if(secs >= 60) {
320 		fprintf(out, " %dm", secs/60);
321 		secs = secs%60;
322 	}
323 	if(secs > 0) {
324 		fprintf(out, " %ds", secs);
325 	}
326 }
327 
328 static void xfrd_write_dname(FILE* out, uint8_t* dname)
329 {
330 	uint8_t* d= dname+1;
331 	uint8_t len = *d++;
332 	uint8_t i;
333 
334 	if(dname[0]<=1) {
335 		fprintf(out, ".");
336 		return;
337 	}
338 
339 	while(len)
340 	{
341 		assert(d - (dname+1) <= dname[0]);
342 		for(i=0; i<len; i++)
343 		{
344 			uint8_t ch = *d++;
345 			if (isalnum(ch) || ch == '-' || ch == '_') {
346 				fprintf(out, "%c", ch);
347 			} else if (ch == '.' || ch == '\\') {
348 				fprintf(out, "\\%c", ch);
349 			} else {
350 				fprintf(out, "\\%03u", (unsigned int)ch);
351 			}
352 		}
353 		fprintf(out, ".");
354 		len = *d++;
355 	}
356 }
357 
358 static void
359 xfrd_write_state_soa(FILE* out, const char* id,
360 	xfrd_soa_t* soa, time_t soatime, const dname_type* ATTR_UNUSED(apex))
361 {
362 	fprintf(out, "\t%s_acquired: %d", id, (int)soatime);
363 	if(!soatime) {
364 		fprintf(out, "\n");
365 		return;
366 	}
367 	neato_timeout(out, "\t# was", xfrd_time()-soatime);
368 	fprintf(out, " ago\n");
369 
370 	fprintf(out, "\t%s: %u %u %u %u", id,
371 		ntohs(soa->type), ntohs(soa->klass),
372 		ntohl(soa->ttl), ntohs(soa->rdata_count));
373 	fprintf(out, " ");
374 	xfrd_write_dname(out, soa->prim_ns);
375 	fprintf(out, " ");
376 	xfrd_write_dname(out, soa->email);
377 	fprintf(out, " %u", ntohl(soa->serial));
378 	fprintf(out, " %u", ntohl(soa->refresh));
379 	fprintf(out, " %u", ntohl(soa->retry));
380 	fprintf(out, " %u", ntohl(soa->expire));
381 	fprintf(out, " %u\n", ntohl(soa->minimum));
382 	fprintf(out, "\t#");
383 	neato_timeout(out, " refresh =", ntohl(soa->refresh));
384 	neato_timeout(out, " retry =", ntohl(soa->retry));
385 	neato_timeout(out, " expire =", ntohl(soa->expire));
386 	neato_timeout(out, " minimum =", ntohl(soa->minimum));
387 	fprintf(out, "\n");
388 }
389 
390 void
391 xfrd_write_state(struct xfrd_state* xfrd)
392 {
393 	rbnode_t* p;
394 	const char* statefile = xfrd->nsd->options->xfrdfile;
395 	FILE *out;
396 	time_t now = xfrd_time();
397 
398 	DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: write file %s", statefile));
399 	out = fopen(statefile, "w");
400 	if(!out) {
401 		log_msg(LOG_ERR, "xfrd: Could not open file %s for writing: %s",
402 				statefile, strerror(errno));
403 		return;
404 	}
405 
406 	fprintf(out, "%s\n", XFRD_FILE_MAGIC);
407 	fprintf(out, "# This file is written on exit by nsd xfr daemon.\n");
408 	fprintf(out, "# This file contains slave zone information:\n");
409 	fprintf(out, "# 	* timeouts (when was zone data acquired)\n");
410 	fprintf(out, "# 	* state (OK, refreshing, expired)\n");
411 	fprintf(out, "# 	* which master transfer to attempt next\n");
412 	fprintf(out, "# The file is read on start (but not on reload) by nsd xfr daemon.\n");
413 	fprintf(out, "# You can edit; but do not change statement order\n");
414 	fprintf(out, "# and no fancy stuff (like quoted \"strings\").\n");
415 	fprintf(out, "#\n");
416 	fprintf(out, "# If you remove a zone entry, it will be refreshed.\n");
417 	fprintf(out, "# This can be useful for an expired zone; it revives\n");
418 	fprintf(out, "# the zone temporarily, from refresh-expiry time.\n");
419 	fprintf(out, "# If you delete the file all slave zones are updated.\n");
420 	fprintf(out, "#\n");
421 	fprintf(out, "# Note: if you edit this file while nsd is running,\n");
422 	fprintf(out, "#       it will be overwritten on exit by nsd.\n");
423 	fprintf(out, "\n");
424 	fprintf(out, "filetime: %d\t# %s\n", (int)now, ctime(&now));
425 	fprintf(out, "# The number of zone entries in this file\n");
426 	fprintf(out, "numzones: %d\n", (int)xfrd->zones->count);
427 	fprintf(out, "\n");
428 	for(p = rbtree_first(xfrd->zones); p && p!=RBTREE_NULL; p=rbtree_next(p))
429 	{
430 		xfrd_zone_t* zone = (xfrd_zone_t*)p;
431 		fprintf(out, "zone: \tname: %s\n", zone->apex_str);
432 		fprintf(out, "\tstate: %d", (int)zone->state);
433 		fprintf(out, " # %s", zone->state==xfrd_zone_ok?"OK":(
434 			zone->state==xfrd_zone_refreshing?"refreshing":"expired"));
435 		fprintf(out, "\n");
436 		fprintf(out, "\tmaster: %d\n", zone->master_num);
437 		fprintf(out, "\tnext_master: %d\n", zone->next_master);
438 		fprintf(out, "\tround_num: %d\n", zone->round_num);
439 		fprintf(out, "\tnext_timeout: %d",
440 			zone->zone_handler.timeout?(int)zone->timeout.tv_sec:0);
441 		if(zone->zone_handler.timeout) {
442 			neato_timeout(out, "\t# =", zone->timeout.tv_sec - xfrd_time());
443 		}
444 		fprintf(out, "\n");
445 		xfrd_write_state_soa(out, "soa_nsd", &zone->soa_nsd,
446 			zone->soa_nsd_acquired, zone->apex);
447 		xfrd_write_state_soa(out, "soa_disk", &zone->soa_disk,
448 			zone->soa_disk_acquired, zone->apex);
449 		xfrd_write_state_soa(out, "soa_notify", &zone->soa_notified,
450 			zone->soa_notified_acquired, zone->apex);
451 		fprintf(out, "\n");
452 	}
453 
454 	fprintf(out, "%s\n", XFRD_FILE_MAGIC);
455 	DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: written %d zones to state file",
456 		(int)xfrd->zones->count));
457 	fclose(out);
458 }
459