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