/*
* xfrd-disk.c - XFR (transfer) Daemon TCP system source file. Read/Write state to disk.
*
* Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
*
* See LICENSE for the license.
*
*/
zone = (xfrd_zone_type*)rbtree_search(xfrd->zones, dname);
if(!zone) {
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: state file has info for not configured zone %s", p));
continue;
}
if(soa_nsd_acquired_read>xfrd_time()+15 ||
soa_disk_acquired_read>xfrd_time()+15 ||
soa_notified_acquired_read>xfrd_time()+15)
{
log_msg(LOG_ERR, "xfrd: statefile %s contains"
" times in the future for zone %s. Ignoring.",
statefile, zone->apex_str);
continue;
}
zone->state = state;
zone->master_num = masnum;
zone->next_master = nextmas;
zone->round_num = round_num;
zone->timeout.tv_sec = timeout;
zone->timeout.tv_usec = 0;
zone->fresh_xfr_timeout = backoff*XFRD_TRANSFER_TIMEOUT_START;
/* read the zone OK, now set the master properly */
zone->master = acl_find_num(zone->zone_options->pattern->
request_xfr, zone->master_num);
if(!zone->master) {
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: masters changed for zone %s",
zone->apex_str));
zone->master = zone->zone_options->pattern->request_xfr;
zone->master_num = 0;
zone->round_num = 0;
}
/*
* There is no timeout,
* or there is a notification,
* or there is a soa && current time is past refresh point
*/
soa_refresh = ntohl(soa_disk_read.refresh);
if (soa_refresh > (time_t)zone->zone_options->pattern->max_refresh_time)
soa_refresh = zone->zone_options->pattern->max_refresh_time;
else if (soa_refresh < (time_t)zone->zone_options->pattern->min_refresh_time)
soa_refresh = zone->zone_options->pattern->min_refresh_time;
if(timeout == 0 || soa_notified_acquired_read != 0 ||
(soa_disk_acquired_read != 0 &&
(uint32_t)xfrd_time() - soa_disk_acquired_read
> (uint32_t)soa_refresh))
{
zone->state = xfrd_zone_refreshing;
xfrd_set_refresh_now(zone);
}
if(timeout != 0 && filetime + timeout < (uint32_t)xfrd_time()) {
/* timeout is in the past, refresh the zone */
timeout = 0;
if(zone->state == xfrd_zone_ok)
zone->state = xfrd_zone_refreshing;
xfrd_set_refresh_now(zone);
}
/* There is a soa && current time is past expiry point */
if(soa_disk_acquired_read!=0 &&
(uint32_t)xfrd_time() - soa_disk_acquired_read
> ntohl(soa_disk_read.expire))
{
zone->state = xfrd_zone_expired;
xfrd_set_refresh_now(zone);
}
/* there is a zone read and it matches what we had before */
if(zone->soa_nsd_acquired && zone->state != xfrd_zone_expired
&& zone->soa_nsd.serial == soa_nsd_read.serial) {
xfrd_deactivate_zone(zone);
zone->state = state;
xfrd_set_timer(zone,
within_refresh_bounds(zone, timeout));
}
if((zone->soa_nsd_acquired == 0 && soa_nsd_acquired_read == 0 &&
soa_disk_acquired_read == 0) ||
(zone->state != xfrd_zone_ok && timeout != 0)) {
/* but don't check now, because that would mean a
* storm of attempts on some master servers */
xfrd_deactivate_zone(zone);
zone->state = state;
xfrd_set_timer(zone,
within_retry_bounds(zone, timeout));
}
/* handle as an incoming SOA. */
incoming_soa = zone->soa_nsd;
incoming_acquired = zone->soa_nsd_acquired;
zone->soa_nsd = soa_nsd_read;
zone->soa_nsd_acquired = soa_nsd_acquired_read;
/* use soa and soa_acquired from starting NSD, not what is stored in
* the state file, because the actual zone contents trumps the contents
* of this cache */
zone->soa_disk = incoming_soa;
zone->soa_disk_acquired = incoming_acquired;
zone->soa_notified = soa_notified_read;
zone->soa_notified_acquired = soa_notified_acquired_read;
if (zone->state == xfrd_zone_expired)
{
xfrd_send_expire_notification(zone);
}
if(incoming_acquired != 0)
xfrd_handle_incoming_soa(zone, &incoming_soa, incoming_acquired);
}
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: write file %s", statefile));
out = fopen(statefile, "w");
if(!out) {
log_msg(LOG_ERR, "xfrd: Could not open file %s for writing: %s",
statefile, strerror(errno));
return;
}
fprintf(out, "%s\n", XFRD_FILE_MAGIC);
fprintf(out, "# This file is written on exit by nsd xfr daemon.\n");
fprintf(out, "# This file contains slave zone information:\n");
fprintf(out, "# * timeouts (when was zone data acquired)\n");
fprintf(out, "# * state (OK, refreshing, expired)\n");
fprintf(out, "# * which master transfer to attempt next\n");
fprintf(out, "# The file is read on start (but not on reload) by nsd xfr daemon.\n");
fprintf(out, "# You can edit; but do not change statement order\n");
fprintf(out, "# and no fancy stuff (like quoted \"strings\").\n");
fprintf(out, "#\n");
fprintf(out, "# If you remove a zone entry, it will be refreshed.\n");
fprintf(out, "# This can be useful for an expired zone; it revives\n");
fprintf(out, "# the zone temporarily, from refresh-expiry time.\n");
fprintf(out, "# If you delete the file all slave zones are updated.\n");
fprintf(out, "#\n");
fprintf(out, "# Note: if you edit this file while nsd is running,\n");
fprintf(out, "# it will be overwritten on exit by nsd.\n");
fprintf(out, "\n");
fprintf(out, "filetime: %lld\t# %s\n", (long long)now, ctime(&now));
fprintf(out, "# The number of zone entries in this file\n");
fprintf(out, "numzones: %d\n", (int)xfrd->zones->count);
fprintf(out, "\n");
for(p = rbtree_first(xfrd->zones); p && p!=RBTREE_NULL; p=rbtree_next(p))
{
xfrd_zone_type* zone = (xfrd_zone_type*)p;
fprintf(out, "zone: \tname: %s\n", zone->apex_str);
fprintf(out, "\tstate: %d", (int)zone->state);
fprintf(out, " # %s", zone->state==xfrd_zone_ok?"OK":(
zone->state==xfrd_zone_refreshing?"refreshing":"expired"));
fprintf(out, "\n");
fprintf(out, "\tmaster: %d\n", zone->master_num);
fprintf(out, "\tnext_master: %d\n", zone->next_master);
fprintf(out, "\tround_num: %d\n", zone->round_num);
fprintf(out, "\tnext_timeout: %d",
(zone->zone_handler_flags&EV_TIMEOUT)?(int)zone->timeout.tv_sec:0);
if((zone->zone_handler_flags&EV_TIMEOUT)) {
neato_timeout(out, "\t# =", zone->timeout.tv_sec);
}
fprintf(out, "\n");
fprintf(out, "\tbackoff: %d\n", zone->fresh_xfr_timeout/XFRD_TRANSFER_TIMEOUT_START);
xfrd_write_state_soa(out, "soa_nsd", &zone->soa_nsd,
zone->soa_nsd_acquired, zone->apex);
xfrd_write_state_soa(out, "soa_disk", &zone->soa_disk,
zone->soa_disk_acquired, zone->apex);
xfrd_write_state_soa(out, "soa_notify", &zone->soa_notified,
zone->soa_notified_acquired, zone->apex);
fprintf(out, "\n");
}
fprintf(out, "%s\n", XFRD_FILE_MAGIC);
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: written %d zones to state file",
(int)xfrd->zones->count));
fclose(out);
}
void
xfrd_make_tempdir(struct nsd* nsd)
{
char tnm[1024];
tempdirname(tnm, sizeof(tnm), nsd);
/* create parent directories if needed (0750 permissions) */
if(!create_dirs(tnm)) {
log_msg(LOG_ERR, "parentdirs of %s failed", tnm);
}
/* restrictive permissions here, because this may be in /tmp */
if(mkdir(tnm, 0700)==-1) {
if(errno != EEXIST) {
log_msg(LOG_ERR, "mkdir %s failed: %s",
tnm, strerror(errno));
}
}
}
void
xfrd_del_tempdir(struct nsd* nsd)
{
char tnm[1024];
tempdirname(tnm, sizeof(tnm), nsd);
/* ignore parent directories, they are likely /var/tmp, /tmp or
* /var/cache/nsd and do not have to be deleted */
if(rmdir(tnm)==-1 && errno != ENOENT) {
log_msg(LOG_WARNING, "rmdir %s failed: %s", tnm,
strerror(errno));
}
}