#include "config.h"
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <inttypes.h>
#include "xfrd.h"
#include "xfrd-tcp.h"
#include "xfrd-disk.h"
#include "xfrd-notify.h"
#include "xfrd-catalog-zones.h"
#include "options.h"
#include "util.h"
#include "netio.h"
#include "region-allocator.h"
#include "nsd.h"
#include "packet.h"
#include "rdata.h"
#include "difffile.h"
#include "ipc.h"
#include "remote.h"
#include "rrl.h"
#ifdef USE_DNSTAP
#include "dnstap/dnstap_collector.h"
#endif
#ifdef USE_METRICS
#include "metrics.h"
#endif
#ifdef HAVE_SYSTEMD
#include <systemd/sd-daemon.h>
#endif
#ifdef HAVE_SSL
#include <openssl/ssl.h>
#include <openssl/x509.h>
#include <openssl/evp.h>
#endif
#define XFRD_UDP_TIMEOUT 10
#define XFRD_NO_IXFR_CACHE 172800
#define XFRD_MAX_ROUNDS 1
#define XFRD_TSIG_MAX_UNSIGNED 103
#define XFRD_CHILD_REAP_TIMEOUT 60
xfrd_state_type* xfrd = 0;
static void xfrd_main(void);
static void xfrd_shutdown(void);
static void xfrd_clean_pending_tasks(struct nsd* nsd, udb_base* u);
static void xfrd_init_zones(void);
static void xfrd_receive_soa(int socket, int shortsoa);
static int xfrd_handle_incoming_notify(xfrd_zone_type* zone,
xfrd_soa_type* soa);
static int xfrd_parse_soa_info(buffer_type* packet, xfrd_soa_type* soa);
static void xfrd_set_zone_state(xfrd_zone_type* zone,
enum xfrd_zone_state new_zone_state);
static void xfrd_set_timer_retry(xfrd_zone_type* zone);
static void xfrd_set_timer_refresh(xfrd_zone_type* zone);
static void xfrd_set_reload_timeout(void);
static void xfrd_handle_reload(int fd, short event, void* arg);
static void xfrd_handle_child_timer(int fd, short event, void* arg);
static int xfrd_send_ixfr_request_udp(xfrd_zone_type* zone);
static void xfrd_udp_obtain(xfrd_zone_type* zone);
static void xfrd_udp_read(xfrd_zone_type* zone);
static int find_same_master_notify(xfrd_zone_type* zone, int acl_num_nfy);
static void xfrd_write_timer_set(void);
static void xfrd_free_zone_xfr(xfrd_zone_type* zone, xfrd_xfr_type* xfr);
static void
xfrd_signal_callback(int sig, short event, void* ATTR_UNUSED(arg))
{
if(!(event & EV_SIGNAL))
return;
sig_handler(sig);
}
static struct event* xfrd_sig_evs[10];
static int xfrd_sig_num = 0;
static void
xfrd_sigsetup(int sig)
{
struct event *ev = xalloc_zero(sizeof(*ev));
assert(xfrd_sig_num <= (int)(sizeof(xfrd_sig_evs)/sizeof(ev)));
xfrd_sig_evs[xfrd_sig_num++] = ev;
signal_set(ev, sig, xfrd_signal_callback, NULL);
if(event_base_set(xfrd->event_base, ev) != 0) {
log_msg(LOG_ERR, "xfrd sig handler: event_base_set failed");
}
if(signal_add(ev, NULL) != 0) {
log_msg(LOG_ERR, "xfrd sig handler: signal_add failed");
}
}
void
xfrd_init(int socket, struct nsd* nsd, int shortsoa, int reload_active,
pid_t nsd_pid)
{
region_type* region;
size_t i;
assert(xfrd == 0);
nsd->server_kind = NSD_SERVER_MAIN;
region = region_create_custom(xalloc, free, DEFAULT_CHUNK_SIZE,
DEFAULT_LARGE_OBJECT_SIZE, DEFAULT_INITIAL_CLEANUP_SIZE, 1);
xfrd = (xfrd_state_type*)region_alloc(region, sizeof(xfrd_state_type));
memset(xfrd, 0, sizeof(xfrd_state_type));
xfrd->region = region;
xfrd->xfrd_start_time = time(0);
xfrd->event_base = nsd_child_event_base();
if(!xfrd->event_base) {
log_msg(LOG_ERR, "xfrd: cannot create event base");
exit(1);
}
xfrd->nsd = nsd;
xfrd->packet = buffer_create(xfrd->region, QIOBUFSZ);
xfrd->udp_waiting_first = NULL;
xfrd->udp_waiting_last = NULL;
xfrd->udp_use_num = 0;
xfrd->got_time = 0;
xfrd->xfrfilenumber = 0;
#ifdef USE_ZONE_STATS
xfrd->zonestat_safe = nsd->zonestatdesired;
#endif
xfrd->activated_first = NULL;
xfrd->last_task = region_alloc(xfrd->region, sizeof(*xfrd->last_task));
udb_ptr_init(xfrd->last_task, xfrd->nsd->task[xfrd->nsd->mytask]);
assert(shortsoa || udb_base_get_userdata(xfrd->nsd->task[xfrd->nsd->mytask])->data == 0);
xfrd->reload_handler.ev_fd = -1;
xfrd->reload_added = 0;
xfrd->reload_timeout.tv_sec = 0;
xfrd->reload_cmd_last_sent = xfrd->xfrd_start_time;
xfrd->reload_cmd_first_sent = 0;
xfrd->reload_failed = 0;
xfrd->can_send_reload = !reload_active;
xfrd->reload_pid = nsd_pid;
xfrd->child_timer_added = 0;
xfrd->ipc_send_blocked = 0;
memset(&xfrd->ipc_handler, 0, sizeof(xfrd->ipc_handler));
event_set(&xfrd->ipc_handler, socket, EV_PERSIST|EV_READ,
xfrd_handle_ipc, xfrd);
if(event_base_set(xfrd->event_base, &xfrd->ipc_handler) != 0)
log_msg(LOG_ERR, "xfrd ipc handler: event_base_set failed");
if(event_add(&xfrd->ipc_handler, NULL) != 0)
log_msg(LOG_ERR, "xfrd ipc handler: event_add failed");
xfrd->ipc_handler_flags = EV_PERSIST|EV_READ;
xfrd->notify_events = (struct event *) region_alloc_array_zero(
xfrd->region, nsd->child_count * 2, sizeof(struct event));
xfrd->notify_pipes = (struct xfrd_tcp *) region_alloc_array_zero(
xfrd->region, nsd->child_count * 2, sizeof(struct xfrd_tcp));
for(i = 0; i < 2 * nsd->child_count; i++) {
int fd = nsd->serve2xfrd_fd_recv[i];
xfrd->notify_pipes[i].fd = fd;
xfrd->notify_pipes[i].packet = buffer_create(xfrd->region, QIOBUFSZ);
event_set(&xfrd->notify_events[i], fd,
EV_PERSIST|EV_READ, xfrd_handle_notify, &xfrd->notify_pipes[i]);
if(event_base_set(xfrd->event_base, &xfrd->notify_events[i]) != 0)
log_msg( LOG_ERR
, "xfrd notify_event: event_base_set failed");
if(event_add(&xfrd->notify_events[i], NULL) != 0)
log_msg(LOG_ERR, "xfrd notify_event: event_add failed");
}
xfrd->need_to_send_reload = 0;
xfrd->need_to_send_shutdown = 0;
xfrd->need_to_send_stats = 0;
xfrd->write_zonefile_needed = 0;
if(nsd->options->zonefiles_write)
xfrd_write_timer_set();
xfrd->notify_waiting_first = NULL;
xfrd->notify_waiting_last = NULL;
xfrd->notify_udp_num = 0;
daemon_remote_attach(xfrd->nsd->rc, xfrd);
#ifdef USE_METRICS
daemon_metrics_attach(xfrd->nsd->metrics, xfrd);
#endif
xfrd->tcp_set = xfrd_tcp_set_create(xfrd->region, nsd->options->tls_cert_bundle, nsd->options->xfrd_tcp_max, nsd->options->xfrd_tcp_pipeline);
xfrd->tcp_set->tcp_timeout = nsd->tcp_timeout;
#if !defined(HAVE_ARC4RANDOM) && !defined(HAVE_GETRANDOM)
srandom((unsigned long) getpid() * (unsigned long) time(NULL));
#endif
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd pre-startup"));
xfrd_init_zones();
xfrd_receive_soa(socket, shortsoa);
if(nsd->options->xfrdfile != NULL && nsd->options->xfrdfile[0]!=0)
xfrd_read_state(xfrd);
if(nsd->signal_hint_shutdown) {
kill(nsd_pid, SIGTERM);
xfrd_shutdown();
return;
}
xfrd_sigsetup(SIGHUP);
xfrd_sigsetup(SIGTERM);
xfrd_sigsetup(SIGQUIT);
xfrd_sigsetup(SIGCHLD);
xfrd_sigsetup(SIGALRM);
xfrd_sigsetup(SIGILL);
xfrd_sigsetup(SIGUSR1);
xfrd_sigsetup(SIGINT);
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd startup"));
#ifdef HAVE_SYSTEMD
sd_notify(0, "READY=1");
#endif
xfrd_main();
}
static void
xfrd_process_activated(void)
{
xfrd_zone_type* zone;
while((zone = xfrd->activated_first)) {
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd zone %s activation",
zone->apex_str));
xfrd->activated_first = zone->activated_next;
if(zone->activated_next)
zone->activated_next->activated_prev = NULL;
zone->is_activated = 0;
xfrd_handle_zone(zone->zone_handler.ev_fd, 0, zone);
}
}
static void
xfrd_sig_process(void)
{
int status;
pid_t child_pid;
if(xfrd->nsd->signal_hint_quit || xfrd->nsd->signal_hint_shutdown) {
xfrd->nsd->signal_hint_quit = 0;
xfrd->nsd->signal_hint_shutdown = 0;
xfrd->need_to_send_shutdown = 1;
if(!(xfrd->ipc_handler_flags&EV_WRITE)) {
ipc_xfrd_set_listening(xfrd, EV_PERSIST|EV_READ|EV_WRITE);
}
} else if(xfrd->nsd->signal_hint_reload_hup) {
log_msg(LOG_WARNING, "SIGHUP received, reloading...");
xfrd->nsd->signal_hint_reload_hup = 0;
if(xfrd->nsd->options->reload_config) {
xfrd_reload_config(xfrd);
}
if(xfrd->nsd->options->zonefiles_check) {
task_new_check_zonefiles(xfrd->nsd->task[
xfrd->nsd->mytask], xfrd->last_task, NULL);
}
xfrd_set_reload_now(xfrd);
} else if(xfrd->nsd->signal_hint_statsusr) {
xfrd->nsd->signal_hint_statsusr = 0;
xfrd->need_to_send_stats = 1;
if(!(xfrd->ipc_handler_flags&EV_WRITE)) {
ipc_xfrd_set_listening(xfrd, EV_PERSIST|EV_READ|EV_WRITE);
}
}
xfrd->nsd->signal_hint_child = 0;
while((child_pid = waitpid(-1, &status, WNOHANG)) != -1 && child_pid != 0) {
if(status != 0) {
log_msg(LOG_ERR, "process %d exited with status %d",
(int)child_pid, status);
}
}
if(!xfrd->child_timer_added) {
struct timeval tv;
tv.tv_sec = XFRD_CHILD_REAP_TIMEOUT;
tv.tv_usec = 0;
memset(&xfrd->child_timer, 0, sizeof(xfrd->child_timer));
event_set(&xfrd->child_timer, -1, EV_TIMEOUT,
xfrd_handle_child_timer, xfrd);
if(event_base_set(xfrd->event_base, &xfrd->child_timer) != 0)
log_msg(LOG_ERR, "xfrd child timer: event_base_set failed");
if(event_add(&xfrd->child_timer, &tv) != 0)
log_msg(LOG_ERR, "xfrd child timer: event_add failed");
xfrd->child_timer_added = 1;
}
}
static void
xfrd_main(void)
{
xfrd_sig_process();
xfrd->shutdown = 0;
while(!xfrd->shutdown)
{
xfrd_process_catalog_producer_zones();
xfrd_process_catalog_consumer_zones();
xfrd_process_activated();
xfrd->got_time = 0;
if(event_base_loop(xfrd->event_base, EVLOOP_ONCE) == -1) {
if (errno != EINTR) {
log_msg(LOG_ERR,
"xfrd dispatch failed: %s",
strerror(errno));
}
}
xfrd_sig_process();
}
xfrd_shutdown();
}
static void
xfrd_shutdown()
{
xfrd_zone_type* zone;
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd shutdown"));
#ifdef HAVE_SYSTEMD
sd_notify(0, "STOPPING=1");
#endif
event_del(&xfrd->ipc_handler);
close(xfrd->ipc_handler.ev_fd);
zone_list_close(nsd.options);
if(xfrd->nsd->options->xfrdfile != NULL && xfrd->nsd->options->xfrdfile[0]!=0)
xfrd_write_state(xfrd);
if(xfrd->reload_added) {
event_del(&xfrd->reload_handler);
xfrd->reload_added = 0;
}
if(xfrd->child_timer_added) {
event_del(&xfrd->child_timer);
xfrd->child_timer_added = 0;
}
if(xfrd->nsd->options->zonefiles_write) {
event_del(&xfrd->write_timer);
}
daemon_remote_close(xfrd->nsd->rc);
RBTREE_FOR(zone, xfrd_zone_type*, xfrd->zones)
{
if(zone->event_added) {
event_del(&zone->zone_handler);
if(zone->zone_handler.ev_fd != -1) {
close(zone->zone_handler.ev_fd);
zone->zone_handler.ev_fd = -1;
}
zone->event_added = 0;
}
}
close_notify_fds(xfrd->notify_zones);
if(xfrd->reload_pid != -1) {
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd wait for servermain %d",
(int)xfrd->reload_pid));
while(1) {
if(waitpid(xfrd->reload_pid, NULL, 0) == -1) {
if(errno == EINTR) continue;
if(errno == ECHILD) break;
log_msg(LOG_ERR, "xfrd: waitpid(%d): %s",
(int)xfrd->reload_pid, strerror(errno));
}
break;
}
}
RBTREE_FOR(zone, xfrd_zone_type*, xfrd->zones)
{
xfrd_xfr_type *xfr;
for(xfr = zone->latest_xfr; xfr != NULL; xfr = xfr->prev) {
if (xfr->acquired == 0)
continue;
xfrd_unlink_xfrfile(xfrd->nsd, xfr->xfrfilenumber);
}
}
xfrd_clean_pending_tasks(xfrd->nsd, xfrd->nsd->task[xfrd->nsd->mytask]);
xfrd_del_tempdir(xfrd->nsd);
daemon_remote_delete(xfrd->nsd->rc);
#ifdef HAVE_SSL
if (xfrd->nsd->tls_ctx)
SSL_CTX_free(xfrd->nsd->tls_ctx);
# ifdef HAVE_TLS_1_3
if (xfrd->tcp_set->ssl_ctx) {
int i;
for(i=0; i<xfrd->tcp_set->tcp_max; i++) {
if(xfrd->tcp_set->tcp_state[i] &&
xfrd->tcp_set->tcp_state[i]->ssl) {
SSL_free(xfrd->tcp_set->tcp_state[i]->ssl);
xfrd->tcp_set->tcp_state[i]->ssl = NULL;
}
}
SSL_CTX_free(xfrd->tcp_set->ssl_ctx);
}
# endif
#endif
#ifdef USE_DNSTAP
dt_collector_close(nsd.dt_collector, &nsd);
#endif
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd shutdown complete"));
#ifdef MEMCLEAN
if(xfrd->nsd->db) {
namedb_close(xfrd->nsd->db);
}
if(xfrd->zones) {
xfrd_zone_type* z;
RBTREE_FOR(z, xfrd_zone_type*, xfrd->zones) {
while(z->latest_xfr != NULL) {
xfrd_free_zone_xfr(z, z->latest_xfr);
}
}
}
if(xfrd->notify_zones) {
struct notify_zone* n;
RBTREE_FOR(n, struct notify_zone*, xfrd->notify_zones) {
tsig_delete_record(&n->notify_tsig, NULL);
}
}
if(xfrd_sig_num > 0) {
int i;
for(i=0; i<xfrd_sig_num; i++) {
signal_del(xfrd_sig_evs[i]);
free(xfrd_sig_evs[i]);
}
}
#ifdef RATELIMIT
rrl_mmap_deinit();
#endif
#ifdef USE_DNSTAP
dt_collector_destroy(nsd.dt_collector, &nsd);
#endif
udb_base_free(nsd.task[0]);
udb_base_free(nsd.task[1]);
event_base_free(xfrd->event_base);
region_destroy(xfrd->region);
nsd_options_destroy(nsd.options);
region_destroy(nsd.region);
log_finalize();
#endif
exit(0);
}
static void
xfrd_clean_pending_tasks(struct nsd* nsd, udb_base* u)
{
udb_ptr t;
udb_ptr_new(&t, u, udb_base_get_userdata(u));
while(!udb_ptr_is_null(&t)) {
if(TASKLIST(&t)->task_type == task_apply_xfr) {
xfrd_unlink_xfrfile(nsd, TASKLIST(&t)->yesno);
}
udb_ptr_set_rptr(&t, u, &TASKLIST(&t)->next);
}
udb_ptr_unlink(&t, u);
}
void
xfrd_init_slave_zone(xfrd_state_type* xfrd, struct zone_options* zone_opt)
{
int num, num_xot;
xfrd_zone_type *xzone;
xzone = (xfrd_zone_type*)region_alloc(xfrd->region,
sizeof(xfrd_zone_type));
memset(xzone, 0, sizeof(xfrd_zone_type));
xzone->apex = zone_opt->node.key;
xzone->apex_str = zone_opt->name;
xzone->state = xfrd_zone_refreshing;
xzone->zone_options = zone_opt;
xzone->master = xzone->zone_options->pattern->request_xfr;
xzone->master_num = 0;
xzone->next_master = 0;
xzone->fresh_xfr_timeout = XFRD_TRANSFER_TIMEOUT_START;
xzone->soa_nsd_acquired = 0;
xzone->soa_disk_acquired = 0;
xzone->latest_xfr = NULL;
xzone->soa_notified_acquired = 0;
xzone->soa_nsd.prim_ns[0] = 1;
xzone->soa_nsd.email[0] = 1;
xzone->soa_disk.prim_ns[0]=1;
xzone->soa_disk.email[0]=1;
xzone->soa_notified.prim_ns[0]=1;
xzone->soa_notified.email[0]=1;
xzone->zone_handler.ev_fd = -1;
xzone->zone_handler_flags = 0;
xzone->event_added = 0;
xzone->tcp_conn = -1;
xzone->tcp_waiting = 0;
xzone->udp_waiting = 0;
xzone->is_activated = 0;
xzone->multi_master_first_master = -1;
xzone->multi_master_update_check = -1;
xfrd_set_refresh_now(xzone);
num = 0;
num_xot = 0;
for (; xzone->master != NULL; xzone->master = xzone->master->next, num++) {
if (xzone->master->tls_auth_options != NULL) num_xot++;
}
if (num_xot != 0 && num != num_xot)
log_msg(LOG_WARNING, "Some but not all request-xfrs for %s have XFR-over-TLS configured",
xzone->apex_str);
xzone->node.key = xzone->apex;
rbtree_insert(xfrd->zones, (rbnode_type*)xzone);
}
static void
xfrd_init_zones()
{
struct zone_options *zone_opt;
assert(xfrd->zones == 0);
xfrd->zones = rbtree_create(xfrd->region,
(int (*)(const void *, const void *)) dname_compare);
xfrd->notify_zones = rbtree_create(xfrd->region,
(int (*)(const void *, const void *)) dname_compare);
xfrd->catalog_consumer_zones = rbtree_create(xfrd->region,
(int (*)(const void *, const void *)) dname_compare);
xfrd->catalog_producer_zones = rbtree_create(xfrd->region,
(int (*)(const void *, const void *)) dname_compare);
RBTREE_FOR(zone_opt, struct zone_options*, xfrd->nsd->options->zone_options)
{
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: adding %s zone",
zone_opt->name));
if(zone_is_catalog_consumer(zone_opt)) {
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s "
"is a catalog consumer zone", zone_opt->name));
xfrd_init_catalog_consumer_zone(xfrd, zone_opt);
}
if(zone_is_catalog_producer_member(zone_opt)) {
xfrd_add_catalog_producer_member(
as_catalog_member_zone(zone_opt));
}
init_notify_send(xfrd->notify_zones, xfrd->region, zone_opt);
if(!zone_is_slave(zone_opt)) {
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s, "
"master zone has no outgoing xfr requests",
zone_opt->name));
continue;
}
xfrd_init_slave_zone(xfrd, zone_opt);
}
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: started server %d "
"secondary zones, %d catalog zones", (int)xfrd->zones->count,
(int)xfrd->catalog_consumer_zones->count));
}
static void
apply_xfrs_to_consumer_zone(struct xfrd_catalog_consumer_zone* consumer_zone,
zone_type* dbzone, xfrd_xfr_type* xfr)
{
FILE* df;
if(xfr->msg_is_ixfr) {
uint32_t soa_serial=0, after_serial=0;
xfrd_xfr_type* prev;
if(dbzone->soa_rrset == NULL
#ifndef PACKED_STRUCTS
|| dbzone->soa_rrset->rrs == NULL
#endif
|| dbzone->soa_rrset->rrs[0]->rdlength < 20+2*sizeof(void*)
|| !retrieve_soa_rdata_serial(dbzone->soa_rrset->rrs[0],
&soa_serial)) {
make_catalog_consumer_invalid(consumer_zone,
"could not apply ixfr on catalog consumer zone "
"\'%s\': invalid SOA resource record",
consumer_zone->options->name);
return;
}
if(soa_serial == xfr->msg_old_serial)
goto apply_xfr;
for(prev = xfr->prev; prev; prev = prev->prev) {
if(!prev->sent)
continue;
if(xfr->msg_old_serial != prev->msg_new_serial)
continue;
apply_xfrs_to_consumer_zone(consumer_zone, dbzone, prev);
break;
}
if(!prev
|| !retrieve_soa_rdata_serial(dbzone->soa_rrset->rrs[0],
&after_serial)
|| xfr->msg_old_serial != after_serial) {
make_catalog_consumer_invalid(consumer_zone,
"could not find and/or apply xfrs for catalog "
"consumer zone \'%s\': to update to serial %u",
consumer_zone->options->name,
xfr->msg_new_serial);
return;
}
}
apply_xfr:
DEBUG(DEBUG_IPC,1, (LOG_INFO, "apply %sXFR %u -> %u to consumer zone "
"\'%s\'", (xfr->msg_is_ixfr ? "I" : "A"), xfr->msg_old_serial,
xfr->msg_new_serial, consumer_zone->options->name));
if(!(df = xfrd_open_xfrfile(xfrd->nsd, xfr->xfrfilenumber, "r"))) {
make_catalog_consumer_invalid(consumer_zone,
"could not open transfer file %lld: %s",
(long long)xfr->xfrfilenumber, strerror(errno));
} else if(0 >= apply_ixfr_for_zone(xfrd->nsd, dbzone, df,
xfrd->nsd->options, NULL, xfr->xfrfilenumber)) {
make_catalog_consumer_invalid(consumer_zone,
"error processing transfer file %lld",
(long long)xfr->xfrfilenumber);
fclose(df);
} else {
make_catalog_consumer_valid(consumer_zone);
fclose(df);
DEBUG(DEBUG_IPC,1, (LOG_INFO, "%sXFR %u -> %u to consumer zone \'%s\' "
"applied", (xfr->msg_is_ixfr ? "I" : "A"), xfr->msg_old_serial,
xfr->msg_new_serial, consumer_zone->options->name));
}
}
static void
xfrd_process_soa_info_task(struct task_list_d* task)
{
xfrd_soa_type soa;
xfrd_soa_type* soa_ptr = &soa;
xfrd_zone_type* zone;
struct xfrd_catalog_producer_zone* producer_zone;
struct xfrd_catalog_consumer_zone* consumer_zone = NULL;
zone_type* dbzone = NULL;
xfrd_xfr_type* xfr;
xfrd_xfr_type* prev_xfr;
enum soainfo_hint hint;
#ifndef NDEBUG
time_t before;
#endif
time_t acquired = 0;
DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: process SOAINFO %s",
dname_to_string(task->zname, 0)));
zone = (xfrd_zone_type*)rbtree_search(xfrd->zones, task->zname);
hint = (enum soainfo_hint)task->yesno;
if(task->size <= sizeof(struct task_list_d)+dname_total_size(
task->zname)+sizeof(uint32_t)*6 + sizeof(uint8_t)*2) {
DEBUG(DEBUG_IPC,1, (LOG_INFO, "SOAINFO for %s %s zone",
dname_to_string(task->zname,0),
hint == soainfo_bad ? "kept" : "lost"));
soa_ptr = NULL;
#ifndef NDEBUG
before = xfrd_time();
#endif
} else {
uint8_t* p = (uint8_t*)task->zname + dname_total_size(
task->zname);
memset(&soa, 0, sizeof(soa));
soa.type = htons(TYPE_SOA);
soa.klass = htons(CLASS_IN);
memmove(&soa.ttl, p, sizeof(uint32_t));
p += sizeof(uint32_t);
memmove(soa.prim_ns, p, sizeof(uint8_t));
p += sizeof(uint8_t);
memmove(soa.prim_ns+1, p, soa.prim_ns[0]);
p += soa.prim_ns[0];
memmove(soa.email, p, sizeof(uint8_t));
p += sizeof(uint8_t);
memmove(soa.email+1, p, soa.email[0]);
p += soa.email[0];
memmove(&soa.serial, p, sizeof(uint32_t));
p += sizeof(uint32_t);
memmove(&soa.refresh, p, sizeof(uint32_t));
p += sizeof(uint32_t);
memmove(&soa.retry, p, sizeof(uint32_t));
p += sizeof(uint32_t);
memmove(&soa.expire, p, sizeof(uint32_t));
p += sizeof(uint32_t);
memmove(&soa.minimum, p, sizeof(uint32_t));
DEBUG(DEBUG_IPC,1, (LOG_INFO, "SOAINFO for %s %u",
dname_to_string(task->zname,0),
(unsigned)ntohl(soa.serial)));
#ifndef NDEBUG
before = xfrd->reload_cmd_first_sent;
#endif
}
if(zone)
;
else if((producer_zone = (struct xfrd_catalog_producer_zone*)
rbtree_search(xfrd->catalog_producer_zones, task->zname))) {
struct xfrd_producer_xfr* pxfr, *next_pxfr;
DEBUG(DEBUG_IPC,1, (LOG_INFO, "Zone %s is catalog producer",
dname_to_string(task->zname,0)));
if(hint != soainfo_ok)
producer_zone->axfr = 1;
for(pxfr = producer_zone->latest_pxfr; pxfr; pxfr = next_pxfr) {
next_pxfr = pxfr->next;
DEBUG(DEBUG_IPC,1, (LOG_INFO, "pxfr for zone %s for serial %u",
dname_to_string(task->zname,0), pxfr->serial));
if(hint != soainfo_ok)
;
else if(!soa_ptr || soa_ptr->serial != htonl(pxfr->serial))
continue;
else if(xfrd->reload_failed) {
DEBUG(DEBUG_IPC, 1,
(LOG_INFO, "xfrd: zone %s mark update "
"to serial %u verified",
producer_zone->options->name,
pxfr->serial));
diff_update_commit(
producer_zone->options->name,
DIFF_VERIFIED, xfrd->nsd,
pxfr->xfrfilenumber);
return;
}
DEBUG(DEBUG_IPC, 1,
(LOG_INFO, "xfrd: zone %s delete update to "
"serial %u", producer_zone->options->name,
pxfr->serial));
xfrd_unlink_xfrfile(xfrd->nsd, pxfr->xfrfilenumber);
if((*pxfr->prev_next_ptr = pxfr->next))
pxfr->next->prev_next_ptr = pxfr->prev_next_ptr;
region_recycle(xfrd->region, pxfr, sizeof(*pxfr));
notify_handle_master_zone_soainfo(xfrd->notify_zones,
task->zname, soa_ptr);
}
return;
} else {
DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: zone %s primary zone updated",
dname_to_string(task->zname,0)));
notify_handle_master_zone_soainfo(xfrd->notify_zones,
task->zname, soa_ptr);
return;
}
if(xfrd->nsd->db
&& xfrd->catalog_consumer_zones
#ifndef MULTIPLE_CATALOG_CONSUMER_ZONES
&& xfrd->catalog_consumer_zones->count == 1
#endif
&& (consumer_zone = (struct xfrd_catalog_consumer_zone*)rbtree_search(
xfrd->catalog_consumer_zones, task->zname))) {
dbzone = namedb_find_or_create_zone( xfrd->nsd->db, task->zname
, consumer_zone->options);
}
for(xfr = zone->latest_xfr; xfr; xfr = prev_xfr) {
prev_xfr = xfr->prev;
if(!xfr->acquired) {
continue;
}
if(hint == soainfo_ok) {
if(!xfr->sent)
continue;
assert(xfr->acquired <= before);
}
if(hint == soainfo_ok && soa_ptr) {
if(!acquired) {
acquired = xfr->acquired;
}
if(xfrd->reload_failed) {
DEBUG(DEBUG_IPC, 1,
(LOG_INFO, "xfrd: zone %s mark update "
"to serial %u verified",
zone->apex_str,
xfr->msg_new_serial));
diff_update_commit(
zone->apex_str, DIFF_VERIFIED,
xfrd->nsd, xfr->xfrfilenumber);
return;
}
if(consumer_zone && dbzone &&
(soa_ptr && soa_ptr->serial == htonl(xfr->msg_new_serial)))
apply_xfrs_to_consumer_zone(
consumer_zone, dbzone, xfr);
}
DEBUG(DEBUG_IPC, 1,
(LOG_INFO, "xfrd: zone %s delete update to serial %u",
zone->apex_str,
xfr->msg_new_serial));
xfrd_delete_zone_xfr(zone, xfr);
}
switch(hint) {
case soainfo_bad:
zone->soa_disk_acquired = zone->soa_nsd_acquired;
zone->soa_disk = zone->soa_nsd;
if(xfrd_time() - zone->soa_disk_acquired
>= (time_t)ntohl(zone->soa_disk.expire))
{
xfrd_set_zone_state(zone, xfrd_zone_expired);
}
xfrd_set_timer_retry(zone);
if(zone->soa_notified_acquired != 0 &&
(zone->soa_notified.serial == 0 ||
compare_serial(ntohl(zone->soa_disk.serial),
ntohl(zone->soa_notified.serial)) >= 0))
{
zone->soa_notified_acquired = 0;
}
if(zone->soa_notified_acquired && zone->state == xfrd_zone_ok)
{
xfrd_set_zone_state(zone, xfrd_zone_refreshing);
xfrd_set_refresh_now(zone);
}
break;
case soainfo_ok:
if(xfrd->reload_failed)
break;
case soainfo_gone:
xfrd_handle_incoming_soa(zone, soa_ptr, acquired);
break;
}
}
static void
xfrd_receive_soa(int socket, int shortsoa)
{
sig_atomic_t cmd;
struct udb_base* xtask = xfrd->nsd->task[xfrd->nsd->mytask];
udb_ptr last_task, t;
xfrd_zone_type* zone;
if(!shortsoa) {
udb_ptr_init(&last_task, xtask);
RBTREE_FOR(zone, xfrd_zone_type*, xfrd->zones) {
if(zone->state == xfrd_zone_expired) {
task_new_expire(xtask, &last_task, zone->apex, 1);
}
}
udb_ptr_unlink(&last_task, xtask);
task_process_sync(xtask);
cmd = NSD_RELOAD;
if(!write_socket(socket, &cmd, sizeof(cmd))) {
log_msg(LOG_ERR, "problems sending reload xfrdtomain: %s",
strerror(errno));
}
}
if(block_read(&nsd, socket, &cmd, sizeof(cmd), -1) != sizeof(cmd) ||
cmd != NSD_RELOAD_DONE) {
if(nsd.signal_hint_shutdown)
return;
log_msg(LOG_ERR, "did not get start signal from main");
exit(1);
}
if(block_read(NULL, socket, &xfrd->reload_pid, sizeof(pid_t), -1)
!= sizeof(pid_t)) {
log_msg(LOG_ERR, "xfrd cannot get reload_pid");
}
udb_ptr_unlink(xfrd->last_task, xtask);
if(!shortsoa)
xfrd->nsd->mytask = 1 - xfrd->nsd->mytask;
xtask = xfrd->nsd->task[xfrd->nsd->mytask];
task_remap(xtask);
udb_ptr_new(&t, xtask, udb_base_get_userdata(xtask));
while(!udb_ptr_is_null(&t)) {
xfrd_process_soa_info_task(TASKLIST(&t));
udb_ptr_set_rptr(&t, xtask, &TASKLIST(&t)->next);
}
udb_ptr_unlink(&t, xtask);
task_clear(xtask);
udb_ptr_init(xfrd->last_task, xfrd->nsd->task[xfrd->nsd->mytask]);
if(!shortsoa) {
if(block_read(NULL, socket, &cmd, sizeof(cmd), -1) !=
sizeof(cmd) ||
cmd != NSD_RELOAD_DONE) {
log_msg(LOG_ERR, "did not get start signal 2 from "
"main");
exit(1);
}
} else {
RBTREE_FOR(zone, xfrd_zone_type*, xfrd->zones) {
if(zone->state == xfrd_zone_expired) {
xfrd_send_expire_notification(zone);
}
}
}
}
void
xfrd_reopen_logfile(void)
{
if (xfrd->nsd->file_rotation_ok)
log_reopen(xfrd->nsd->log_filename, 0);
}
void
xfrd_deactivate_zone(xfrd_zone_type* z)
{
if(z->is_activated) {
if(z->activated_prev)
z->activated_prev->activated_next = z->activated_next;
else xfrd->activated_first = z->activated_next;
if(z->activated_next)
z->activated_next->activated_prev = z->activated_prev;
z->is_activated = 0;
}
}
void
xfrd_del_slave_zone(xfrd_state_type* xfrd, const dname_type* dname)
{
xfrd_zone_type* z = (xfrd_zone_type*)rbtree_delete(xfrd->zones, dname);
if(!z) return;
if(z->tcp_waiting) {
if(z->tcp_waiting_prev)
z->tcp_waiting_prev->tcp_waiting_next =
z->tcp_waiting_next;
else xfrd->tcp_set->tcp_waiting_first = z->tcp_waiting_next;
if(z->tcp_waiting_next)
z->tcp_waiting_next->tcp_waiting_prev =
z->tcp_waiting_prev;
else xfrd->tcp_set->tcp_waiting_last = z->tcp_waiting_prev;
z->tcp_waiting = 0;
}
if(z->udp_waiting) {
if(z->udp_waiting_prev)
z->udp_waiting_prev->udp_waiting_next =
z->udp_waiting_next;
else xfrd->udp_waiting_first = z->udp_waiting_next;
if(z->udp_waiting_next)
z->udp_waiting_next->udp_waiting_prev =
z->udp_waiting_prev;
else xfrd->udp_waiting_last = z->udp_waiting_prev;
z->udp_waiting = 0;
}
xfrd_deactivate_zone(z);
if(z->tcp_conn != -1) {
xfrd_tcp_release(xfrd->tcp_set, z);
} else if(z->zone_handler.ev_fd != -1 && z->event_added) {
xfrd_udp_release(z);
}
if(z->event_added)
event_del(&z->zone_handler);
while(z->latest_xfr) xfrd_delete_zone_xfr(z, z->latest_xfr);
region_recycle(xfrd->region, z, sizeof(*z));
}
void
xfrd_free_namedb(struct nsd* nsd)
{
namedb_close(nsd->db);
nsd->db = 0;
}
static void
xfrd_set_timer_refresh(xfrd_zone_type* zone)
{
time_t set_refresh;
time_t set_expire;
time_t set;
if(zone->soa_disk_acquired == 0 || zone->state != xfrd_zone_ok) {
xfrd_set_timer_retry(zone);
return;
}
set_refresh = bound_soa_disk_refresh(zone);
set_expire = bound_soa_disk_expire(zone);
set = zone->soa_disk_acquired + ( set_refresh < set_expire
? set_refresh : set_expire );
xfrd_set_timer(zone, within_refresh_bounds(zone,
set > xfrd_time()
? set - xfrd_time() : XFRD_LOWERBOUND_REFRESH));
}
static void
xfrd_set_timer_retry(xfrd_zone_type* zone)
{
time_t set_retry;
time_t set_expire;
int mult;
if(zone->fresh_xfr_timeout == 0)
zone->fresh_xfr_timeout = XFRD_TRANSFER_TIMEOUT_START;
else {
zone->fresh_xfr_timeout *= 2;
if(zone->fresh_xfr_timeout > XFRD_TRANSFER_TIMEOUT_MAX)
zone->fresh_xfr_timeout = XFRD_TRANSFER_TIMEOUT_MAX;
}
mult = zone->fresh_xfr_timeout / XFRD_TRANSFER_TIMEOUT_START;
if(mult == 0) mult = 1;
if(zone->soa_disk_acquired == 0) {
xfrd_set_timer(zone,
within_retry_bounds(zone, zone->fresh_xfr_timeout
+ random_generate(zone->fresh_xfr_timeout)));
return;
}
set_retry = within_retry_bounds(zone,
ntohl(zone->soa_disk.retry) * mult);
if(zone->state == xfrd_zone_expired) {
xfrd_set_timer(zone, set_retry);
return;
}
set_expire = zone->soa_disk_acquired + bound_soa_disk_expire(zone);
if(xfrd_time() + set_retry < set_expire) {
xfrd_set_timer(zone, set_retry);
return;
}
xfrd_set_timer(zone, within_retry_bounds(zone,
set_expire > xfrd_time()
? set_expire - xfrd_time() : XFRD_LOWERBOUND_RETRY));
}
void
xfrd_handle_zone(int ATTR_UNUSED(fd), short event, void* arg)
{
xfrd_zone_type* zone = (xfrd_zone_type*)arg;
if(zone->tcp_conn != -1) {
if(event == 0)
return;
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s event tcp", zone->apex_str));
xfrd_tcp_release(xfrd->tcp_set, zone);
event = EV_TIMEOUT;
}
if((event & EV_READ)) {
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s event udp read", zone->apex_str));
xfrd_udp_read(zone);
return;
}
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s timeout", zone->apex_str));
if(zone->zone_handler.ev_fd != -1 && zone->event_added &&
(event & EV_TIMEOUT)) {
assert(zone->tcp_conn == -1);
xfrd_udp_release(zone);
}
if(zone->tcp_waiting) {
DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s skips retry, TCP connections full",
zone->apex_str));
xfrd_unset_timer(zone);
return;
}
if(zone->udp_waiting) {
DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s skips retry, UDP connections full",
zone->apex_str));
xfrd_unset_timer(zone);
return;
}
if(zone->soa_disk_acquired)
{
if (zone->state != xfrd_zone_expired &&
xfrd_time() >= zone->soa_disk_acquired
+ bound_soa_disk_expire(zone)) {
log_msg(LOG_ERR, "xfrd: zone %s has expired", zone->apex_str);
xfrd_set_zone_state(zone, xfrd_zone_expired);
}
else if(zone->state == xfrd_zone_ok &&
xfrd_time() >= zone->soa_disk_acquired
+ bound_soa_disk_refresh(zone)) {
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s is refreshing", zone->apex_str));
xfrd_set_zone_state(zone, xfrd_zone_refreshing);
}
}
if(zone->zone_handler.ev_fd == -1 && zone->tcp_conn == -1) {
xfrd_make_request(zone);
}
}
void
xfrd_make_request(xfrd_zone_type* zone)
{
if(zone->next_master != -1) {
DEBUG(DEBUG_XFRD,1, (LOG_INFO,
"xfrd zone %s use primary %i",
zone->apex_str, zone->next_master));
zone->master_num = zone->next_master;
zone->master = acl_find_num(zone->zone_options->pattern->
request_xfr, zone->master_num);
if(!zone->master) {
zone->master = zone->zone_options->pattern->request_xfr;
zone->master_num = 0;
}
zone->next_master = -1;
zone->round_num = 0;
} else {
if(zone->round_num != -1 && zone->master && zone->master->next)
{
zone->master = zone->master->next;
zone->master_num++;
} else {
zone->master = zone->zone_options->pattern->request_xfr;
zone->master_num = 0;
zone->round_num++;
}
if(zone->round_num >= XFRD_MAX_ROUNDS) {
zone->round_num = -1;
xfrd_set_timer_retry(zone);
DEBUG(DEBUG_XFRD,1, (LOG_INFO,
"xfrd zone %s makereq wait_retry, rd %d mr %d nx %d",
zone->apex_str, zone->round_num, zone->master_num, zone->next_master));
zone->multi_master_first_master = -1;
return;
}
}
if(zone->zone_options->pattern->multi_primary_check) {
if(zone->multi_master_first_master == zone->master_num &&
zone->round_num > 0 &&
zone->state != xfrd_zone_expired) {
if(zone->multi_master_update_check >= 0) {
VERBOSITY(2, (LOG_INFO, "xfrd: multi primary "
"check: zone %s completed transfers",
zone->apex_str));
}
zone->round_num = -1;
zone->multi_master_first_master = -1;
xfrd_set_timer_refresh(zone);
return;
}
if(zone->multi_master_first_master < 0) {
zone->multi_master_first_master = zone->master_num;
zone->multi_master_update_check = -1;
}
}
if (zone->master->ixfr_disabled &&
(zone->master->ixfr_disabled + XFRD_NO_IXFR_CACHE) <= time(NULL)) {
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "clear negative caching ixfr "
"disabled for primary %s num "
"%d ",
zone->master->ip_address_spec, zone->master_num));
zone->master->ixfr_disabled = 0;
}
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd zone %s make request round %d mr %d nx %d",
zone->apex_str, zone->round_num, zone->master_num, zone->next_master));
if (!zone->master->use_axfr_only && zone->soa_disk_acquired > 0 &&
!zone->master->ixfr_disabled) {
if (zone->master->allow_udp) {
xfrd_set_timer(zone, XFRD_UDP_TIMEOUT);
xfrd_udp_obtain(zone);
}
else {
xfrd_set_timer(zone, xfrd->tcp_set->tcp_timeout);
xfrd_tcp_obtain(xfrd->tcp_set, zone);
}
}
else if (zone->master->use_axfr_only || zone->soa_disk_acquired <= 0) {
xfrd_set_timer(zone, xfrd->tcp_set->tcp_timeout);
xfrd_tcp_obtain(xfrd->tcp_set, zone);
}
else if (zone->master->ixfr_disabled) {
if (zone->zone_options->pattern->allow_axfr_fallback) {
xfrd_set_timer(zone, xfrd->tcp_set->tcp_timeout);
xfrd_tcp_obtain(xfrd->tcp_set, zone);
} else {
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd zone %s axfr "
"fallback not allowed, skipping primary %s.",
zone->apex_str, zone->master->ip_address_spec));
}
}
}
static void
xfrd_udp_obtain(xfrd_zone_type* zone)
{
assert(zone->udp_waiting == 0);
if(zone->tcp_conn != -1) {
xfrd_tcp_release(xfrd->tcp_set, zone);
}
if(xfrd->udp_use_num < XFRD_MAX_UDP) {
int fd;
xfrd->udp_use_num++;
fd = xfrd_send_ixfr_request_udp(zone);
if(fd == -1)
xfrd->udp_use_num--;
else {
if(zone->event_added)
event_del(&zone->zone_handler);
memset(&zone->zone_handler, 0,
sizeof(zone->zone_handler));
event_set(&zone->zone_handler, fd,
EV_PERSIST|EV_READ|EV_TIMEOUT,
xfrd_handle_zone, zone);
if(event_base_set(xfrd->event_base, &zone->zone_handler) != 0)
log_msg(LOG_ERR, "xfrd udp: event_base_set failed");
if(event_add(&zone->zone_handler, &zone->timeout) != 0)
log_msg(LOG_ERR, "xfrd udp: event_add failed");
zone->zone_handler_flags=EV_PERSIST|EV_READ|EV_TIMEOUT;
zone->event_added = 1;
}
return;
}
zone->udp_waiting = 1;
zone->udp_waiting_next = NULL;
zone->udp_waiting_prev = xfrd->udp_waiting_last;
if(!xfrd->udp_waiting_first)
xfrd->udp_waiting_first = zone;
if(xfrd->udp_waiting_last)
xfrd->udp_waiting_last->udp_waiting_next = zone;
xfrd->udp_waiting_last = zone;
xfrd_unset_timer(zone);
}
time_t
xfrd_time()
{
if(!xfrd->got_time) {
xfrd->current_time = time(0);
xfrd->got_time = 1;
}
return xfrd->current_time;
}
void
xfrd_copy_soa(xfrd_soa_type* soa, rr_type* rr)
{
const uint8_t* rr_ns_wire = dname_name(domain_dname(rdata_domain_ref(rr)));
uint8_t rr_ns_len = domain_dname(rdata_domain_ref(rr))->name_size;
const uint8_t* rr_em_wire = dname_name(domain_dname(
rdata_domain_ref_offset(rr, sizeof(void*))));
uint8_t rr_em_len = domain_dname(rdata_domain_ref_offset(rr,
sizeof(void*)))->name_size;
uint8_t* p;
if(rr->type != TYPE_SOA || rr->rdlength != 20+2*sizeof(void*)) {
log_msg(LOG_ERR, "xfrd: copy_soa called with bad rr, type %d rrs %u.",
rr->type, (unsigned)rr->rdlength);
return;
}
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: copy_soa rr, type %d rrs %u, ttl %u.",
(int)rr->type, (unsigned)rr->rdlength, (unsigned)rr->ttl));
soa->type = htons(rr->type);
soa->klass = htons(rr->klass);
soa->ttl = htonl(rr->ttl);
soa->prim_ns[0] = rr_ns_len;
memcpy(soa->prim_ns+1, rr_ns_wire, rr_ns_len);
soa->email[0] = rr_em_len;
memcpy(soa->email+1, rr_em_wire, rr_em_len);
p = rr->rdata + 2*sizeof(void*);
memcpy(&soa->serial, p, sizeof(uint32_t));
memcpy(&soa->refresh, p+4, sizeof(uint32_t));
memcpy(&soa->retry, p+8, sizeof(uint32_t));
memcpy(&soa->expire, p+12, sizeof(uint32_t));
memcpy(&soa->minimum, p+16, sizeof(uint32_t));
DEBUG(DEBUG_XFRD,1, (LOG_INFO,
"xfrd: copy_soa rr, serial %u refresh %u retry %u expire %u",
(unsigned)ntohl(soa->serial), (unsigned)ntohl(soa->refresh),
(unsigned)ntohl(soa->retry), (unsigned)ntohl(soa->expire)));
}
static void
xfrd_set_zone_state(xfrd_zone_type* zone, enum xfrd_zone_state s)
{
if(s != zone->state) {
enum xfrd_zone_state old = zone->state;
zone->state = s;
if((s == xfrd_zone_expired || old == xfrd_zone_expired)
&& s!=old) {
xfrd_send_expire_notification(zone);
}
}
}
void
xfrd_set_refresh_now(xfrd_zone_type* zone)
{
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd zone %s is activated, state %d",
zone->apex_str, zone->state));
if(!zone->is_activated) {
zone->activated_prev = 0;
zone->activated_next = xfrd->activated_first;
if(xfrd->activated_first)
xfrd->activated_first->activated_prev = zone;
xfrd->activated_first = zone;
zone->is_activated = 1;
}
}
void
xfrd_unset_timer(xfrd_zone_type* zone)
{
assert(zone->zone_handler.ev_fd == -1);
if(zone->event_added)
event_del(&zone->zone_handler);
zone->zone_handler_flags = 0;
zone->event_added = 0;
}
void
xfrd_set_timer(xfrd_zone_type* zone, time_t t)
{
int fd = zone->zone_handler.ev_fd;
int fl = ((fd == -1)?EV_TIMEOUT:zone->zone_handler_flags);
if(t > XFRD_TRANSFER_TIMEOUT_MAX)
t = XFRD_TRANSFER_TIMEOUT_MAX;
if(t > 10) {
time_t base = t*9/10;
t = base + random_generate(t-base);
}
if(zone->event_added)
event_del(&zone->zone_handler);
else fd = -1;
zone->timeout.tv_sec = t;
zone->timeout.tv_usec = 0;
memset(&zone->zone_handler, 0, sizeof(zone->zone_handler));
event_set(&zone->zone_handler, fd, fl, xfrd_handle_zone, zone);
if(event_base_set(xfrd->event_base, &zone->zone_handler) != 0)
log_msg(LOG_ERR, "xfrd timer: event_base_set failed");
if(event_add(&zone->zone_handler, &zone->timeout) != 0)
log_msg(LOG_ERR, "xfrd timer: event_add failed");
zone->zone_handler_flags = fl;
zone->event_added = 1;
}
void
xfrd_handle_incoming_soa(xfrd_zone_type* zone,
xfrd_soa_type* soa, time_t acquired)
{
time_t seconds_since_acquired;
if(soa == NULL) {
zone->soa_nsd_acquired = 0;
zone->soa_disk_acquired = 0;
xfrd_set_zone_state(zone, xfrd_zone_refreshing);
xfrd_set_refresh_now(zone);
return;
}
if(zone->soa_nsd_acquired && soa->serial == zone->soa_nsd.serial)
return;
if(zone->soa_disk_acquired) {
int cmp = compare_serial(ntohl(soa->serial), ntohl(zone->soa_disk.serial));
if(!((cmp == 0) || (cmp < 0 && acquired != 0))) {
goto zonefile;
}
if(cmp == 0) {
acquired = zone->soa_disk_acquired;
}
{
uint32_t soa_serial, soa_nsd_serial;
soa_serial = ntohl(soa->serial);
soa_nsd_serial = ntohl(zone->soa_nsd.serial);
if (compare_serial(soa_serial, soa_nsd_serial) > 0)
log_msg(LOG_INFO, "zone %s serial %"PRIu32" is updated to %"PRIu32,
zone->apex_str, soa_nsd_serial, soa_serial);
else
log_msg(LOG_INFO, "zone %s serial is updated to %"PRIu32,
zone->apex_str, soa_serial);
}
zone->soa_nsd = *soa;
zone->soa_nsd_acquired = acquired;
xfrd->write_zonefile_needed = 1;
seconds_since_acquired =
xfrd_time() > zone->soa_disk_acquired
? xfrd_time() - zone->soa_disk_acquired : 0;
if(seconds_since_acquired < bound_soa_disk_refresh(zone))
{
xfrd_set_zone_state(zone, xfrd_zone_ok);
}
if (cmp == 0) {
zone->fresh_xfr_timeout = 0;
if(seconds_since_acquired < bound_soa_disk_refresh(zone))
{
zone->round_num = -1;
xfrd_set_timer_refresh(zone);
} else if(seconds_since_acquired < bound_soa_disk_expire(zone))
{
xfrd_set_zone_state(zone, xfrd_zone_refreshing);
xfrd_set_refresh_now(zone);
}
if(seconds_since_acquired >= bound_soa_disk_expire(zone))
{
xfrd_set_zone_state(zone, xfrd_zone_expired);
xfrd_set_refresh_now(zone);
}
if(zone->soa_notified_acquired != 0 &&
(zone->soa_notified.serial == 0 ||
compare_serial(ntohl(zone->soa_disk.serial),
ntohl(zone->soa_notified.serial)) >= 0))
{
zone->soa_notified_acquired = 0;
}
if(zone->soa_notified_acquired && zone->state == xfrd_zone_ok)
{
xfrd_set_zone_state(zone, xfrd_zone_refreshing);
xfrd_set_refresh_now(zone);
}
}
xfrd_send_notify(xfrd->notify_zones, zone->apex, &zone->soa_nsd);
return;
}
zonefile:
acquired = xfrd_time();
DEBUG(DEBUG_XFRD,1, (LOG_INFO,
"xfrd: zone %s serial %u from zonefile. refreshing",
zone->apex_str, (unsigned)ntohl(soa->serial)));
zone->soa_nsd = *soa;
zone->soa_disk = *soa;
zone->soa_nsd_acquired = acquired;
zone->soa_disk_acquired = acquired;
if(zone->soa_notified_acquired != 0 &&
(zone->soa_notified.serial == 0 ||
compare_serial(ntohl(zone->soa_disk.serial),
ntohl(zone->soa_notified.serial)) >= 0))
{
zone->soa_notified_acquired = 0;
}
xfrd_set_zone_state(zone, xfrd_zone_refreshing);
xfrd_set_refresh_now(zone);
xfrd_send_notify(xfrd->notify_zones, zone->apex, &zone->soa_nsd);
}
void
xfrd_send_expire_notification(xfrd_zone_type* zone)
{
task_new_expire(xfrd->nsd->task[xfrd->nsd->mytask], xfrd->last_task,
zone->apex, zone->state == xfrd_zone_expired);
xfrd_set_reload_timeout();
}
int
xfrd_udp_read_packet(buffer_type* packet, int fd, struct sockaddr* src,
socklen_t* srclen)
{
ssize_t received;
buffer_clear(packet);
received = recvfrom(fd, buffer_begin(packet), buffer_remaining(packet),
0, src, srclen);
if(received == -1) {
log_msg(LOG_ERR, "xfrd: recvfrom failed: %s",
strerror(errno));
return 0;
}
buffer_set_limit(packet, received);
return 1;
}
void
xfrd_udp_release(xfrd_zone_type* zone)
{
assert(zone->udp_waiting == 0);
if(zone->event_added)
event_del(&zone->zone_handler);
if(zone->zone_handler.ev_fd != -1) {
close(zone->zone_handler.ev_fd);
}
zone->zone_handler.ev_fd = -1;
zone->zone_handler_flags = 0;
zone->event_added = 0;
if(xfrd->udp_use_num == XFRD_MAX_UDP)
{
while(xfrd->udp_waiting_first) {
xfrd_zone_type* wz = xfrd->udp_waiting_first;
assert(wz->udp_waiting);
wz->udp_waiting = 0;
xfrd->udp_waiting_first = wz->udp_waiting_next;
if(wz->udp_waiting_next)
wz->udp_waiting_next->udp_waiting_prev = NULL;
if(xfrd->udp_waiting_last == wz)
xfrd->udp_waiting_last = NULL;
if(wz->tcp_conn == -1) {
int fd = xfrd_send_ixfr_request_udp(wz);
if(fd != -1) {
if(wz->event_added)
event_del(&wz->zone_handler);
memset(&wz->zone_handler, 0,
sizeof(wz->zone_handler));
event_set(&wz->zone_handler, fd,
EV_READ|EV_TIMEOUT|EV_PERSIST,
xfrd_handle_zone, wz);
if(event_base_set(xfrd->event_base,
&wz->zone_handler) != 0)
log_msg(LOG_ERR, "cannot set event_base for ixfr");
if(event_add(&wz->zone_handler, &wz->timeout) != 0)
log_msg(LOG_ERR, "cannot add event for ixfr");
wz->zone_handler_flags = EV_READ|EV_TIMEOUT|EV_PERSIST;
wz->event_added = 1;
return;
} else {
xfrd_set_refresh_now(wz);
}
}
}
}
if(xfrd->udp_use_num > 0)
xfrd->udp_use_num--;
}
void
xfrd_disable_ixfr(xfrd_zone_type* zone)
{
if(!(zone->master->ixfr_disabled &&
(zone->master->ixfr_disabled + XFRD_NO_IXFR_CACHE) <= time(NULL))) {
zone->round_num = 0;
zone->next_master = zone->master_num;
}
zone->master->ixfr_disabled = time(NULL);
}
static void
xfrd_udp_read(xfrd_zone_type* zone)
{
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s read udp data", zone->apex_str));
if(!xfrd_udp_read_packet(xfrd->packet, zone->zone_handler.ev_fd,
NULL, NULL)) {
zone->master->bad_xfr_count++;
if (zone->master->bad_xfr_count > 2) {
xfrd_disable_ixfr(zone);
zone->master->bad_xfr_count = 0;
}
xfrd_udp_release(zone);
xfrd_make_request(zone);
return;
}
switch(xfrd_handle_received_xfr_packet(zone, xfrd->packet)) {
case xfrd_packet_tcp:
xfrd_set_timer(zone, xfrd->tcp_set->tcp_timeout);
xfrd_udp_release(zone);
xfrd_tcp_obtain(xfrd->tcp_set, zone);
break;
case xfrd_packet_transfer:
if(zone->zone_options->pattern->multi_primary_check) {
xfrd_udp_release(zone);
xfrd_make_request(zone);
break;
}
case xfrd_packet_newlease:
assert(zone->round_num == -1);
xfrd_udp_release(zone);
break;
case xfrd_packet_notimpl:
xfrd_disable_ixfr(zone);
xfrd_udp_release(zone);
xfrd_make_request(zone);
break;
case xfrd_packet_more:
case xfrd_packet_drop:
xfrd_udp_release(zone);
xfrd_make_request(zone);
break;
case xfrd_packet_bad:
default:
zone->master->bad_xfr_count++;
if (zone->master->bad_xfr_count > 2) {
xfrd_disable_ixfr(zone);
zone->master->bad_xfr_count = 0;
}
xfrd_udp_release(zone);
xfrd_make_request(zone);
break;
}
}
int
xfrd_send_udp(struct acl_options* acl, buffer_type* packet,
struct acl_options* ifc)
{
#ifdef INET6
struct sockaddr_storage to;
#else
struct sockaddr_in to;
#endif
int fd, family;
socklen_t to_len = xfrd_acl_sockaddr_to(acl, &to);
if(acl->is_ipv6) {
#ifdef INET6
family = PF_INET6;
#else
return -1;
#endif
} else {
family = PF_INET;
}
fd = socket(family, SOCK_DGRAM, IPPROTO_UDP);
if(fd == -1) {
log_msg(LOG_ERR, "xfrd: cannot create udp socket to %s: %s",
acl->ip_address_spec, strerror(errno));
return -1;
}
if (!xfrd_bind_local_interface(fd, ifc, acl, 0)) {
log_msg(LOG_ERR, "xfrd: cannot bind outgoing interface '%s' to "
"udp socket: No matching ip addresses found",
ifc->ip_address_spec);
close(fd);
return -1;
}
if(sendto(fd,
buffer_current(packet),
buffer_remaining(packet), 0,
(struct sockaddr*)&to, to_len) == -1)
{
log_msg(LOG_ERR, "xfrd: sendto %s failed %s",
acl->ip_address_spec, strerror(errno));
close(fd);
return -1;
}
return fd;
}
int
xfrd_bind_local_interface(int sockd, struct acl_options* ifc,
struct acl_options* acl, int tcp)
{
#ifdef SO_LINGER
struct linger linger = {1, 0};
#endif
socklen_t frm_len;
#ifdef INET6
struct sockaddr_storage frm;
#else
struct sockaddr_in frm;
#endif
int ret = 1;
if (!ifc)
return 1;
while (ifc) {
if (ifc->is_ipv6 != acl->is_ipv6) {
ifc = ifc->next;
continue;
}
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: bind() %s to %s socket",
ifc->ip_address_spec, tcp? "tcp":"udp"));
ret = 0;
frm_len = xfrd_acl_sockaddr_frm(ifc, &frm);
if (tcp) {
#ifdef SO_REUSEADDR
if (setsockopt(sockd, SOL_SOCKET, SO_REUSEADDR, &frm,
frm_len) < 0) {
VERBOSITY(2, (LOG_WARNING, "xfrd: setsockopt "
"SO_REUSEADDR failed: %s", strerror(errno)));
}
#else
VERBOSITY(2, (LOG_WARNING, "xfrd: setsockopt SO_REUSEADDR "
"failed: SO_REUSEADDR not defined"));
#endif
if (ifc->port != 0) {
#ifdef SO_LINGER
if (setsockopt(sockd, SOL_SOCKET, SO_LINGER,
&linger, sizeof(linger)) < 0) {
VERBOSITY(2, (LOG_WARNING, "xfrd: setsockopt "
"SO_LINGER failed: %s", strerror(errno)));
}
#else
VERBOSITY(2, (LOG_WARNING, "xfrd: setsockopt SO_LINGER "
"failed: SO_LINGER not defined"));
#endif
}
}
if(bind(sockd, (struct sockaddr*)&frm, frm_len) >= 0) {
DEBUG(DEBUG_XFRD,2, (LOG_INFO, "xfrd: bind() %s to %s "
"socket was successful",
ifc->ip_address_spec, tcp? "tcp":"udp"));
return 1;
}
DEBUG(DEBUG_XFRD,2, (LOG_INFO, "xfrd: bind() %s to %s socket"
"failed: %s",
ifc->ip_address_spec, tcp? "tcp":"udp",
strerror(errno)));
log_msg(LOG_WARNING, "xfrd: could not bind source address:port to "
"socket: %s", strerror(errno));
ifc = ifc->next;
}
return ret;
}
void
xfrd_tsig_sign_request(buffer_type* packet, tsig_record_type* tsig,
struct acl_options* acl)
{
tsig_algorithm_type* algo;
assert(acl->key_options && acl->key_options->tsig_key);
algo = tsig_get_algorithm_by_name(acl->key_options->algorithm);
if(!algo) {
log_msg(LOG_ERR, "tsig unknown algorithm %s",
acl->key_options->algorithm);
return;
}
assert(algo);
tsig_init_record(tsig, algo, acl->key_options->tsig_key);
tsig_init_query(tsig, ID(packet));
tsig_prepare(tsig);
tsig_update(tsig, packet, buffer_position(packet));
tsig_sign(tsig);
tsig_append_rr(tsig, packet);
ARCOUNT_SET(packet, ARCOUNT(packet) + 1);
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "appending tsig to packet"));
tsig_prepare(tsig);
}
static int
xfrd_send_ixfr_request_udp(xfrd_zone_type* zone)
{
int fd, apex_compress = 0;
assert(zone->master);
if(zone->tcp_conn != -1) {
log_msg(LOG_ERR, "xfrd: %s tried to send udp whilst tcp engaged",
zone->apex_str);
return -1;
}
xfrd_setup_packet(xfrd->packet, TYPE_IXFR, CLASS_IN, zone->apex,
qid_generate(), &apex_compress);
zone->query_id = ID(xfrd->packet);
xfrd_prepare_zone_xfr(zone, TYPE_IXFR);
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "sent query with ID %d", zone->query_id));
NSCOUNT_SET(xfrd->packet, 1);
xfrd_write_soa_buffer(xfrd->packet, zone->apex, &zone->soa_disk,
apex_compress);
if(zone->master->key_options && zone->master->key_options->tsig_key) {
xfrd_tsig_sign_request(
xfrd->packet, &zone->latest_xfr->tsig, zone->master);
}
buffer_flip(xfrd->packet);
xfrd_set_timer(zone, XFRD_UDP_TIMEOUT);
if((fd = xfrd_send_udp(zone->master, xfrd->packet,
zone->zone_options->pattern->outgoing_interface)) == -1)
return -1;
DEBUG(DEBUG_XFRD,1, (LOG_INFO,
"xfrd sent udp request for ixfr=%u for zone %s to %s",
(unsigned)ntohl(zone->soa_disk.serial),
zone->apex_str, zone->master->ip_address_spec));
return fd;
}
static int xfrd_parse_soa_info(buffer_type* packet, xfrd_soa_type* soa)
{
if(!buffer_available(packet, 10))
return 0;
soa->type = htons(buffer_read_u16(packet));
soa->klass = htons(buffer_read_u16(packet));
soa->ttl = htonl(buffer_read_u32(packet));
if(ntohs(soa->type) != TYPE_SOA || ntohs(soa->klass) != CLASS_IN)
{
return 0;
}
if(!buffer_available(packet, buffer_read_u16(packet)) ||
!(soa->prim_ns[0] = dname_make_wire_from_packet(soa->prim_ns+1, packet, 1)) ||
!(soa->email[0] = dname_make_wire_from_packet(soa->email+1, packet, 1)))
{
return 0;
}
soa->serial = htonl(buffer_read_u32(packet));
soa->refresh = htonl(buffer_read_u32(packet));
soa->retry = htonl(buffer_read_u32(packet));
soa->expire = htonl(buffer_read_u32(packet));
soa->minimum = htonl(buffer_read_u32(packet));
return 1;
}
static int
xfrd_xfr_check_rrs(xfrd_zone_type* zone, buffer_type* packet, size_t count,
int *done, xfrd_soa_type* soa, region_type* temp)
{
const struct nsd_type_descriptor *descriptor;
uint32_t tmp_serial = 0;
uint16_t type, rrlen;
size_t i, soapos, mempos;
const dname_type* dname;
struct rr* rr;
int32_t code;
domain_table_type* owners;
for(i=0; i<count; ++i,++zone->latest_xfr->msg_rr_count)
{
if (*done) {
DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr has "
"trailing garbage", zone->apex_str));
return 0;
}
region_free_all(temp);
owners = domain_table_create(temp);
dname = dname_make_from_packet(temp, packet, 1, 1);
if(!dname) {
DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr unable "
"to parse owner name", zone->apex_str));
return 0;
}
if(!buffer_available(packet, 10)) {
DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr hdr "
"too small", zone->apex_str));
return 0;
}
soapos = buffer_position(packet);
type = buffer_read_u16(packet);
(void)buffer_read_u16(packet);
(void)buffer_read_u32(packet);
rrlen = buffer_read_u16(packet);
if(!buffer_available(packet, rrlen)) {
DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr pkt "
"too small", zone->apex_str));
return 0;
}
mempos = buffer_position(packet);
descriptor = nsd_type_descriptor(type);
code = descriptor->read_rdata(owners, rrlen, packet, &rr);
if(code < 0) {
DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr unable "
"to parse rdata %s %s %s", zone->apex_str,
dname_to_string(dname,0),
rrtype_to_string(type),
read_rdata_fail_str(code)));
return 0;
}
if(type == TYPE_SOA) {
buffer_set_position(packet, soapos);
if(!xfrd_parse_soa_info(packet, soa)) {
DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr "
"unable to parse soainfo", zone->apex_str));
return 0;
}
if(zone->latest_xfr->msg_rr_count == 1 &&
ntohl(soa->serial) != zone->latest_xfr->msg_new_serial) {
zone->latest_xfr->msg_is_ixfr = 1;
if(!zone->soa_disk_acquired) {
DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr "
"got ixfr but need axfr", zone->apex_str));
return 0;
}
if(ntohl(soa->serial) != ntohl(zone->soa_disk.serial)) {
DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr "
"bad start serial", zone->apex_str));
return 0;
}
zone->latest_xfr->msg_old_serial = ntohl(soa->serial);
tmp_serial = ntohl(soa->serial);
}
else if(ntohl(soa->serial) == zone->latest_xfr->msg_new_serial) {
if(zone->latest_xfr->msg_is_ixfr == 1) {
zone->latest_xfr->msg_is_ixfr = 2;
} else {
*done = 1;
}
}
else if (zone->latest_xfr->msg_is_ixfr) {
if(ntohl(soa->serial) > zone->latest_xfr->msg_new_serial) {
DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr "
"bad middle serial", zone->apex_str));
return 0;
}
if(ntohl(soa->serial) < tmp_serial) {
DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr "
"serial decreasing not allowed", zone->apex_str));
return 0;
}
tmp_serial = ntohl(soa->serial);
}
}
buffer_set_position(packet, mempos);
buffer_skip(packet, rrlen);
}
return 1;
}
static int
xfrd_xfr_process_tsig(xfrd_zone_type* zone, buffer_type* packet)
{
int have_tsig = 0;
assert(zone && zone->master && zone->master->key_options
&& zone->master->key_options->tsig_key && packet);
if(!tsig_find_rr(&zone->latest_xfr->tsig, packet)) {
log_msg(LOG_ERR, "xfrd: zone %s, from %s: malformed tsig RR",
zone->apex_str, zone->master->ip_address_spec);
return 0;
}
if(zone->latest_xfr->tsig.status == TSIG_OK) {
have_tsig = 1;
if (zone->latest_xfr->tsig.error_code != TSIG_ERROR_NOERROR) {
log_msg(LOG_ERR, "xfrd: zone %s, from %s: tsig error "
"(%s)", zone->apex_str,
zone->master->ip_address_spec,
tsig_error(zone->latest_xfr->tsig.error_code));
}
}
if(have_tsig) {
buffer_set_limit(packet, zone->latest_xfr->tsig.position);
ARCOUNT_SET(packet, ARCOUNT(packet) - 1);
}
tsig_update(&zone->latest_xfr->tsig, packet, buffer_limit(packet));
if(have_tsig) {
if (!tsig_verify(&zone->latest_xfr->tsig)) {
log_msg(LOG_ERR, "xfrd: zone %s, from %s: bad tsig signature",
zone->apex_str, zone->master->ip_address_spec);
return 0;
}
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s, from %s: good tsig signature",
zone->apex_str, zone->master->ip_address_spec));
tsig_prepare(&zone->latest_xfr->tsig);
}
else if(zone->latest_xfr->tsig.updates_since_last_prepare > XFRD_TSIG_MAX_UNSIGNED) {
log_msg(LOG_INFO, "xfrd: zone %s, from %s: too many consecutive "
"packets without TSIG", zone->apex_str,
zone->master->ip_address_spec);
return 0;
}
if(!have_tsig && zone->latest_xfr->msg_seq_nr == 0) {
log_msg(LOG_ERR, "xfrd: zone %s, from %s: no tsig in first packet of reply",
zone->apex_str, zone->master->ip_address_spec);
return 0;
}
return 1;
}
static enum xfrd_packet_result
xfrd_parse_received_xfr_packet(xfrd_zone_type* zone, buffer_type* packet,
xfrd_soa_type* soa)
{
size_t rr_count;
size_t qdcount = QDCOUNT(packet);
size_t ancount = ANCOUNT(packet), ancount_todo;
size_t nscount = NSCOUNT(packet);
int done = 0;
region_type* tempregion = NULL;
assert(zone->master);
if(!buffer_available(packet, QHEADERSZ)) {
log_msg(LOG_INFO, "packet too small");
return xfrd_packet_bad;
}
DEBUG(DEBUG_XFRD,2, (LOG_INFO,
"got query with ID %d and %d needed", ID(packet), zone->query_id));
if(ID(packet) != zone->query_id) {
log_msg(LOG_ERR, "xfrd: zone %s received bad query id from %s, "
"dropped",
zone->apex_str, zone->master->ip_address_spec);
return xfrd_packet_bad;
}
if(RCODE(packet) != RCODE_OK) {
if(!(verbosity < 3 && (RCODE(packet) == RCODE_IMPL ||
RCODE(packet) == RCODE_FORMAT) &&
!zone->master->ixfr_disabled &&
!zone->master->use_axfr_only)) {
log_msg(LOG_ERR, "xfrd: zone %s received error code %s from "
"%s",
zone->apex_str, rcode2str(RCODE(packet)),
zone->master->ip_address_spec);
}
if (RCODE(packet) == RCODE_IMPL ||
RCODE(packet) == RCODE_FORMAT) {
return xfrd_packet_notimpl;
}
if (RCODE(packet) != RCODE_NOTAUTH) {
return xfrd_packet_drop;
}
}
if(zone->master->key_options) {
if(!xfrd_xfr_process_tsig(zone, packet)) {
DEBUG(DEBUG_XFRD,1, (LOG_ERR, "dropping xfr reply due "
"to bad TSIG"));
return xfrd_packet_bad;
}
}
if (RCODE(packet) == RCODE_NOTAUTH) {
return xfrd_packet_drop;
}
buffer_skip(packet, QHEADERSZ);
if(qdcount > 64 || ancount > 65530 || nscount > 65530) {
DEBUG(DEBUG_XFRD,1, (LOG_ERR, "dropping xfr reply, impossibly "
"high record count"));
return xfrd_packet_bad;
}
for(rr_count = 0; rr_count < qdcount; ++rr_count) {
if (!packet_skip_rr(packet, 1)) {
log_msg(LOG_ERR, "xfrd: zone %s, from %s: bad RR in "
"question section",
zone->apex_str, zone->master->ip_address_spec);
return xfrd_packet_bad;
}
}
if(zone->latest_xfr->msg_rr_count == 0 && ancount == 0) {
if(zone->tcp_conn == -1 && TC(packet)) {
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: TC flagged"));
return xfrd_packet_tcp;
}
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: too short xfr packet: no "
"answer"));
if (nscount == 1) {
if(!packet_skip_dname(packet) || !xfrd_parse_soa_info(packet, soa)) {
DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s, from %s: "
"no SOA begins authority section",
zone->apex_str, zone->master->ip_address_spec));
return xfrd_packet_bad;
}
return xfrd_packet_notimpl;
}
return xfrd_packet_bad;
}
ancount_todo = ancount;
tempregion = region_create(xalloc, free);
if(zone->latest_xfr->msg_rr_count == 0) {
const dname_type* soaname = dname_make_from_packet(tempregion,
packet, 1, 1);
if(!soaname) {
DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s, from %s: "
"parse error in SOA record",
zone->apex_str, zone->master->ip_address_spec));
region_destroy(tempregion);
return xfrd_packet_bad;
}
if(dname_compare(soaname, zone->apex) != 0) {
DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s, from %s: "
"wrong SOA record",
zone->apex_str, zone->master->ip_address_spec));
region_destroy(tempregion);
return xfrd_packet_bad;
}
if(!xfrd_parse_soa_info(packet, soa))
{
DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s, from %s: "
"bad SOA rdata",
zone->apex_str, zone->master->ip_address_spec));
region_destroy(tempregion);
return xfrd_packet_bad;
}
if(zone->soa_disk_acquired != 0 &&
zone->state != xfrd_zone_expired &&
compare_serial(ntohl(soa->serial), ntohl(zone->soa_disk.serial)) < 0) {
DEBUG(DEBUG_XFRD,1, (LOG_INFO,
"xfrd: zone %s ignoring old serial (local: %u, remote: %u) from %s",
zone->apex_str, ntohl(zone->soa_disk.serial), ntohl(soa->serial), zone->master->ip_address_spec));
VERBOSITY(1, (LOG_INFO,
"xfrd: zone %s ignoring old serial (local: %u, remote: %u) from %s",
zone->apex_str, ntohl(zone->soa_disk.serial), ntohl(soa->serial), zone->master->ip_address_spec));
region_destroy(tempregion);
return xfrd_packet_bad;
}
if(zone->soa_disk_acquired != 0 && zone->soa_disk.serial == soa->serial) {
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s got "
"update indicating "
"current serial",
zone->apex_str));
zone->soa_disk_acquired = xfrd_time();
if(zone->soa_nsd.serial == soa->serial)
zone->soa_nsd_acquired = xfrd_time();
xfrd_set_zone_state(zone, xfrd_zone_ok);
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s is ok",
zone->apex_str));
if(zone->zone_options->pattern->multi_primary_check) {
region_destroy(tempregion);
return xfrd_packet_drop;
}
if(zone->soa_notified_acquired == 0) {
zone->round_num = -1;
xfrd_set_timer_refresh(zone);
region_destroy(tempregion);
return xfrd_packet_newlease;
}
region_destroy(tempregion);
return xfrd_packet_drop;
}
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "IXFR reply has ok serial (have \
%u, reply %u).", (unsigned)zone->soa_disk_acquired ? ntohl(zone->soa_disk.serial) : 0, (unsigned)ntohl(soa->serial)));
if(ancount == 1) {
(void)xfrd_handle_incoming_notify(zone, soa);
}
else if(zone->soa_notified_acquired && zone->soa_notified.serial &&
compare_serial(ntohl(zone->soa_notified.serial), ntohl(soa->serial)) < 0) {
zone->soa_notified.serial = soa->serial;
}
zone->latest_xfr->msg_new_serial = ntohl(soa->serial);
zone->latest_xfr->msg_rr_count = 1;
zone->latest_xfr->msg_is_ixfr = 0;
if(zone->soa_disk_acquired)
zone->latest_xfr->msg_old_serial = ntohl(zone->soa_disk.serial);
else zone->latest_xfr->msg_old_serial = 0;
ancount_todo = ancount - 1;
}
if(zone->tcp_conn == -1 && TC(packet)) {
DEBUG(DEBUG_XFRD,1, (LOG_INFO,
"xfrd: zone %s received TC from %s. retry tcp.",
zone->apex_str, zone->master->ip_address_spec));
region_destroy(tempregion);
return xfrd_packet_tcp;
}
if(zone->tcp_conn == -1 && ancount < 2) {
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: udp reply is short. Try "
"tcp anyway."));
region_destroy(tempregion);
return xfrd_packet_tcp;
}
if(!xfrd_xfr_check_rrs(zone, packet, ancount_todo, &done, soa,
tempregion))
{
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s sent bad xfr "
"reply.", zone->apex_str));
region_destroy(tempregion);
return xfrd_packet_bad;
}
region_destroy(tempregion);
if(zone->tcp_conn == -1 && done == 0) {
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: udp reply incomplete"));
return xfrd_packet_bad;
}
if(done == 0)
return xfrd_packet_more;
if(zone->master->key_options) {
if(zone->latest_xfr->tsig.updates_since_last_prepare != 0) {
log_msg(LOG_INFO, "xfrd: last packet of reply has no "
"TSIG");
return xfrd_packet_bad;
}
}
return xfrd_packet_transfer;
}
const char*
xfrd_pretty_time(time_t v)
{
struct tm* tm = localtime(&v);
static char buf[64];
if(!strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", tm))
snprintf(buf, sizeof(buf), "strftime-err-%u", (unsigned)v);
return buf;
}
static void
xfrd_free_zone_xfr(xfrd_zone_type *zone, xfrd_xfr_type *xfr)
{
if(xfr == zone->latest_xfr) {
assert(xfr->next == NULL);
if((zone->latest_xfr = xfr->prev) != NULL)
zone->latest_xfr->next = NULL;
} else {
if(xfr->next != NULL)
xfr->next->prev = xfr->prev;
if(xfr->prev != NULL)
xfr->prev->next = xfr->next;
}
tsig_delete_record(&xfr->tsig, xfrd->region);
region_recycle(xfrd->region, xfr, sizeof(*xfr));
}
void
xfrd_delete_zone_xfr(xfrd_zone_type *zone, xfrd_xfr_type *xfr)
{
if(xfr->acquired != 0 || xfr->msg_seq_nr != 0) {
xfrd_unlink_xfrfile(xfrd->nsd, xfr->xfrfilenumber);
}
xfrd_free_zone_xfr(zone, xfr);
}
xfrd_xfr_type *
xfrd_prepare_zone_xfr(xfrd_zone_type *zone, uint16_t query_type)
{
xfrd_xfr_type *xfr;
if(zone->latest_xfr != NULL && !zone->latest_xfr->acquired) {
xfrd_delete_zone_xfr(zone, zone->latest_xfr);
}
xfr = region_alloc_zero(xfrd->region, sizeof(*xfr));
if((xfr->prev = zone->latest_xfr) != NULL) {
xfr->prev->next = xfr;
}
tsig_create_record_custom(&xfr->tsig, NULL, 0, 0, 4);
zone->latest_xfr = xfr;
xfr->query_type = query_type;
return xfr;
}
enum xfrd_packet_result
xfrd_handle_received_xfr_packet(xfrd_zone_type* zone, buffer_type* packet)
{
xfrd_soa_type soa;
enum xfrd_packet_result res;
uint64_t xfrfile_size;
assert(zone->latest_xfr);
switch((res=xfrd_parse_received_xfr_packet(zone, packet, &soa)))
{
case xfrd_packet_more:
case xfrd_packet_transfer:
break;
case xfrd_packet_newlease:
return xfrd_packet_newlease;
case xfrd_packet_tcp:
return xfrd_packet_tcp;
case xfrd_packet_notimpl:
case xfrd_packet_bad:
case xfrd_packet_drop:
default:
{
if(zone->latest_xfr->msg_seq_nr > 0) {
xfrd_unlink_xfrfile(xfrd->nsd, zone->latest_xfr->xfrfilenumber);
VERBOSITY(1, (LOG_INFO, "xfrd: zone %s "
"reverted transfer %u from %s",
zone->apex_str, zone->latest_xfr->msg_rr_count?
(int)zone->latest_xfr->msg_new_serial:0,
zone->master->ip_address_spec));
zone->latest_xfr->msg_seq_nr = 0;
} else if (res == xfrd_packet_bad) {
VERBOSITY(1, (LOG_INFO, "xfrd: zone %s "
"bad transfer %u from %s",
zone->apex_str, zone->latest_xfr->msg_rr_count?
(int)zone->latest_xfr->msg_new_serial:0,
zone->master->ip_address_spec));
}
if (res == xfrd_packet_notimpl
&& zone->latest_xfr->query_type == TYPE_IXFR)
return res;
else
return xfrd_packet_bad;
}
}
if(zone->latest_xfr->msg_seq_nr == 0)
zone->latest_xfr->xfrfilenumber = xfrd->xfrfilenumber++;
diff_write_packet(dname_to_string(zone->apex,0),
zone->zone_options->pattern->pname,
zone->latest_xfr->msg_old_serial,
zone->latest_xfr->msg_new_serial,
zone->latest_xfr->msg_seq_nr,
buffer_begin(packet), buffer_limit(packet), xfrd->nsd,
zone->latest_xfr->xfrfilenumber);
if(verbosity < 4 || zone->latest_xfr->msg_seq_nr == 0)
;
else if((verbosity >= 6)
|| (verbosity >= 5 && zone->latest_xfr->msg_seq_nr % 1000 == 0)
|| (verbosity >= 4 && zone->latest_xfr->msg_seq_nr % 10000 == 0)) {
VERBOSITY(4, (LOG_INFO,
"xfrd: zone %s written received XFR packet %u from %s "
"with serial %u to disk", zone->apex_str,
zone->latest_xfr->msg_seq_nr,
zone->master->ip_address_spec,
(int)zone->latest_xfr->msg_new_serial));
}
zone->latest_xfr->msg_seq_nr++;
xfrfile_size = xfrd_get_xfrfile_size(
xfrd->nsd, zone->latest_xfr->xfrfilenumber);
if( zone->zone_options->pattern->size_limit_xfr != 0 &&
xfrfile_size > zone->zone_options->pattern->size_limit_xfr ) {
log_msg(LOG_INFO, "xfrd : transferred zone data was too large %llu", (long long unsigned)xfrfile_size);
return xfrd_packet_bad;
}
if(res == xfrd_packet_more) {
return xfrd_packet_more;
}
buffer_clear(packet);
buffer_printf(packet, "received update to serial %u at %s from %s",
(unsigned)zone->latest_xfr->msg_new_serial, xfrd_pretty_time(xfrd_time()),
zone->master->ip_address_spec);
if(zone->master->key_options) {
buffer_printf(packet, " TSIG verified with key %s",
zone->master->key_options->name);
}
if(zone->master->tls_auth_options && zone->master->tls_auth_options->auth_domain_name) {
buffer_printf(packet, " TLS authenticated with domain %s",
zone->master->tls_auth_options->auth_domain_name);
#ifdef HAVE_TLS_1_3
if (zone->tcp_conn != -1) {
struct xfrd_tcp_pipeline* tp = NULL;
struct region* tmpregion = region_create(xalloc, free);
char *cert_serial=NULL, *key_id=NULL,
*cert_algorithm=NULL, *tls_version=NULL;
for (int i = 0; i < xfrd->tcp_set->tcp_max; i++) {
struct xfrd_tcp_pipeline* test_tp = xfrd->tcp_set->tcp_state[i];
if (test_tp && test_tp->ssl && test_tp->handshake_done) {
struct xfrd_tcp_pipeline_id* zid;
RBTREE_FOR(zid, struct xfrd_tcp_pipeline_id*, test_tp->zone_per_id) {
if (zid->zone == zone) {
tp = test_tp;
break;
}
}
if (tp) break;
}
}
if(!tmpregion)
tp = NULL;
if(tp && tp->ssl) {
get_cert_info(tp->ssl, tmpregion, &cert_serial,
&key_id, &cert_algorithm, &tls_version);
} else {
tp = NULL;
}
if (tp && cert_serial && cert_serial[0] != '\0') {
buffer_printf(packet, " cert-serial:%s", cert_serial);
}
if (tp && key_id && key_id[0] != '\0') {
buffer_printf(packet, " key-id:%s", key_id);
}
if (tp && cert_algorithm && cert_algorithm[0] != '\0') {
buffer_printf(packet, " cert-algo:%s", cert_algorithm);
}
if (tp && tls_version && tls_version[0] != '\0') {
buffer_printf(packet, " tls-version:%s", tls_version);
}
region_destroy(tmpregion);
}
#endif
}
buffer_flip(packet);
diff_write_commit(zone->apex_str, zone->latest_xfr->msg_old_serial,
zone->latest_xfr->msg_new_serial, zone->latest_xfr->msg_seq_nr, 1,
(char*)buffer_begin(packet), xfrd->nsd, zone->latest_xfr->xfrfilenumber);
VERBOSITY(1, (LOG_INFO, "xfrd: zone %s committed \"%s\"",
zone->apex_str, (char*)buffer_begin(packet)));
if(xfrd->can_send_reload &&
task_new_apply_xfr(
xfrd->nsd->task[xfrd->nsd->mytask],
xfrd->last_task,
zone->apex,
zone->latest_xfr->msg_old_serial,
zone->latest_xfr->msg_new_serial,
zone->latest_xfr->xfrfilenumber))
{
zone->latest_xfr->sent = xfrd->nsd->mytask + 1;
}
zone->latest_xfr->msg_seq_nr = 0;
zone->soa_disk_acquired = zone->latest_xfr->acquired = xfrd_time();
zone->soa_disk = soa;
if(zone->soa_notified_acquired && (
zone->soa_notified.serial == 0 ||
compare_serial(ntohl(zone->soa_disk.serial),
ntohl(zone->soa_notified.serial)) >= 0))
{
zone->soa_notified_acquired = 0;
}
if(!zone->soa_notified_acquired) {
if(zone->state != xfrd_zone_expired)
xfrd_set_zone_state(zone, xfrd_zone_ok);
DEBUG(DEBUG_XFRD,1, (LOG_INFO,
"xfrd: zone %s is waiting for reload",
zone->apex_str));
if(zone->zone_options->pattern->multi_primary_check) {
zone->multi_master_update_check = zone->master_num;
xfrd_set_reload_timeout();
return xfrd_packet_transfer;
}
zone->round_num = -1;
xfrd_set_timer_refresh(zone);
xfrd_set_reload_timeout();
return xfrd_packet_transfer;
} else {
xfrd_set_reload_timeout();
return xfrd_packet_bad;
}
}
static void
xfrd_set_reload_timeout()
{
if(xfrd->nsd->options->xfrd_reload_timeout == -1)
return;
if(xfrd->reload_timeout.tv_sec == 0 ||
xfrd_time() >= (time_t)xfrd->reload_timeout.tv_sec ) {
xfrd_set_reload_now(xfrd);
xfrd->reload_timeout.tv_sec = xfrd_time() +
xfrd->nsd->options->xfrd_reload_timeout;
xfrd->reload_timeout.tv_usec = 0;
return;
}
if(xfrd->reload_added == 0) {
struct timeval tv;
tv.tv_sec = xfrd->reload_timeout.tv_sec - xfrd_time();
tv.tv_usec = 0;
if(tv.tv_sec > xfrd->nsd->options->xfrd_reload_timeout)
tv.tv_sec = xfrd->nsd->options->xfrd_reload_timeout;
memset(&xfrd->reload_handler, 0, sizeof(xfrd->reload_handler));
event_set(&xfrd->reload_handler, -1, EV_TIMEOUT,
xfrd_handle_reload, xfrd);
if(event_base_set(xfrd->event_base, &xfrd->reload_handler) != 0)
log_msg(LOG_ERR, "cannot set reload event base");
if(event_add(&xfrd->reload_handler, &tv) != 0)
log_msg(LOG_ERR, "cannot add reload event");
xfrd->reload_added = 1;
}
}
static void
xfrd_handle_reload(int ATTR_UNUSED(fd), short event, void* ATTR_UNUSED(arg))
{
assert((event & EV_TIMEOUT));
(void)event;
xfrd->reload_added = 0;
xfrd->reload_timeout.tv_sec = xfrd_time() +
xfrd->nsd->options->xfrd_reload_timeout;
xfrd_set_reload_now(xfrd);
}
void
xfrd_handle_notify_and_start_xfr(xfrd_zone_type* zone, xfrd_soa_type* soa)
{
if(xfrd_handle_incoming_notify(zone, soa)) {
if(zone->zone_handler.ev_fd == -1 && zone->tcp_conn == -1 &&
!zone->tcp_waiting && !zone->udp_waiting) {
xfrd_set_refresh_now(zone);
}
if(zone->soa_disk_acquired == 0)
zone->fresh_xfr_timeout = XFRD_TRANSFER_TIMEOUT_START;
}
}
void
xfrd_handle_passed_packet(buffer_type* packet,
int acl_num, int acl_num_xfr)
{
uint8_t qnamebuf[MAXDOMAINLEN];
uint16_t qtype, qclass;
const dname_type* dname;
region_type* tempregion = region_create(xalloc, free);
xfrd_zone_type* zone;
buffer_skip(packet, QHEADERSZ);
if(!packet_read_query_section(packet, qnamebuf, &qtype, &qclass)) {
region_destroy(tempregion);
return;
}
dname = dname_make(tempregion, qnamebuf, 1);
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: got passed packet for %s, acl "
"%d", dname_to_string(dname,0), acl_num));
zone = (xfrd_zone_type*)rbtree_search(xfrd->zones, dname);
if(!zone) {
DEBUG(DEBUG_XFRD, 1, (LOG_INFO, "xfrd: incoming packet for "
"unknown zone %s", dname_to_string(dname,0)));
region_destroy(tempregion);
return;
}
region_destroy(tempregion);
if(OPCODE(packet) == OPCODE_NOTIFY) {
xfrd_soa_type soa;
int have_soa = 0;
int next;
if(ANCOUNT(packet) == 1 && packet_skip_dname(packet) &&
xfrd_parse_soa_info(packet, &soa)) {
have_soa = 1;
}
xfrd_handle_notify_and_start_xfr(zone, have_soa?&soa:NULL);
if (acl_find_num(zone->zone_options->pattern->request_xfr,
acl_num_xfr))
next = acl_num_xfr;
else
next = find_same_master_notify(zone, acl_num);
if(next != -1) {
zone->next_master = next;
DEBUG(DEBUG_XFRD,1, (LOG_INFO,
"xfrd: notify set next primary to query %d",
next));
}
}
else {
}
}
static int
xfrd_handle_incoming_notify(xfrd_zone_type* zone, xfrd_soa_type* soa)
{
if(soa && zone->soa_disk_acquired && zone->state != xfrd_zone_expired &&
compare_serial(ntohl(soa->serial),ntohl(zone->soa_disk.serial)) <= 0)
{
DEBUG(DEBUG_XFRD,1, (LOG_INFO,
"xfrd: ignored notify %s %u old serial, zone valid "
"(soa disk serial %u)", zone->apex_str,
(unsigned)ntohl(soa->serial),
(unsigned)ntohl(zone->soa_disk.serial)));
return 0;
}
if(soa == 0) {
zone->soa_notified.serial = 0;
}
else if (zone->soa_notified_acquired == 0 ||
zone->soa_notified.serial == 0 ||
compare_serial(ntohl(soa->serial),
ntohl(zone->soa_notified.serial)) > 0)
{
zone->soa_notified = *soa;
}
zone->soa_notified_acquired = xfrd_time();
if(zone->state == xfrd_zone_ok) {
xfrd_set_zone_state(zone, xfrd_zone_refreshing);
}
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "Handle incoming notify for zone %s",
zone->apex_str));
return 1;
}
static int
find_same_master_notify(xfrd_zone_type* zone, int acl_num_nfy)
{
struct acl_options* nfy_acl = acl_find_num(zone->zone_options->pattern->
allow_notify, acl_num_nfy);
int num = 0;
struct acl_options* master = zone->zone_options->pattern->request_xfr;
if(!nfy_acl)
return -1;
while(master)
{
if(acl_addr_matches_host(nfy_acl, master))
return num;
master = master->next;
num++;
}
return -1;
}
void
xfrd_check_failed_updates(void)
{
xfrd_zone_type* zone;
xfrd_xfr_type* xfr;
xfrd_xfr_type* prev_xfr;
uint8_t sent = (xfrd->nsd->mytask == 0) + 1;
RBTREE_FOR(zone, xfrd_zone_type*, xfrd->zones)
{
if(!zone->latest_xfr)
continue;
xfr = zone->latest_xfr;
while(!xfr->sent && xfr->prev) {
xfr = xfr->prev;
}
if(xfr->sent == sent &&
(zone->soa_nsd_acquired == 0 ||
zone->soa_nsd.serial != htonl(xfr->msg_new_serial)))
{
xfrd_soa_type soa;
soa.serial = htonl(xfr->msg_new_serial);
log_msg(LOG_ERR, "xfrd: zone %s: soa serial %u update "
"failed, restarting transfer "
"(notified zone)",
zone->apex_str, xfr->msg_new_serial);
if(xfr->acquired == zone->soa_nsd_acquired) {
zone->soa_disk_acquired = 0;
zone->soa_nsd_acquired = 0;
} else {
zone->soa_disk_acquired = zone->soa_nsd_acquired;
zone->soa_disk = zone->soa_nsd;
}
memset(&soa, 0, sizeof(soa));
soa.serial = htonl(xfr->msg_new_serial);
xfrd_handle_incoming_notify(zone, &soa);
xfrd_set_timer_refresh(zone);
for(xfr = zone->latest_xfr; xfr; xfr = prev_xfr) {
prev_xfr = xfr->prev;
if(!xfr->acquired)
continue;
DEBUG(DEBUG_IPC, 1,
(LOG_INFO, "xfrd: zone %s delete "
"update to serial %u",
zone->apex_str,
xfr->msg_new_serial));
xfrd_delete_zone_xfr(zone, xfr);
}
}
}
}
void
xfrd_prepare_zones_for_reload(void)
{
xfrd_zone_type* zone;
xfrd_xfr_type* xfr;
int reload, send;
send = 1;
reload = 0;
RBTREE_FOR(zone, xfrd_zone_type*, xfrd->zones)
{
xfr = zone->latest_xfr;
while(xfr) {
if(!xfr->prev)
break;
xfr = xfr->prev;
assert(xfr->acquired);
}
while(xfr && xfr->acquired) {
if(xfrd->reload_cmd_first_sent && !xfr->sent)
break;
assert(!xfrd->reload_cmd_first_sent ||
xfrd->reload_cmd_first_sent >= xfr->acquired);
if(send) {
send = task_new_apply_xfr(
xfrd->nsd->task[xfrd->nsd->mytask],
xfrd->last_task,
zone->apex,
xfr->msg_old_serial,
xfr->msg_new_serial,
xfr->xfrfilenumber);
if(send && !reload) {
reload = 1;
xfrd_set_reload_timeout();
}
}
xfr->sent = send ? 1 + xfrd->nsd->mytask : 0;
xfr = xfr->next;
}
}
}
struct buffer*
xfrd_get_temp_buffer()
{
return xfrd->packet;
}
#ifdef USE_ZONE_STATS
static void
xfrd_process_zonestat_inc_task(xfrd_state_type* xfrd, struct task_list_d* task)
{
xfrd->zonestat_safe = (unsigned)task->oldserial;
zonestat_remap(xfrd->nsd, 0, xfrd->zonestat_safe*sizeof(struct nsdst));
xfrd->nsd->zonestatsize[0] = xfrd->zonestat_safe;
zonestat_remap(xfrd->nsd, 1, xfrd->zonestat_safe*sizeof(struct nsdst));
xfrd->nsd->zonestatsize[1] = xfrd->zonestat_safe;
}
#endif
static void
xfrd_handle_taskresult(xfrd_state_type* xfrd, struct task_list_d* task)
{
#ifndef USE_ZONE_STATS
(void)xfrd;
#endif
switch(task->task_type) {
case task_soa_info:
xfrd_process_soa_info_task(task);
break;
#ifdef USE_ZONE_STATS
case task_zonestat_inc:
xfrd_process_zonestat_inc_task(xfrd, task);
break;
#endif
default:
log_msg(LOG_WARNING, "unhandled task result in xfrd from "
"reload type %d", (int)task->task_type);
}
}
void xfrd_process_task_result(xfrd_state_type* xfrd, struct udb_base* taskudb)
{
udb_ptr t;
task_remap(taskudb);
udb_ptr_new(&t, taskudb, udb_base_get_userdata(taskudb));
while(!udb_ptr_is_null(&t)) {
xfrd_handle_taskresult(xfrd, TASKLIST(&t));
udb_ptr_set_rptr(&t, taskudb, &TASKLIST(&t)->next);
}
udb_ptr_unlink(&t, taskudb);
task_clear(taskudb);
#ifdef HAVE_SYSTEMD
sd_notify(0, "READY=1");
#endif
}
void xfrd_set_reload_now(xfrd_state_type* xfrd)
{
xfrd->need_to_send_reload = 1;
if(!(xfrd->ipc_handler_flags&EV_WRITE)) {
ipc_xfrd_set_listening(xfrd, EV_PERSIST|EV_READ|EV_WRITE);
}
}
static void
xfrd_handle_write_timer(int ATTR_UNUSED(fd), short event, void* ATTR_UNUSED(arg))
{
assert((event & EV_TIMEOUT));
(void)event;
if(xfrd->nsd->options->zonefiles_write == 0)
return;
if(!xfrd->write_zonefile_needed) {
DEBUG(DEBUG_XFRD,2, (LOG_INFO, "zonefiles write timer (nothing)"));
xfrd_write_timer_set();
return;
}
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "zonefiles write timer"));
task_new_write_zonefiles(xfrd->nsd->task[xfrd->nsd->mytask],
xfrd->last_task, NULL);
xfrd_set_reload_now(xfrd);
xfrd->write_zonefile_needed = 0;
xfrd_write_timer_set();
}
static void xfrd_write_timer_set()
{
struct timeval tv;
if(xfrd->nsd->options->zonefiles_write == 0)
return;
tv.tv_sec = xfrd->nsd->options->zonefiles_write;
tv.tv_usec = 0;
memset(&xfrd->write_timer, 0, sizeof(xfrd->write_timer));
event_set(&xfrd->write_timer, -1, EV_TIMEOUT,
xfrd_handle_write_timer, xfrd);
if(event_base_set(xfrd->event_base, &xfrd->write_timer) != 0)
log_msg(LOG_ERR, "xfrd write timer: event_base_set failed");
if(event_add(&xfrd->write_timer, &tv) != 0)
log_msg(LOG_ERR, "xfrd write timer: event_add failed");
}
static void xfrd_handle_child_timer(int ATTR_UNUSED(fd), short event,
void* ATTR_UNUSED(arg))
{
assert((event & EV_TIMEOUT));
(void)event;
xfrd->child_timer_added = 0;
}