xref: /openbsd-src/usr.sbin/nsd/xfrd-disk.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
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 		/* we had better use what we got from starting NSD, not
285 		 * what we store in this file, because the actual zone
286 		 * contents trumps the contents of this cache */
287 		/* zone->soa_disk_acquired = soa_disk_acquired_read; */
288 		zone->soa_notified_acquired = soa_notified_acquired_read;
289 		xfrd_handle_incoming_soa(zone, &incoming_soa, incoming_acquired);
290 	}
291 
292 	if(!xfrd_read_check_str(in, XFRD_FILE_MAGIC)) {
293 		log_msg(LOG_ERR, "xfrd: corrupt state file %s dated %d (now=%d)",
294 			statefile, (int)filetime, (int)xfrd_time());
295 		region_destroy(tempregion);
296 		fclose(in);
297 		return;
298 	}
299 
300 	DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: read %d zones from state file", numzones));
301 	fclose(in);
302 	region_destroy(tempregion);
303 }
304 
305 /* prints neato days hours and minutes. */
306 static void
307 neato_timeout(FILE* out, const char* str, uint32_t secs)
308 {
309 	fprintf(out, "%s", str);
310 	if(secs <= 0) {
311 		fprintf(out, " %ds", secs);
312 		return;
313 	}
314 	if(secs >= 3600*24) {
315 		fprintf(out, " %dd", secs/(3600*24));
316 		secs = secs % (3600*24);
317 	}
318 	if(secs >= 3600) {
319 		fprintf(out, " %dh", secs/3600);
320 		secs = secs%3600;
321 	}
322 	if(secs >= 60) {
323 		fprintf(out, " %dm", secs/60);
324 		secs = secs%60;
325 	}
326 	if(secs > 0) {
327 		fprintf(out, " %ds", secs);
328 	}
329 }
330 
331 static void xfrd_write_dname(FILE* out, uint8_t* dname)
332 {
333 	uint8_t* d= dname+1;
334 	uint8_t len = *d++;
335 	uint8_t i;
336 
337 	if(dname[0]<=1) {
338 		fprintf(out, ".");
339 		return;
340 	}
341 
342 	while(len)
343 	{
344 		assert(d - (dname+1) <= dname[0]);
345 		for(i=0; i<len; i++)
346 		{
347 			uint8_t ch = *d++;
348 			if (isalnum(ch) || ch == '-' || ch == '_') {
349 				fprintf(out, "%c", ch);
350 			} else if (ch == '.' || ch == '\\') {
351 				fprintf(out, "\\%c", ch);
352 			} else {
353 				fprintf(out, "\\%03u", (unsigned int)ch);
354 			}
355 		}
356 		fprintf(out, ".");
357 		len = *d++;
358 	}
359 }
360 
361 static void
362 xfrd_write_state_soa(FILE* out, const char* id,
363 	xfrd_soa_t* soa, time_t soatime, const dname_type* ATTR_UNUSED(apex))
364 {
365 	fprintf(out, "\t%s_acquired: %d", id, (int)soatime);
366 	if(!soatime) {
367 		fprintf(out, "\n");
368 		return;
369 	}
370 	neato_timeout(out, "\t# was", xfrd_time()-soatime);
371 	fprintf(out, " ago\n");
372 
373 	fprintf(out, "\t%s: %u %u %u %u", id,
374 		ntohs(soa->type), ntohs(soa->klass),
375 		ntohl(soa->ttl), ntohs(soa->rdata_count));
376 	fprintf(out, " ");
377 	xfrd_write_dname(out, soa->prim_ns);
378 	fprintf(out, " ");
379 	xfrd_write_dname(out, soa->email);
380 	fprintf(out, " %u", ntohl(soa->serial));
381 	fprintf(out, " %u", ntohl(soa->refresh));
382 	fprintf(out, " %u", ntohl(soa->retry));
383 	fprintf(out, " %u", ntohl(soa->expire));
384 	fprintf(out, " %u\n", ntohl(soa->minimum));
385 	fprintf(out, "\t#");
386 	neato_timeout(out, " refresh =", ntohl(soa->refresh));
387 	neato_timeout(out, " retry =", ntohl(soa->retry));
388 	neato_timeout(out, " expire =", ntohl(soa->expire));
389 	neato_timeout(out, " minimum =", ntohl(soa->minimum));
390 	fprintf(out, "\n");
391 }
392 
393 void
394 xfrd_write_state(struct xfrd_state* xfrd)
395 {
396 	rbnode_t* p;
397 	const char* statefile = xfrd->nsd->options->xfrdfile;
398 	FILE *out;
399 	time_t now = xfrd_time();
400 
401 	DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: write file %s", statefile));
402 	out = fopen(statefile, "w");
403 	if(!out) {
404 		log_msg(LOG_ERR, "xfrd: Could not open file %s for writing: %s",
405 				statefile, strerror(errno));
406 		return;
407 	}
408 
409 	fprintf(out, "%s\n", XFRD_FILE_MAGIC);
410 	fprintf(out, "# This file is written on exit by nsd xfr daemon.\n");
411 	fprintf(out, "# This file contains slave zone information:\n");
412 	fprintf(out, "# 	* timeouts (when was zone data acquired)\n");
413 	fprintf(out, "# 	* state (OK, refreshing, expired)\n");
414 	fprintf(out, "# 	* which master transfer to attempt next\n");
415 	fprintf(out, "# The file is read on start (but not on reload) by nsd xfr daemon.\n");
416 	fprintf(out, "# You can edit; but do not change statement order\n");
417 	fprintf(out, "# and no fancy stuff (like quoted \"strings\").\n");
418 	fprintf(out, "#\n");
419 	fprintf(out, "# If you remove a zone entry, it will be refreshed.\n");
420 	fprintf(out, "# This can be useful for an expired zone; it revives\n");
421 	fprintf(out, "# the zone temporarily, from refresh-expiry time.\n");
422 	fprintf(out, "# If you delete the file all slave zones are updated.\n");
423 	fprintf(out, "#\n");
424 	fprintf(out, "# Note: if you edit this file while nsd is running,\n");
425 	fprintf(out, "#       it will be overwritten on exit by nsd.\n");
426 	fprintf(out, "\n");
427 	fprintf(out, "filetime: %d\t# %s\n", (int)now, ctime(&now));
428 	fprintf(out, "# The number of zone entries in this file\n");
429 	fprintf(out, "numzones: %d\n", (int)xfrd->zones->count);
430 	fprintf(out, "\n");
431 	for(p = rbtree_first(xfrd->zones); p && p!=RBTREE_NULL; p=rbtree_next(p))
432 	{
433 		xfrd_zone_t* zone = (xfrd_zone_t*)p;
434 		fprintf(out, "zone: \tname: %s\n", zone->apex_str);
435 		fprintf(out, "\tstate: %d", (int)zone->state);
436 		fprintf(out, " # %s", zone->state==xfrd_zone_ok?"OK":(
437 			zone->state==xfrd_zone_refreshing?"refreshing":"expired"));
438 		fprintf(out, "\n");
439 		fprintf(out, "\tmaster: %d\n", zone->master_num);
440 		fprintf(out, "\tnext_master: %d\n", zone->next_master);
441 		fprintf(out, "\tround_num: %d\n", zone->round_num);
442 		fprintf(out, "\tnext_timeout: %d",
443 			zone->zone_handler.timeout?(int)zone->timeout.tv_sec:0);
444 		if(zone->zone_handler.timeout) {
445 			neato_timeout(out, "\t# =", zone->timeout.tv_sec - xfrd_time());
446 		}
447 		fprintf(out, "\n");
448 		xfrd_write_state_soa(out, "soa_nsd", &zone->soa_nsd,
449 			zone->soa_nsd_acquired, zone->apex);
450 		xfrd_write_state_soa(out, "soa_disk", &zone->soa_disk,
451 			zone->soa_disk_acquired, zone->apex);
452 		xfrd_write_state_soa(out, "soa_notify", &zone->soa_notified,
453 			zone->soa_notified_acquired, zone->apex);
454 		fprintf(out, "\n");
455 	}
456 
457 	fprintf(out, "%s\n", XFRD_FILE_MAGIC);
458 	DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: written %d zones to state file",
459 		(int)xfrd->zones->count));
460 	fclose(out);
461 }
462