summaryrefslogtreecommitdiff
path: root/usr.sbin/nsd
diff options
context:
space:
mode:
authorFlorian Obser <florian@cvs.openbsd.org>2016-08-31 07:31:21 +0000
committerFlorian Obser <florian@cvs.openbsd.org>2016-08-31 07:31:21 +0000
commit866be3316a80e2e2b9d2760399423d457227312c (patch)
tree9fbec078c3279dd957706999c843990538c73c52 /usr.sbin/nsd
parent78573f5c573f70e6432b9eb8014b673dbd3d9f43 (diff)
update to 4.1.11
"Working fine here." millert@ OK dlg, sthen
Diffstat (limited to 'usr.sbin/nsd')
-rw-r--r--usr.sbin/nsd/buffer.h29
-rw-r--r--usr.sbin/nsd/configlexer.lex5
-rw-r--r--usr.sbin/nsd/configparser.y56
-rw-r--r--usr.sbin/nsd/configure.ac2
-rw-r--r--usr.sbin/nsd/ipc.c638
-rw-r--r--usr.sbin/nsd/namedb.c6
-rw-r--r--usr.sbin/nsd/namedb.h2
-rw-r--r--usr.sbin/nsd/nsd-checkconf.c34
-rw-r--r--usr.sbin/nsd/nsd.conf.5.in20
-rw-r--r--usr.sbin/nsd/nsd.conf.sample.in9
-rw-r--r--usr.sbin/nsd/nsec3.c4
-rw-r--r--usr.sbin/nsd/options.c91
-rw-r--r--usr.sbin/nsd/options.h9
-rw-r--r--usr.sbin/nsd/query.c7
-rw-r--r--usr.sbin/nsd/server.c26
-rw-r--r--usr.sbin/nsd/util.h29
-rw-r--r--usr.sbin/nsd/xfrd-disk.c22
-rw-r--r--usr.sbin/nsd/xfrd-disk.h13
-rw-r--r--usr.sbin/nsd/xfrd.c30
-rw-r--r--usr.sbin/nsd/zonec.c11
20 files changed, 676 insertions, 367 deletions
diff --git a/usr.sbin/nsd/buffer.h b/usr.sbin/nsd/buffer.h
index bee7d8b29eb..9e17bc95844 100644
--- a/usr.sbin/nsd/buffer.h
+++ b/usr.sbin/nsd/buffer.h
@@ -315,6 +315,20 @@ buffer_write_u32(buffer_type *buffer, uint32_t data)
}
static inline void
+buffer_write_u64_at(buffer_type *buffer, size_t at, uint64_t data)
+{
+ assert(buffer_available_at(buffer, at, sizeof(data)));
+ write_uint64(buffer->_data + at, data);
+}
+
+static inline void
+buffer_write_u64(buffer_type *buffer, uint64_t data)
+{
+ buffer_write_u64_at(buffer, buffer->_position, data);
+ buffer->_position += sizeof(data);
+}
+
+static inline void
buffer_read_at(buffer_type *buffer, size_t at, void *data, size_t count)
{
assert(buffer_available_at(buffer, at, count));
@@ -373,6 +387,21 @@ buffer_read_u32(buffer_type *buffer)
return result;
}
+static inline uint64_t
+buffer_read_u64_at(buffer_type *buffer, size_t at)
+{
+ assert(buffer_available_at(buffer, at, sizeof(uint64_t)));
+ return read_uint64(buffer->_data + at);
+}
+
+static inline uint64_t
+buffer_read_u64(buffer_type *buffer)
+{
+ uint64_t result = buffer_read_u64_at(buffer, buffer->_position);
+ buffer->_position += sizeof(uint64_t);
+ return result;
+}
+
/*
* Print to the buffer, increasing the capacity if required using
* buffer_reserve(). The buffer's position is set to the terminating
diff --git a/usr.sbin/nsd/configlexer.lex b/usr.sbin/nsd/configlexer.lex
index 113fa2284c1..d53635298b1 100644
--- a/usr.sbin/nsd/configlexer.lex
+++ b/usr.sbin/nsd/configlexer.lex
@@ -236,6 +236,7 @@ zone{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ZONE;}
zonefile{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ZONEFILE;}
zonestats{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ZONESTATS;}
allow-notify{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ALLOW_NOTIFY;}
+size-limit-xfr{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_SIZE_LIMIT_XFR;}
request-xfr{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_REQUEST_XFR;}
notify{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_NOTIFY;}
notify-retry{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_NOTIFY_RETRY;}
@@ -268,6 +269,10 @@ zonefiles-check{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ZONEFILES_CHECK;
zonefiles-write{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ZONEFILES_WRITE;}
log-time-ascii{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_LOG_TIME_ASCII;}
round-robin{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ROUND_ROBIN;}
+max-refresh-time{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MAX_REFRESH_TIME;}
+min-refresh-time{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MIN_REFRESH_TIME;}
+max-retry-time{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MAX_RETRY_TIME;}
+min-retry-time{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MIN_RETRY_TIME;}
{NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++;}
/* Quoted strings. Strip leading and ending quotes */
diff --git a/usr.sbin/nsd/configparser.y b/usr.sbin/nsd/configparser.y
index 1d824d10b05..908966588f6 100644
--- a/usr.sbin/nsd/configparser.y
+++ b/usr.sbin/nsd/configparser.y
@@ -54,7 +54,7 @@ extern config_parser_state_t* cfg_parser;
%token VAR_TCP_MSS VAR_OUTGOING_TCP_MSS VAR_IP_FREEBIND
%token VAR_ZONEFILE
%token VAR_ZONE
-%token VAR_ALLOW_NOTIFY VAR_REQUEST_XFR VAR_NOTIFY VAR_PROVIDE_XFR
+%token VAR_ALLOW_NOTIFY VAR_REQUEST_XFR VAR_NOTIFY VAR_PROVIDE_XFR VAR_SIZE_LIMIT_XFR
%token VAR_NOTIFY_RETRY VAR_OUTGOING_INTERFACE VAR_ALLOW_AXFR_FALLBACK
%token VAR_KEY
%token VAR_ALGORITHM VAR_SECRET
@@ -69,6 +69,8 @@ extern config_parser_state_t* cfg_parser;
%token VAR_RRL_WHITELIST_RATELIMIT VAR_RRL_WHITELIST
%token VAR_ZONEFILES_CHECK VAR_ZONEFILES_WRITE VAR_LOG_TIME_ASCII
%token VAR_ROUND_ROBIN VAR_ZONESTATS VAR_REUSEPORT VAR_VERSION
+%token VAR_MAX_REFRESH_TIME VAR_MIN_REFRESH_TIME
+%token VAR_MAX_RETRY_TIME VAR_MIN_RETRY_TIME
%%
toplevelvars: /* empty */ | toplevelvars toplevelvar ;
@@ -598,7 +600,9 @@ content_pattern: pattern_name | zone_config_item;
zone_config_item: zone_zonefile | zone_allow_notify | zone_request_xfr |
zone_notify | zone_notify_retry | zone_provide_xfr |
zone_outgoing_interface | zone_allow_axfr_fallback | include_pattern |
- zone_rrl_whitelist | zone_zonestats;
+ zone_rrl_whitelist | zone_zonestats | zone_max_refresh_time |
+ zone_min_refresh_time | zone_max_retry_time | zone_min_retry_time |
+ zone_size_limit_xfr;
pattern_name: VAR_NAME STRING
{
OUTYY(("P(pattern_name:%s)\n", $2));
@@ -714,6 +718,14 @@ zone_request_xfr: VAR_REQUEST_XFR zone_request_xfr_data
{
}
;
+zone_size_limit_xfr: VAR_SIZE_LIMIT_XFR STRING
+ {
+ OUTYY(("P(size_limit_xfr:%s)\n", $2));
+ if(atoll($2) < 0)
+ yyerror("number >= 0 expected");
+ else cfg_parser->current_pattern->size_limit_xfr = atoll($2);
+ }
+ ;
zone_request_xfr_data: STRING STRING
{
acl_options_t* acl = parse_acl_info(cfg_parser->opt->region, $1, $2);
@@ -819,6 +831,46 @@ zone_rrl_whitelist: VAR_RRL_WHITELIST STRING
#endif
}
;
+zone_max_refresh_time: VAR_MAX_REFRESH_TIME STRING
+{
+ OUTYY(("P(zone_max_refresh_time:%s)\n", $2));
+ if(atoi($2) == 0 && strcmp($2, "0") != 0)
+ yyerror("number expected");
+ else {
+ cfg_parser->current_pattern->max_refresh_time = atoi($2);
+ cfg_parser->current_pattern->max_refresh_time_is_default = 0;
+ }
+};
+zone_min_refresh_time: VAR_MIN_REFRESH_TIME STRING
+{
+ OUTYY(("P(zone_min_refresh_time:%s)\n", $2));
+ if(atoi($2) == 0 && strcmp($2, "0") != 0)
+ yyerror("number expected");
+ else {
+ cfg_parser->current_pattern->min_refresh_time = atoi($2);
+ cfg_parser->current_pattern->min_refresh_time_is_default = 0;
+ }
+};
+zone_max_retry_time: VAR_MAX_RETRY_TIME STRING
+{
+ OUTYY(("P(zone_max_retry_time:%s)\n", $2));
+ if(atoi($2) == 0 && strcmp($2, "0") != 0)
+ yyerror("number expected");
+ else {
+ cfg_parser->current_pattern->max_retry_time = atoi($2);
+ cfg_parser->current_pattern->max_retry_time_is_default = 0;
+ }
+};
+zone_min_retry_time: VAR_MIN_RETRY_TIME STRING
+{
+ OUTYY(("P(zone_min_retry_time:%s)\n", $2));
+ if(atoi($2) == 0 && strcmp($2, "0") != 0)
+ yyerror("number expected");
+ else {
+ cfg_parser->current_pattern->min_retry_time = atoi($2);
+ cfg_parser->current_pattern->min_retry_time_is_default = 0;
+ }
+};
/* key: declaration */
keystart: VAR_KEY
diff --git a/usr.sbin/nsd/configure.ac b/usr.sbin/nsd/configure.ac
index 4c451bd01b3..da75edc073c 100644
--- a/usr.sbin/nsd/configure.ac
+++ b/usr.sbin/nsd/configure.ac
@@ -4,7 +4,7 @@ dnl
sinclude(acx_nlnetlabs.m4)
-AC_INIT(NSD,4.1.10,nsd-bugs@nlnetlabs.nl)
+AC_INIT(NSD,4.1.11,nsd-bugs@nlnetlabs.nl)
AC_CONFIG_HEADER([config.h])
CFLAGS="$CFLAGS"
diff --git a/usr.sbin/nsd/ipc.c b/usr.sbin/nsd/ipc.c
index 449c0514fc1..8b5cba4bb88 100644
--- a/usr.sbin/nsd/ipc.c
+++ b/usr.sbin/nsd/ipc.c
@@ -7,10 +7,11 @@
*
*/
-#include <config.h>
+#include "config.h"
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
+#include <fcntl.h>
#include "ipc.h"
#include "buffer.h"
#include "xfrd-tcp.h"
@@ -18,92 +19,46 @@
#include "namedb.h"
#include "xfrd.h"
#include "xfrd-notify.h"
+#include "difffile.h"
-/* set is_ok for the zone according to the zone message */
-static zone_type* handle_xfrd_zone_state(struct nsd* nsd, buffer_type* packet);
-/* write ipc ZONE_STATE message into the buffer */
-static void write_zone_state_packet(buffer_type* packet, zone_type* zone);
/* attempt to send NSD_STATS command to child fd */
static void send_stat_to_child(struct main_ipc_handler_data* data, int fd);
-/* write IPC expire notification msg to a buffer */
-static void xfrd_write_expire_notification(buffer_type* buffer, xfrd_zone_t* zone);
/* send reload request over the IPC channel */
static void xfrd_send_reload_req(xfrd_state_t* xfrd);
/* send quit request over the IPC channel */
static void xfrd_send_quit_req(xfrd_state_t* xfrd);
-/* get SOA INFO out of IPC packet buffer */
-static void xfrd_handle_ipc_SOAINFO(xfrd_state_t* xfrd, buffer_type* packet);
/* perform read part of handle ipc for xfrd */
-static void xfrd_handle_ipc_read(netio_handler_type *handler, xfrd_state_t* xfrd);
+static void xfrd_handle_ipc_read(struct event* handler, xfrd_state_t* xfrd);
-static zone_type*
-handle_xfrd_zone_state(struct nsd* nsd, buffer_type* packet)
+static void
+ipc_child_quit(struct nsd* nsd)
{
- uint8_t ok;
- const dname_type *dname;
- domain_type *domain;
- zone_type *zone;
-
- ok = buffer_read_u8(packet);
- dname = (dname_type*)buffer_current(packet);
- DEBUG(DEBUG_IPC,1, (LOG_INFO, "handler zone state %s is %s",
- dname_to_string(dname, NULL), ok?"ok":"expired"));
- /* find in zone_types, if does not exist, we cannot serve anyway */
- /* find zone in config, since that one always exists */
- domain = domain_table_find(nsd->db->domains, dname);
- if(!domain) {
- DEBUG(DEBUG_IPC,1, (LOG_INFO, "zone state msg, empty zone (domain %s)",
- dname_to_string(dname, NULL)));
- return NULL;
- }
- zone = domain_find_zone(domain);
- if(!zone || dname_compare(domain_dname(zone->apex), dname) != 0) {
- DEBUG(DEBUG_IPC,1, (LOG_INFO, "zone state msg, empty zone (zone %s)",
- dname_to_string(dname, NULL)));
- return NULL;
- }
- assert(zone);
- /* only update zone->is_ok if needed to minimize copy-on-write
- of memory pages shared after fork() */
- if(ok && !zone->is_ok)
- zone->is_ok = 1;
- if(!ok && zone->is_ok)
- zone->is_ok = 0;
- return zone;
+ /* call shutdown and quit routines */
+ nsd->mode = NSD_QUIT;
+#ifdef BIND8_STATS
+ bind8_stats(nsd);
+#endif /* BIND8_STATS */
+
+#if 0 /* OS collects memory pages */
+ event_base_free(event_base);
+ region_destroy(server_region);
+#endif
+ server_shutdown(nsd);
+ exit(0);
}
void
-child_handle_parent_command(netio_type *ATTR_UNUSED(netio),
- netio_handler_type *handler,
- netio_event_types_type event_types)
+child_handle_parent_command(int fd, short event, void* arg)
{
sig_atomic_t mode;
int len;
struct ipc_handler_conn_data *data =
- (struct ipc_handler_conn_data *) handler->user_data;
- if (!(event_types & NETIO_EVENT_READ)) {
+ (struct ipc_handler_conn_data *) arg;
+ if (!(event & EV_READ)) {
return;
}
- if(data->conn->is_reading) {
- int ret = conn_read(data->conn);
- if(ret == -1) {
- log_msg(LOG_ERR, "handle_parent_command: error in conn_read: %s",
- strerror(errno));
- data->conn->is_reading = 0;
- return;
- }
- if(ret == 0) {
- return; /* continue later */
- }
- /* completed */
- data->conn->is_reading = 0;
- buffer_flip(data->conn->packet);
- (void)handle_xfrd_zone_state(data->nsd, data->conn->packet);
- return;
- }
-
- if ((len = read(handler->fd, &mode, sizeof(mode))) == -1) {
+ if ((len = read(fd, &mode, sizeof(mode))) == -1) {
log_msg(LOG_ERR, "handle_parent_command: read: %s",
strerror(errno));
return;
@@ -111,21 +66,41 @@ child_handle_parent_command(netio_type *ATTR_UNUSED(netio),
if (len == 0)
{
/* parent closed the connection. Quit */
- data->nsd->mode = NSD_QUIT;
+ ipc_child_quit(data->nsd);
return;
}
switch (mode) {
case NSD_STATS:
- case NSD_QUIT:
data->nsd->mode = mode;
break;
- case NSD_ZONE_STATE:
- data->conn->is_reading = 1;
- data->conn->total_bytes = 0;
- data->conn->msglen = 0;
- data->conn->fd = handler->fd;
- buffer_clear(data->conn->packet);
+ case NSD_QUIT:
+ ipc_child_quit(data->nsd);
+ break;
+ case NSD_QUIT_CHILD:
+ /* close our listening sockets and ack */
+ server_close_all_sockets(data->nsd->udp, data->nsd->ifs);
+ server_close_all_sockets(data->nsd->tcp, data->nsd->ifs);
+ /* mode == NSD_QUIT_CHILD */
+ if(write(fd, &mode, sizeof(mode)) == -1) {
+ VERBOSITY(3, (LOG_INFO, "quit child write: %s",
+ strerror(errno)));
+ }
+ ipc_child_quit(data->nsd);
+ break;
+ case NSD_QUIT_WITH_STATS:
+#ifdef BIND8_STATS
+ DEBUG(DEBUG_IPC, 2, (LOG_INFO, "quit QUIT_WITH_STATS"));
+ /* reply with ack and stats and then quit */
+ if(!write_socket(fd, &mode, sizeof(mode))) {
+ log_msg(LOG_ERR, "cannot write quitwst to parent");
+ }
+ if(!write_socket(fd, &data->nsd->st, sizeof(data->nsd->st))) {
+ log_msg(LOG_ERR, "cannot write stats to parent");
+ }
+ fsync(fd);
+#endif /* BIND8_STATS */
+ ipc_child_quit(data->nsd);
break;
default:
log_msg(LOG_ERR, "handle_parent_command: bad mode %d",
@@ -147,38 +122,6 @@ parent_handle_xfrd_command(netio_type *ATTR_UNUSED(netio),
return;
}
- if(data->conn->is_reading) {
- /* handle ZONE_STATE forward to children */
- int ret = conn_read(data->conn);
- size_t i;
- zone_type* zone;
- if(ret == -1) {
- log_msg(LOG_ERR, "main xfrd listener: error in conn_read: %s",
- strerror(errno));
- data->conn->is_reading = 0;
- return;
- }
- if(ret == 0) {
- return; /* continue later */
- }
- /* completed */
- data->conn->is_reading = 0;
- buffer_flip(data->conn->packet);
- zone = handle_xfrd_zone_state(data->nsd, data->conn->packet);
- if(!zone)
- return;
- /* forward to all children */
- for (i = 0; i < data->nsd->child_count; ++i) {
- if(!zone->dirty[i]) {
- zone->dirty[i] = 1;
- stack_push(data->nsd->children[i].dirty_zones, zone);
- data->nsd->children[i].handler->event_types |=
- NETIO_EVENT_WRITE;
- }
- }
- return;
- }
-
if ((len = read(handler->fd, &mode, sizeof(mode))) == -1) {
log_msg(LOG_ERR, "handle_xfrd_command: read: %s",
strerror(errno));
@@ -186,29 +129,29 @@ parent_handle_xfrd_command(netio_type *ATTR_UNUSED(netio),
}
if (len == 0)
{
- DEBUG(DEBUG_IPC,1, (LOG_ERR, "handle_xfrd_command: xfrd closed channel."));
+ /* xfrd closed, we must quit */
+ DEBUG(DEBUG_IPC,1, (LOG_INFO, "handle_xfrd_command: xfrd closed channel."));
close(handler->fd);
handler->fd = -1;
+ data->nsd->mode = NSD_SHUTDOWN;
return;
}
switch (mode) {
case NSD_RELOAD:
+ DEBUG(DEBUG_IPC,1, (LOG_INFO, "parent handle xfrd command RELOAD"));
data->nsd->signal_hint_reload = 1;
break;
case NSD_QUIT:
+ case NSD_SHUTDOWN:
data->nsd->mode = mode;
break;
+ case NSD_STATS:
+ data->nsd->signal_hint_stats = 1;
+ break;
case NSD_REAP_CHILDREN:
data->nsd->signal_hint_child = 1;
break;
- case NSD_ZONE_STATE:
- data->conn->is_reading = 1;
- data->conn->total_bytes = 0;
- data->conn->msglen = 0;
- data->conn->fd = handler->fd;
- buffer_clear(data->conn->packet);
- break;
default:
log_msg(LOG_ERR, "handle_xfrd_command: bad mode %d",
(int) mode);
@@ -217,27 +160,6 @@ parent_handle_xfrd_command(netio_type *ATTR_UNUSED(netio),
}
static void
-write_zone_state_packet(buffer_type* packet, zone_type* zone)
-{
- sig_atomic_t cmd = NSD_ZONE_STATE;
- uint8_t ok = zone->is_ok;
- uint16_t sz;
- if(!zone->apex) {
- return;
- }
- sz = dname_total_size(domain_dname(zone->apex)) + 1;
- sz = htons(sz);
-
- buffer_clear(packet);
- buffer_write(packet, &cmd, sizeof(cmd));
- buffer_write(packet, &sz, sizeof(sz));
- buffer_write(packet, &ok, sizeof(ok));
- buffer_write(packet, domain_dname(zone->apex),
- dname_total_size(domain_dname(zone->apex)));
- buffer_flip(packet);
-}
-
-static void
send_stat_to_child(struct main_ipc_handler_data* data, int fd)
{
sig_atomic_t cmd = NSD_STATS;
@@ -251,6 +173,7 @@ send_stat_to_child(struct main_ipc_handler_data* data, int fd)
data->child->need_to_send_STATS = 0;
}
+#ifndef NDEBUG
int packet_read_query_section(buffer_type *packet, uint8_t* dest, uint16_t* qtype, uint16_t* qclass);
static void
debug_print_fwd_name(int ATTR_UNUSED(len), buffer_type* packet, int acl_num)
@@ -273,11 +196,16 @@ debug_print_fwd_name(int ATTR_UNUSED(len), buffer_type* packet, int acl_num)
buffer_set_position(packet, bufpos);
region_destroy(tempregion);
}
+#endif
static void
send_quit_to_child(struct main_ipc_handler_data* data, int fd)
{
+#ifdef BIND8_STATS
+ sig_atomic_t cmd = NSD_QUIT_WITH_STATS;
+#else
sig_atomic_t cmd = NSD_QUIT;
+#endif
if(write(fd, &cmd, sizeof(cmd)) == -1) {
if(errno == EAGAIN || errno == EINTR)
return; /* try again later */
@@ -290,6 +218,116 @@ send_quit_to_child(struct main_ipc_handler_data* data, int fd)
(int)data->child->pid));
}
+/** the child is done, mark it as exited */
+static void
+child_is_done(struct nsd* nsd, int fd)
+{
+ size_t i;
+ if(fd != -1) close(fd);
+ for(i=0; i<nsd->child_count; ++i)
+ if(nsd->children[i].child_fd == fd) {
+ nsd->children[i].child_fd = -1;
+ nsd->children[i].handler->fd = -1;
+ if(nsd->children[i].need_to_exit) {
+ DEBUG(DEBUG_IPC,1, (LOG_INFO, "server %d is done",
+ (int)nsd->children[i].pid));
+ nsd->children[i].has_exited = 1;
+ } else {
+ log_msg(LOG_WARNING,
+ "server %d died unexpectedly, restarting",
+ (int)nsd->children[i].pid);
+ /* this child is now going to be re-forked as
+ * a subprocess of this server-main, and if a
+ * reload is in progress the other children
+ * are subprocesses of reload. Until the
+ * reload is done and they are all reforked. */
+ nsd->children[i].pid = -1;
+ nsd->restart_children = 1;
+ }
+ }
+ parent_check_all_children_exited(nsd);
+}
+
+#ifdef BIND8_STATS
+/** add stats to total */
+void
+stats_add(struct nsdst* total, struct nsdst* s)
+{
+ unsigned i;
+ for(i=0; i<sizeof(total->qtype)/sizeof(stc_t); i++)
+ total->qtype[i] += s->qtype[i];
+ for(i=0; i<sizeof(total->qclass)/sizeof(stc_t); i++)
+ total->qclass[i] += s->qclass[i];
+ total->qudp += s->qudp;
+ total->qudp6 += s->qudp6;
+ total->ctcp += s->ctcp;
+ total->ctcp6 += s->ctcp6;
+ for(i=0; i<sizeof(total->rcode)/sizeof(stc_t); i++)
+ total->rcode[i] += s->rcode[i];
+ for(i=0; i<sizeof(total->opcode)/sizeof(stc_t); i++)
+ total->opcode[i] += s->opcode[i];
+ total->dropped += s->dropped;
+ total->truncated += s->truncated;
+ total->wrongzone += s->wrongzone;
+ total->txerr += s->txerr;
+ total->rxerr += s->rxerr;
+ total->edns += s->edns;
+ total->ednserr += s->ednserr;
+ total->raxfr += s->raxfr;
+ total->nona += s->nona;
+
+ total->db_disk = s->db_disk;
+ total->db_mem = s->db_mem;
+}
+
+/** subtract stats from total */
+void
+stats_subtract(struct nsdst* total, struct nsdst* s)
+{
+ unsigned i;
+ for(i=0; i<sizeof(total->qtype)/sizeof(stc_t); i++)
+ total->qtype[i] -= s->qtype[i];
+ for(i=0; i<sizeof(total->qclass)/sizeof(stc_t); i++)
+ total->qclass[i] -= s->qclass[i];
+ total->qudp -= s->qudp;
+ total->qudp6 -= s->qudp6;
+ total->ctcp -= s->ctcp;
+ total->ctcp6 -= s->ctcp6;
+ for(i=0; i<sizeof(total->rcode)/sizeof(stc_t); i++)
+ total->rcode[i] -= s->rcode[i];
+ for(i=0; i<sizeof(total->opcode)/sizeof(stc_t); i++)
+ total->opcode[i] -= s->opcode[i];
+ total->dropped -= s->dropped;
+ total->truncated -= s->truncated;
+ total->wrongzone -= s->wrongzone;
+ total->txerr -= s->txerr;
+ total->rxerr -= s->rxerr;
+ total->edns -= s->edns;
+ total->ednserr -= s->ednserr;
+ total->raxfr -= s->raxfr;
+ total->nona -= s->nona;
+}
+
+#define FINAL_STATS_TIMEOUT 10 /* seconds */
+static void
+read_child_stats(struct nsd* nsd, struct nsd_child* child, int fd)
+{
+ struct nsdst s;
+ errno=0;
+ if(block_read(nsd, fd, &s, sizeof(s), FINAL_STATS_TIMEOUT)!=sizeof(s)) {
+ log_msg(LOG_ERR, "problems reading finalstats from server "
+ "%d: %s", (int)child->pid, strerror(errno));
+ } else {
+ stats_add(&nsd->st, &s);
+ child->query_count = s.qudp + s.qudp6 + s.ctcp + s.ctcp6;
+ /* we know that the child is going to close the connection
+ * now (this is an ACK of the QUIT_W_STATS so we know the
+ * child is done, no longer sending e.g. NOTIFY contents) */
+ child_is_done(nsd, fd);
+ }
+}
+#endif /* BIND8_STATS */
+
void
parent_handle_child_command(netio_type *ATTR_UNUSED(netio),
netio_handler_type *handler,
@@ -302,43 +340,14 @@ parent_handle_child_command(netio_type *ATTR_UNUSED(netio),
/* do a nonblocking write to the child if it is ready. */
if (event_types & NETIO_EVENT_WRITE) {
- if(!data->busy_writing_zone_state &&
- !data->child->need_to_send_STATS &&
- !data->child->need_to_send_QUIT &&
- !data->child->need_to_exit &&
- data->child->dirty_zones->num > 0) {
- /* create packet from next dirty zone */
- zone_type* zone = (zone_type*)stack_pop(data->child->dirty_zones);
- assert(zone);
- zone->dirty[data->child_num] = 0;
- data->busy_writing_zone_state = 1;
- write_zone_state_packet(data->write_conn->packet, zone);
- data->write_conn->msglen = buffer_limit(data->write_conn->packet);
- data->write_conn->total_bytes = sizeof(uint16_t); /* len bytes already in packet */
- data->write_conn->fd = handler->fd;
- }
- if(data->busy_writing_zone_state) {
- /* write more of packet */
- int ret = conn_write(data->write_conn);
- if(ret == -1) {
- log_msg(LOG_ERR, "handle_child_cmd %d: could not write: %s",
- (int)data->child->pid, strerror(errno));
- data->busy_writing_zone_state = 0;
- } else if(ret == 1) {
- data->busy_writing_zone_state = 0; /* completed */
- }
- } else if(data->child->need_to_send_STATS &&
- !data->child->need_to_exit) {
+ if(data->child->need_to_send_STATS &&
+ !data->child->need_to_exit) {
send_stat_to_child(data, handler->fd);
} else if(data->child->need_to_send_QUIT) {
send_quit_to_child(data, handler->fd);
if(!data->child->need_to_send_QUIT)
handler->event_types = NETIO_EVENT_READ;
- }
- if(!data->busy_writing_zone_state &&
- !data->child->need_to_send_STATS &&
- !data->child->need_to_send_QUIT &&
- data->child->dirty_zones->num == 0) {
+ } else {
handler->event_types = NETIO_EVENT_READ;
}
}
@@ -396,10 +405,10 @@ parent_handle_child_command(netio_type *ATTR_UNUSED(netio),
/* read rest later */
return;
}
- /* read the acl number */
+ /* read the acl numbers */
got_acl = data->got_bytes - sizeof(data->total_bytes) - data->total_bytes;
if((len = read(handler->fd, (char*)&data->acl_num+got_acl,
- sizeof(data->acl_num)-got_acl)) == -1 ) {
+ sizeof(data->acl_num)+sizeof(data->acl_xfr)-got_acl)) == -1 ) {
log_msg(LOG_ERR, "handle_child_command: read: %s",
strerror(errno));
return;
@@ -411,7 +420,7 @@ parent_handle_child_command(netio_type *ATTR_UNUSED(netio),
}
got_acl += len;
data->got_bytes += len;
- if(got_acl >= (int)sizeof(data->acl_num)) {
+ if(got_acl >= (int)(sizeof(data->acl_num)+sizeof(data->acl_xfr))) {
uint16_t len = htons(data->total_bytes);
DEBUG(DEBUG_IPC,2, (LOG_INFO,
"main fwd passed packet write %d", (int)data->got_bytes));
@@ -426,7 +435,9 @@ parent_handle_child_command(netio_type *ATTR_UNUSED(netio),
!write_socket(*data->xfrd_sock, buffer_begin(data->packet),
data->total_bytes) ||
!write_socket(*data->xfrd_sock, &data->acl_num,
- sizeof(data->acl_num))) {
+ sizeof(data->acl_num)) ||
+ !write_socket(*data->xfrd_sock, &data->acl_xfr,
+ sizeof(data->acl_xfr))) {
log_msg(LOG_ERR, "error in ipc fwd main2xfrd: %s",
strerror(errno));
}
@@ -442,18 +453,7 @@ parent_handle_child_command(netio_type *ATTR_UNUSED(netio),
}
if (len == 0)
{
- size_t i;
- if(handler->fd > 0) close(handler->fd);
- for(i=0; i<data->nsd->child_count; ++i)
- if(data->nsd->children[i].child_fd == handler->fd) {
- data->nsd->children[i].child_fd = -1;
- data->nsd->children[i].has_exited = 1;
- DEBUG(DEBUG_IPC,1, (LOG_INFO,
- "server %d closed cmd channel",
- (int) data->nsd->children[i].pid));
- }
- handler->fd = -1;
- parent_check_all_children_exited(data->nsd);
+ child_is_done(data->nsd, handler->fd);
return;
}
@@ -461,6 +461,11 @@ parent_handle_child_command(netio_type *ATTR_UNUSED(netio),
case NSD_QUIT:
data->nsd->mode = mode;
break;
+#ifdef BIND8_STATS
+ case NSD_QUIT_WITH_STATS:
+ read_child_stats(data->nsd, data->child, handler->fd);
+ break;
+#endif /* BIND8_STATS */
case NSD_STATS:
data->nsd->signal_hint_stats = 1;
break;
@@ -514,11 +519,12 @@ parent_handle_reload_command(netio_type *ATTR_UNUSED(netio),
}
if (len == 0)
{
- if(handler->fd > 0) {
+ if(handler->fd != -1) {
close(handler->fd);
handler->fd = -1;
}
log_msg(LOG_ERR, "handle_reload_cmd: reload closed cmd channel");
+ nsd->reload_failed = 1;
return;
}
switch (mode) {
@@ -528,7 +534,7 @@ parent_handle_reload_command(netio_type *ATTR_UNUSED(netio),
for(i=0; i < nsd->child_count; i++) {
nsd->children[i].need_to_exit = 1;
if(nsd->children[i].pid > 0 &&
- nsd->children[i].child_fd > 0) {
+ nsd->children[i].child_fd != -1) {
nsd->children[i].need_to_send_QUIT = 1;
nsd->children[i].handler->event_types
|= NETIO_EVENT_WRITE;
@@ -547,33 +553,16 @@ parent_handle_reload_command(netio_type *ATTR_UNUSED(netio),
}
static void
-xfrd_write_expire_notification(buffer_type* buffer, xfrd_zone_t* zone)
-{
- sig_atomic_t cmd = NSD_ZONE_STATE;
- uint8_t ok = 1;
- uint16_t sz = dname_total_size(zone->apex) + 1;
- sz = htons(sz);
- if(zone->state == xfrd_zone_expired)
- ok = 0;
-
- DEBUG(DEBUG_IPC,1, (LOG_INFO,
- "xfrd encoding ipc zone state msg for zone %s state %d.",
- zone->apex_str, (int)zone->state));
-
- buffer_clear(buffer);
- buffer_write(buffer, &cmd, sizeof(cmd));
- buffer_write(buffer, &sz, sizeof(sz));
- buffer_write(buffer, &ok, sizeof(ok));
- buffer_write(buffer, zone->apex, dname_total_size(zone->apex));
- buffer_flip(buffer);
-}
-
-static void
xfrd_send_reload_req(xfrd_state_t* xfrd)
{
sig_atomic_t req = NSD_RELOAD;
+ uint64_t p = xfrd->last_task->data;
+ udb_ptr_unlink(xfrd->last_task, xfrd->nsd->task[xfrd->nsd->mytask]);
+ task_process_sync(xfrd->nsd->task[xfrd->nsd->mytask]);
/* ask server_main for a reload */
- if(write(xfrd->ipc_handler.fd, &req, sizeof(req)) == -1) {
+ if(write(xfrd->ipc_handler.ev_fd, &req, sizeof(req)) == -1) {
+ udb_ptr_init(xfrd->last_task, xfrd->nsd->task[xfrd->nsd->mytask]);
+ udb_ptr_set(xfrd->last_task, xfrd->nsd->task[xfrd->nsd->mytask], p);
if(errno == EAGAIN || errno == EINTR)
return; /* try again later */
log_msg(LOG_ERR, "xfrd: problems sending reload command: %s",
@@ -581,21 +570,55 @@ xfrd_send_reload_req(xfrd_state_t* xfrd)
return;
}
DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: asked nsd to reload new updates"));
+ /* swapped task to other side, start to use other task udb. */
+ xfrd->nsd->mytask = 1 - xfrd->nsd->mytask;
+ task_remap(xfrd->nsd->task[xfrd->nsd->mytask]);
+ udb_ptr_init(xfrd->last_task, xfrd->nsd->task[xfrd->nsd->mytask]);
+ assert(udb_base_get_userdata(xfrd->nsd->task[xfrd->nsd->mytask])->data == 0);
+
xfrd_prepare_zones_for_reload();
xfrd->reload_cmd_last_sent = xfrd_time();
xfrd->need_to_send_reload = 0;
xfrd->can_send_reload = 0;
}
+void
+ipc_xfrd_set_listening(struct xfrd_state* xfrd, short mode)
+{
+ int fd = xfrd->ipc_handler.ev_fd;
+ struct event_base* base = xfrd->event_base;
+ event_del(&xfrd->ipc_handler);
+ event_set(&xfrd->ipc_handler, fd, mode, xfrd_handle_ipc, xfrd);
+ if(event_base_set(base, &xfrd->ipc_handler) != 0)
+ log_msg(LOG_ERR, "ipc: cannot set event_base");
+ /* no timeout for IPC events */
+ if(event_add(&xfrd->ipc_handler, NULL) != 0)
+ log_msg(LOG_ERR, "ipc: cannot add event");
+ xfrd->ipc_handler_flags = mode;
+}
+
+static void
+xfrd_send_shutdown_req(xfrd_state_t* xfrd)
+{
+ sig_atomic_t cmd = NSD_SHUTDOWN;
+ xfrd->ipc_send_blocked = 1;
+ ipc_xfrd_set_listening(xfrd, EV_PERSIST|EV_READ);
+ DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: ipc send shutdown"));
+ if(!write_socket(xfrd->ipc_handler.ev_fd, &cmd, sizeof(cmd))) {
+ log_msg(LOG_ERR, "xfrd: error writing shutdown to main: %s",
+ strerror(errno));
+ }
+ xfrd->need_to_send_shutdown = 0;
+}
+
static void
xfrd_send_quit_req(xfrd_state_t* xfrd)
{
sig_atomic_t cmd = NSD_QUIT;
xfrd->ipc_send_blocked = 1;
- xfrd->ipc_handler.event_types &= (~NETIO_EVENT_WRITE);
- xfrd->sending_zone_state = 0;
+ ipc_xfrd_set_listening(xfrd, EV_PERSIST|EV_READ);
DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: ipc send ackreload(quit)"));
- if(write_socket(xfrd->ipc_handler.fd, &cmd, sizeof(cmd)) == -1) {
+ if(!write_socket(xfrd->ipc_handler.ev_fd, &cmd, sizeof(cmd))) {
log_msg(LOG_ERR, "xfrd: error writing ack to main: %s",
strerror(errno));
}
@@ -603,119 +626,55 @@ xfrd_send_quit_req(xfrd_state_t* xfrd)
}
static void
-xfrd_handle_ipc_SOAINFO(xfrd_state_t* xfrd, buffer_type* packet)
+xfrd_send_stats(xfrd_state_t* xfrd)
{
- xfrd_soa_t soa;
- xfrd_soa_t* soa_ptr = &soa;
- xfrd_zone_t* zone;
- /* dname is sent in memory format */
- const dname_type* dname = (const dname_type*)buffer_begin(packet);
-
- /* find zone and decode SOA */
- zone = (xfrd_zone_t*)rbtree_search(xfrd->zones, dname);
- buffer_skip(packet, dname_total_size(dname));
-
- if(!buffer_available(packet, sizeof(uint32_t)*6 + sizeof(uint8_t)*2)) {
- /* NSD has zone without any info */
- DEBUG(DEBUG_IPC,1, (LOG_INFO, "SOAINFO for %s lost zone",
- dname_to_string(dname,0)));
- soa_ptr = NULL;
- } else {
- /* read soa info */
- memset(&soa, 0, sizeof(soa));
- /* left out type, klass, count for speed */
- soa.type = htons(TYPE_SOA);
- soa.klass = htons(CLASS_IN);
- soa.ttl = htonl(buffer_read_u32(packet));
- soa.rdata_count = htons(7);
- soa.prim_ns[0] = buffer_read_u8(packet);
- if(!buffer_available(packet, soa.prim_ns[0]))
- return;
- buffer_read(packet, soa.prim_ns+1, soa.prim_ns[0]);
- soa.email[0] = buffer_read_u8(packet);
- if(!buffer_available(packet, soa.email[0]))
- return;
- buffer_read(packet, soa.email+1, soa.email[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));
- DEBUG(DEBUG_IPC,1, (LOG_INFO, "SOAINFO for %s %u",
- dname_to_string(dname,0), ntohl(soa.serial)));
- }
-
- if(!zone) {
- DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: zone %s master zone updated",
- dname_to_string(dname,0)));
- notify_handle_master_zone_soainfo(xfrd->notify_zones,
- dname, soa_ptr);
- return;
+ sig_atomic_t cmd = NSD_STATS;
+ DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: ipc send stats"));
+ if(!write_socket(xfrd->ipc_handler.ev_fd, &cmd, sizeof(cmd))) {
+ log_msg(LOG_ERR, "xfrd: error writing stats to main: %s",
+ strerror(errno));
}
- xfrd_handle_incoming_soa(zone, soa_ptr, xfrd_time());
+ xfrd->need_to_send_stats = 0;
}
void
-xfrd_handle_ipc(netio_type* ATTR_UNUSED(netio),
- netio_handler_type *handler,
- netio_event_types_type event_types)
+xfrd_handle_ipc(int ATTR_UNUSED(fd), short event, void* arg)
{
- xfrd_state_t* xfrd = (xfrd_state_t*)handler->user_data;
- if ((event_types & NETIO_EVENT_READ))
+ xfrd_state_t* xfrd = (xfrd_state_t*)arg;
+ if ((event & EV_READ))
{
/* first attempt to read as a signal from main
* could block further send operations */
- xfrd_handle_ipc_read(handler, xfrd);
+ xfrd_handle_ipc_read(&xfrd->ipc_handler, xfrd);
}
- if ((event_types & NETIO_EVENT_WRITE))
+ if ((event & EV_WRITE))
{
- if(xfrd->ipc_send_blocked) { /* wait for SOA_END */
- handler->event_types = NETIO_EVENT_READ;
+ if(xfrd->ipc_send_blocked) { /* wait for RELOAD_DONE */
+ ipc_xfrd_set_listening(xfrd, EV_PERSIST|EV_READ);
return;
}
- /* if necessary prepare a packet */
- if(!(xfrd->can_send_reload && xfrd->need_to_send_reload) &&
- !xfrd->need_to_send_quit &&
- !xfrd->sending_zone_state &&
- xfrd->dirty_zones->num > 0) {
- xfrd_zone_t* zone = (xfrd_zone_t*)stack_pop(xfrd->dirty_zones);
- assert(zone);
- zone->dirty = 0;
- xfrd->sending_zone_state = 1;
- xfrd_write_expire_notification(xfrd->ipc_conn_write->packet, zone);
- xfrd->ipc_conn_write->msglen = buffer_limit(xfrd->ipc_conn_write->packet);
- /* skip length bytes; they are encoded in the packet, after cmd */
- xfrd->ipc_conn_write->total_bytes = sizeof(uint16_t);
- }
- /* write a bit */
- if(xfrd->sending_zone_state) {
- /* call conn_write */
- int ret = conn_write(xfrd->ipc_conn_write);
- if(ret == -1) {
- log_msg(LOG_ERR, "xfrd: error in write ipc: %s", strerror(errno));
- xfrd->sending_zone_state = 0;
- }
- else if(ret == 1) { /* done */
- xfrd->sending_zone_state = 0;
- }
+ if(xfrd->need_to_send_shutdown) {
+ xfrd_send_shutdown_req(xfrd);
} else if(xfrd->need_to_send_quit) {
xfrd_send_quit_req(xfrd);
} else if(xfrd->can_send_reload && xfrd->need_to_send_reload) {
xfrd_send_reload_req(xfrd);
+ } else if(xfrd->need_to_send_stats) {
+ xfrd_send_stats(xfrd);
}
if(!(xfrd->can_send_reload && xfrd->need_to_send_reload) &&
+ !xfrd->need_to_send_shutdown &&
!xfrd->need_to_send_quit &&
- !xfrd->sending_zone_state &&
- xfrd->dirty_zones->num == 0) {
- handler->event_types = NETIO_EVENT_READ; /* disable writing for now */
+ !xfrd->need_to_send_stats) {
+ /* disable writing for now */
+ ipc_xfrd_set_listening(xfrd, EV_PERSIST|EV_READ);
}
}
}
static void
-xfrd_handle_ipc_read(netio_handler_type *handler, xfrd_state_t* xfrd)
+xfrd_handle_ipc_read(struct event* handler, xfrd_state_t* xfrd)
{
sig_atomic_t cmd;
int len;
@@ -723,6 +682,7 @@ xfrd_handle_ipc_read(netio_handler_type *handler, xfrd_state_t* xfrd)
if(xfrd->ipc_conn->is_reading==2) {
buffer_type* tmp = xfrd->ipc_pass;
uint32_t acl_num;
+ int32_t acl_xfr;
/* read acl_num */
int ret = conn_read(xfrd->ipc_conn);
if(ret == -1) {
@@ -737,11 +697,13 @@ xfrd_handle_ipc_read(netio_handler_type *handler, xfrd_state_t* xfrd)
xfrd->ipc_conn->packet = tmp;
xfrd->ipc_conn->is_reading = 0;
acl_num = buffer_read_u32(xfrd->ipc_pass);
- xfrd_handle_passed_packet(xfrd->ipc_conn->packet, acl_num);
+ acl_xfr = (int32_t)buffer_read_u32(xfrd->ipc_pass);
+ xfrd_handle_passed_packet(xfrd->ipc_conn->packet, acl_num, acl_xfr);
return;
}
if(xfrd->ipc_conn->is_reading) {
/* reading an IPC message */
+ buffer_type* tmp;
int ret = conn_read(xfrd->ipc_conn);
if(ret == -1) {
log_msg(LOG_ERR, "xfrd: error in read ipc: %s", strerror(errno));
@@ -751,26 +713,22 @@ xfrd_handle_ipc_read(netio_handler_type *handler, xfrd_state_t* xfrd)
if(ret == 0)
return;
buffer_flip(xfrd->ipc_conn->packet);
- if(xfrd->ipc_is_soa) {
- xfrd->ipc_conn->is_reading = 0;
- xfrd_handle_ipc_SOAINFO(xfrd, xfrd->ipc_conn->packet);
- } else {
- /* use ipc_conn to read remaining data as well */
- buffer_type* tmp = xfrd->ipc_pass;
- xfrd->ipc_conn->is_reading=2;
- xfrd->ipc_pass = xfrd->ipc_conn->packet;
- xfrd->ipc_conn->packet = tmp;
- xfrd->ipc_conn->total_bytes = sizeof(xfrd->ipc_conn->msglen);
- xfrd->ipc_conn->msglen = sizeof(uint32_t);
- buffer_clear(xfrd->ipc_conn->packet);
- buffer_set_limit(xfrd->ipc_conn->packet, xfrd->ipc_conn->msglen);
- }
+ /* use ipc_conn to read remaining data as well */
+ tmp = xfrd->ipc_pass;
+ xfrd->ipc_conn->is_reading=2;
+ xfrd->ipc_pass = xfrd->ipc_conn->packet;
+ xfrd->ipc_conn->packet = tmp;
+ xfrd->ipc_conn->total_bytes = sizeof(xfrd->ipc_conn->msglen);
+ xfrd->ipc_conn->msglen = 2*sizeof(uint32_t);
+ buffer_clear(xfrd->ipc_conn->packet);
+ buffer_set_limit(xfrd->ipc_conn->packet, xfrd->ipc_conn->msglen);
return;
}
- if((len = read(handler->fd, &cmd, sizeof(cmd))) == -1) {
- log_msg(LOG_ERR, "xfrd_handle_ipc: read: %s",
- strerror(errno));
+ if((len = read(handler->ev_fd, &cmd, sizeof(cmd))) == -1) {
+ if(errno != EINTR && errno != EAGAIN)
+ log_msg(LOG_ERR, "xfrd_handle_ipc: read: %s",
+ strerror(errno));
return;
}
if(len == 0)
@@ -784,48 +742,47 @@ xfrd_handle_ipc_read(netio_handler_type *handler, xfrd_state_t* xfrd)
switch(cmd) {
case NSD_QUIT:
case NSD_SHUTDOWN:
- DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: main send shutdown cmd."));
+ DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: main sent shutdown cmd."));
xfrd->shutdown = 1;
break;
- case NSD_SOA_BEGIN:
- DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: ipc recv SOA_BEGIN"));
- /* reload starts sending SOA INFOs; don't block */
- xfrd->parent_soa_info_pass = 1;
- /* reset the nonblocking ipc write;
- the new parent does not want half a packet */
- xfrd->sending_zone_state = 0;
- break;
- case NSD_SOA_INFO:
- DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: ipc recv SOA_INFO"));
- assert(xfrd->parent_soa_info_pass);
- xfrd->ipc_is_soa = 1;
- xfrd->ipc_conn->is_reading = 1;
- break;
- case NSD_SOA_END:
+ case NSD_RELOAD_DONE:
/* reload has finished */
- DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: ipc recv SOA_END"));
+ DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: ipc recv RELOAD_DONE"));
+ if(block_read(NULL, handler->ev_fd, &xfrd->reload_pid,
+ sizeof(pid_t), -1) != sizeof(pid_t)) {
+ log_msg(LOG_ERR, "xfrd cannot get reload_pid");
+ }
+ /* read the not-mytask for the results and soainfo */
+ xfrd_process_task_result(xfrd,
+ xfrd->nsd->task[1-xfrd->nsd->mytask]);
+ /* reset the IPC, (and the nonblocking ipc write;
+ the new parent does not want half a packet) */
xfrd->can_send_reload = 1;
- xfrd->parent_soa_info_pass = 0;
xfrd->ipc_send_blocked = 0;
- handler->event_types |= NETIO_EVENT_WRITE;
+ ipc_xfrd_set_listening(xfrd, EV_PERSIST|EV_READ|EV_WRITE);
xfrd_reopen_logfile();
xfrd_check_failed_updates();
- xfrd_send_expy_all_zones();
break;
case NSD_PASS_TO_XFRD:
DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: ipc recv PASS_TO_XFRD"));
- xfrd->ipc_is_soa = 0;
xfrd->ipc_conn->is_reading = 1;
break;
+ case NSD_RELOAD_REQ:
+ DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: ipc recv RELOAD_REQ"));
+ /* make reload happen, right away, and schedule file check */
+ task_new_check_zonefiles(xfrd->nsd->task[xfrd->nsd->mytask],
+ xfrd->last_task, NULL);
+ xfrd_set_reload_now(xfrd);
+ break;
case NSD_RELOAD:
/* main tells us that reload is done, stop ipc send to main */
DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: ipc recv RELOAD"));
- handler->event_types |= NETIO_EVENT_WRITE;
+ ipc_xfrd_set_listening(xfrd, EV_PERSIST|EV_READ|EV_WRITE);
xfrd->need_to_send_quit = 1;
break;
default:
log_msg(LOG_ERR, "xfrd_handle_ipc: bad mode %d (%d)", (int)cmd,
- ntohl(cmd));
+ (int)ntohl(cmd));
break;
}
@@ -836,4 +793,3 @@ xfrd_handle_ipc_read(netio_handler_type *handler, xfrd_state_t* xfrd)
buffer_clear(xfrd->ipc_conn->packet);
}
}
-
diff --git a/usr.sbin/nsd/namedb.c b/usr.sbin/nsd/namedb.c
index db90965c2d3..dbe8efd89ad 100644
--- a/usr.sbin/nsd/namedb.c
+++ b/usr.sbin/nsd/namedb.c
@@ -533,7 +533,7 @@ domain_find_zone(namedb_type* db, domain_type* domain)
}
zone_type *
-domain_find_parent_zone(zone_type* zone)
+domain_find_parent_zone(namedb_type* db, zone_type* zone)
{
rrset_type* rrset;
@@ -544,6 +544,10 @@ domain_find_parent_zone(zone_type* zone)
return rrset->zone;
}
}
+ /* the NS record in the parent zone above this zone is not present,
+ * workaround to find that parent zone anyway */
+ if(zone->apex->parent)
+ return domain_find_zone(db, zone->apex->parent);
return NULL;
}
diff --git a/usr.sbin/nsd/namedb.h b/usr.sbin/nsd/namedb.h
index ca46477ae20..e5cf36c0159 100644
--- a/usr.sbin/nsd/namedb.h
+++ b/usr.sbin/nsd/namedb.h
@@ -232,7 +232,7 @@ rrset_type* domain_find_rrset(domain_type* domain, zone_type* zone, uint16_t typ
rrset_type* domain_find_any_rrset(domain_type* domain, zone_type* zone);
zone_type* domain_find_zone(namedb_type* db, domain_type* domain);
-zone_type* domain_find_parent_zone(zone_type* zone);
+zone_type* domain_find_parent_zone(namedb_type* db, zone_type* zone);
domain_type* domain_find_ns_rrsets(domain_type* domain, zone_type* zone, rrset_type **ns);
/* find DNAME rrset in domain->parent or higher and return that domain */
diff --git a/usr.sbin/nsd/nsd-checkconf.c b/usr.sbin/nsd/nsd-checkconf.c
index e5f669f967b..b7afdd1b916 100644
--- a/usr.sbin/nsd/nsd-checkconf.c
+++ b/usr.sbin/nsd/nsd-checkconf.c
@@ -59,6 +59,12 @@ extern int optind;
return; \
}
+#define ZONE_GET_INT(NAME, VAR, PATTERN) \
+ if (strcasecmp(#NAME, (VAR)) == 0) { \
+ printf("%d\n", (int) PATTERN->NAME); \
+ return; \
+ }
+
#define SERV_GET_BIN(NAME, VAR) \
if (strcasecmp(#NAME, (VAR)) == 0) { \
printf("%s\n", opt->NAME?"yes":"no"); \
@@ -306,6 +312,11 @@ config_print_zone(nsd_options_t* opt, const char* k, int s, const char *o,
ZONE_GET_STR(zonestats, o, zone->pattern);
ZONE_GET_OUTGOING(outgoing_interface, o, zone->pattern);
ZONE_GET_BIN(allow_axfr_fallback, o, zone->pattern);
+ ZONE_GET_INT(max_refresh_time, o, zone->pattern);
+ ZONE_GET_INT(min_refresh_time, o, zone->pattern);
+ ZONE_GET_INT(max_retry_time, o, zone->pattern);
+ ZONE_GET_INT(min_retry_time, o, zone->pattern);
+ ZONE_GET_INT(size_limit_xfr, o, zone->pattern);
#ifdef RATELIMIT
ZONE_GET_RRL(rrl_whitelist, o, zone->pattern);
#endif
@@ -331,6 +342,11 @@ config_print_zone(nsd_options_t* opt, const char* k, int s, const char *o,
ZONE_GET_STR(zonestats, o, p);
ZONE_GET_OUTGOING(outgoing_interface, o, p);
ZONE_GET_BIN(allow_axfr_fallback, o, p);
+ ZONE_GET_INT(max_refresh_time, o, p);
+ ZONE_GET_INT(min_refresh_time, o, p);
+ ZONE_GET_INT(max_retry_time, o, p);
+ ZONE_GET_INT(min_retry_time, o, p);
+ ZONE_GET_INT(size_limit_xfr, o, p);
#ifdef RATELIMIT
ZONE_GET_RRL(rrl_whitelist, o, p);
#endif
@@ -431,6 +447,17 @@ static void print_zone_content_elems(pattern_options_t* pat)
if(!pat->allow_axfr_fallback_is_default)
printf("\tallow-axfr-fallback: %s\n",
pat->allow_axfr_fallback?"yes":"no");
+ if(!pat->max_refresh_time_is_default)
+ printf("\tmax-refresh-time: %d\n", pat->max_refresh_time);
+ if(!pat->min_refresh_time_is_default)
+ printf("\tmin-refresh-time: %d\n", pat->min_refresh_time);
+ if(!pat->max_retry_time_is_default)
+ printf("\tmax-retry-time: %d\n", pat->max_retry_time);
+ if(!pat->min_retry_time_is_default)
+ printf("\tmin-retry-time: %d\n", pat->min_retry_time);
+ if(pat->size_limit_xfr != 0)
+ printf("\tsize-limit-xfr: %llu\n",
+ (long long unsigned)pat->size_limit_xfr);
}
void
@@ -559,6 +586,13 @@ additional_checks(nsd_options_t* opt, const char* filename)
fprintf(stderr, "%s: cannot parse zone name syntax for zone %s.\n", filename, zone->name);
errors ++;
}
+#ifndef ROOT_SERVER
+ /* Is it a root zone? Are we a root server then? Idiot proof. */
+ if(dname->label_count == 1) {
+ fprintf(stderr, "%s: not configured as a root server.\n", filename);
+ errors ++;
+ }
+#endif
if(zone->pattern->allow_notify && !zone->pattern->request_xfr) {
fprintf(stderr, "%s: zone %s has allow-notify but no request-xfr"
" items. Where can it get a zone transfer when a notify "
diff --git a/usr.sbin/nsd/nsd.conf.5.in b/usr.sbin/nsd/nsd.conf.5.in
index 44493c722cd..bcec054af2c 100644
--- a/usr.sbin/nsd/nsd.conf.5.in
+++ b/usr.sbin/nsd/nsd.conf.5.in
@@ -583,6 +583,10 @@ transmitted using TCP.
This option should be accompanied by request\-xfr. It (dis)allows NSD (as secondary)
to fallback to AXFR if the primary name server does not support IXFR. Default is yes.
.TP
+.B size\-limit\-xfr:\fR <number>
+This option should be accompanied by request\-xfr. It specifies XFR temporary file size limit. It can be used to stop very large zone retrieval, that could otherwise use up a lot of memory and disk space.
+If this option is 0, unlimited. Default value is 0.
+.TP
.B notify:\fR <ip\-address> <key\-name | NOKEY>
Access control list. The listed address (a secondary) is notified
of updates to this zone. A port number can be added using a suffix of @number,
@@ -624,6 +628,22 @@ A port number can be added using a suffix of @number, for example
1.2.3.4@5300.
.RE
.TP
+.B max\-refresh\-time:\fR <seconds>
+Limit refresh time for secondary zones. This is the timer which checks to see
+if the zone has to be refetched when it expires. Normally the value from the
+SOA record is used, but this option restricts that value.
+.TP
+.B min\-refresh\-time:\fR <seconds>
+Limit refresh time for secondary zones.
+.TP
+.B max\-retry\-time:\fR <seconds>
+Limit retry time for secondary zones. This is the timeout after a failed
+fetch attempt for the zone. Normally the value from the SOA record is used,
+but this option restricts that value.
+.TP
+.B min\-retry\-time:\fR <seconds>
+Limit retry time for secondary zones.
+.TP
.B zonestats:\fR <name>
When compiled with \-\-enable\-zone\-stats NSD can collect statistics per zone.
This name gives the group where statistics are added to. The groups are
diff --git a/usr.sbin/nsd/nsd.conf.sample.in b/usr.sbin/nsd/nsd.conf.sample.in
index 48eef14ff9e..cda7dd084ba 100644
--- a/usr.sbin/nsd/nsd.conf.sample.in
+++ b/usr.sbin/nsd/nsd.conf.sample.in
@@ -258,6 +258,15 @@ remote-control:
# set local interface for sending zone transfer requests.
# default is let the OS choose.
#outgoing-interface: 10.0.0.10
+ # limit the refresh and retry interval in seconds.
+ #max-refresh-time: 2419200
+ #min-refresh-time: 0
+ #max-retry-time: 1209600
+ #min-retry-time: 0
+
+ # limit the zone transfer size (in bytes), stops very large transfers
+ # 0 is no limits enforced.
+ # size-limit-xfr: 0
# if compiled with --enable-zone-stats, give name of stat block for
# this zone (or group of zones). Output from nsd-control stats.
diff --git a/usr.sbin/nsd/nsec3.c b/usr.sbin/nsd/nsec3.c
index d4fd1a2d336..bad5af8a1bc 100644
--- a/usr.sbin/nsd/nsec3.c
+++ b/usr.sbin/nsd/nsec3.c
@@ -893,7 +893,9 @@ nsec3_add_ds_proof(struct query *query, struct answer *answer,
/* use NSEC3 record from above the zone cut. */
nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
domain->nsec3->nsec3_ds_parent_cover);
- } else if (!delegpt && domain->nsec3 && domain->nsec3->nsec3_is_exact) {
+ } else if (!delegpt && domain->nsec3 && domain->nsec3->nsec3_is_exact
+ && nsec3_domain_part_of_zone(domain->nsec3->nsec3_cover,
+ query->zone)) {
nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
domain->nsec3->nsec3_cover);
} else {
diff --git a/usr.sbin/nsd/options.c b/usr.sbin/nsd/options.c
index 349e271b422..058ceeccab4 100644
--- a/usr.sbin/nsd/options.c
+++ b/usr.sbin/nsd/options.c
@@ -813,6 +813,7 @@ pattern_options_create(region_type* region)
p->zonestats = 0;
p->allow_notify = 0;
p->request_xfr = 0;
+ p->size_limit_xfr = 0;
p->notify = 0;
p->provide_xfr = 0;
p->outgoing_interface = 0;
@@ -822,6 +823,14 @@ pattern_options_create(region_type* region)
p->allow_axfr_fallback_is_default = 1;
p->implicit = 0;
p->xfrd_flags = 0;
+ p->max_refresh_time = 2419200; /* 4 weeks */
+ p->max_refresh_time_is_default = 1;
+ p->min_refresh_time = 0;
+ p->min_refresh_time_is_default = 1;
+ p->max_retry_time = 1209600; /* 2 weeks */
+ p->max_retry_time_is_default = 1;
+ p->min_retry_time = 0;
+ p->min_retry_time_is_default = 1;
#ifdef RATELIMIT
p->rrl_whitelist = 0;
#endif
@@ -944,6 +953,14 @@ copy_pat_fixed(region_type* region, pattern_options_t* orig,
if(p->zonestats)
orig->zonestats = region_strdup(region, p->zonestats);
else orig->zonestats = NULL;
+ orig->max_refresh_time = p->max_refresh_time;
+ orig->max_refresh_time_is_default = p->max_refresh_time_is_default;
+ orig->min_refresh_time = p->min_refresh_time;
+ orig->min_refresh_time_is_default = p->min_refresh_time_is_default;
+ orig->max_retry_time = p->max_retry_time;
+ orig->max_retry_time_is_default = p->max_retry_time_is_default;
+ orig->min_retry_time = p->min_retry_time;
+ orig->min_retry_time_is_default = p->min_retry_time_is_default;
#ifdef RATELIMIT
orig->rrl_whitelist = p->rrl_whitelist;
#endif
@@ -1017,9 +1034,22 @@ pattern_options_equal(pattern_options_t* p, pattern_options_t* q)
if(!acl_list_equal(p->provide_xfr, q->provide_xfr)) return 0;
if(!acl_list_equal(p->outgoing_interface, q->outgoing_interface))
return 0;
+ if(p->max_refresh_time != q->max_refresh_time) return 0;
+ if(!booleq(p->max_refresh_time_is_default,
+ q->max_refresh_time_is_default)) return 0;
+ if(p->min_refresh_time != q->min_refresh_time) return 0;
+ if(!booleq(p->min_refresh_time_is_default,
+ q->min_refresh_time_is_default)) return 0;
+ if(p->max_retry_time != q->max_retry_time) return 0;
+ if(!booleq(p->max_retry_time_is_default,
+ q->max_retry_time_is_default)) return 0;
+ if(p->min_retry_time != q->min_retry_time) return 0;
+ if(!booleq(p->min_retry_time_is_default,
+ q->min_retry_time_is_default)) return 0;
#ifdef RATELIMIT
if(p->rrl_whitelist != q->rrl_whitelist) return 0;
#endif
+ if(p->size_limit_xfr != q->size_limit_xfr) return 0;
return 1;
}
@@ -1036,6 +1066,19 @@ unmarshal_u8(struct buffer* b)
return buffer_read_u8(b);
}
+static void
+marshal_u64(struct buffer* b, uint64_t v)
+{
+ buffer_reserve(b, 8);
+ buffer_write_u64(b, v);
+}
+
+static uint64_t
+unmarshal_u64(struct buffer* b)
+{
+ return buffer_read_u64(b);
+}
+
#ifdef RATELIMIT
static void
marshal_u16(struct buffer* b, uint16_t v)
@@ -1054,6 +1097,19 @@ unmarshal_u16(struct buffer* b)
#endif
static void
+marshal_u32(struct buffer* b, uint32_t v)
+{
+ buffer_reserve(b, 4);
+ buffer_write_u32(b, v);
+}
+
+static uint32_t
+unmarshal_u32(struct buffer* b)
+{
+ return buffer_read_u32(b);
+}
+
+static void
marshal_str(struct buffer* b, const char* s)
{
if(!s) marshal_u8(b, 0);
@@ -1138,11 +1194,20 @@ pattern_options_marshal(struct buffer* b, pattern_options_t* p)
marshal_u8(b, p->notify_retry);
marshal_u8(b, p->notify_retry_is_default);
marshal_u8(b, p->implicit);
+ marshal_u64(b, p->size_limit_xfr);
marshal_acl_list(b, p->allow_notify);
marshal_acl_list(b, p->request_xfr);
marshal_acl_list(b, p->notify);
marshal_acl_list(b, p->provide_xfr);
marshal_acl_list(b, p->outgoing_interface);
+ marshal_u32(b, p->max_refresh_time);
+ marshal_u8(b, p->max_refresh_time_is_default);
+ marshal_u32(b, p->min_refresh_time);
+ marshal_u8(b, p->min_refresh_time_is_default);
+ marshal_u32(b, p->max_retry_time);
+ marshal_u8(b, p->max_retry_time_is_default);
+ marshal_u32(b, p->min_retry_time);
+ marshal_u8(b, p->min_retry_time_is_default);
}
pattern_options_t*
@@ -1160,11 +1225,20 @@ pattern_options_unmarshal(region_type* r, struct buffer* b)
p->notify_retry = unmarshal_u8(b);
p->notify_retry_is_default = unmarshal_u8(b);
p->implicit = unmarshal_u8(b);
+ p->size_limit_xfr = unmarshal_u64(b);
p->allow_notify = unmarshal_acl_list(r, b);
p->request_xfr = unmarshal_acl_list(r, b);
p->notify = unmarshal_acl_list(r, b);
p->provide_xfr = unmarshal_acl_list(r, b);
p->outgoing_interface = unmarshal_acl_list(r, b);
+ p->max_refresh_time = unmarshal_u32(b);
+ p->max_refresh_time_is_default = unmarshal_u8(b);
+ p->min_refresh_time = unmarshal_u32(b);
+ p->min_refresh_time_is_default = unmarshal_u8(b);
+ p->max_retry_time = unmarshal_u32(b);
+ p->max_retry_time_is_default = unmarshal_u8(b);
+ p->min_retry_time = unmarshal_u32(b);
+ p->min_retry_time_is_default = unmarshal_u8(b);
return p;
}
@@ -1875,6 +1949,23 @@ config_apply_pattern(const char* name)
a->notify_retry = pat->notify_retry;
a->notify_retry_is_default = 0;
}
+ if(!pat->max_refresh_time_is_default) {
+ a->max_refresh_time = pat->max_refresh_time;
+ a->max_refresh_time_is_default = 0;
+ }
+ if(!pat->min_refresh_time_is_default) {
+ a->min_refresh_time = pat->min_refresh_time;
+ a->min_refresh_time_is_default = 0;
+ }
+ if(!pat->max_retry_time_is_default) {
+ a->max_retry_time = pat->max_retry_time;
+ a->max_retry_time_is_default = 0;
+ }
+ if(!pat->min_retry_time_is_default) {
+ a->min_retry_time = pat->min_retry_time;
+ a->min_retry_time_is_default = 0;
+ }
+ a->size_limit_xfr = pat->size_limit_xfr;
#ifdef RATELIMIT
a->rrl_whitelist |= pat->rrl_whitelist;
#endif
diff --git a/usr.sbin/nsd/options.h b/usr.sbin/nsd/options.h
index ceba624fe3f..e0f749cb972 100644
--- a/usr.sbin/nsd/options.h
+++ b/usr.sbin/nsd/options.h
@@ -154,6 +154,15 @@ struct pattern_options {
uint8_t notify_retry_is_default;
uint8_t implicit; /* pattern is implicit, part_of_config zone used */
uint8_t xfrd_flags;
+ uint32_t max_refresh_time;
+ uint8_t max_refresh_time_is_default;
+ uint32_t min_refresh_time;
+ uint8_t min_refresh_time_is_default;
+ uint32_t max_retry_time;
+ uint8_t max_retry_time_is_default;
+ uint32_t min_retry_time;
+ uint8_t min_retry_time_is_default;
+ uint64_t size_limit_xfr;
};
#define PATTERN_IMPLICIT_MARKER "_implicit_"
diff --git a/usr.sbin/nsd/query.c b/usr.sbin/nsd/query.c
index f8a429c4a98..7256449d38d 100644
--- a/usr.sbin/nsd/query.c
+++ b/usr.sbin/nsd/query.c
@@ -704,11 +704,16 @@ add_rrset(struct query *query,
result = answer_add_rrset(answer, section, owner, rrset);
switch (rrset_rrtype(rrset)) {
case TYPE_NS:
+#if defined(INET6)
/* if query over IPv6, swap A and AAAA; put AAAA first */
add_additional_rrsets(query, answer, rrset, 0, 1,
(query->addr.ss_family == AF_INET6)?
swap_aaaa_additional_rr_types:
default_additional_rr_types);
+#else
+ add_additional_rrsets(query, answer, rrset, 0, 1,
+ default_additional_rr_types);
+#endif
break;
case TYPE_MB:
add_additional_rrsets(query, answer, rrset, 0, 0,
@@ -1205,7 +1210,7 @@ answer_lookup_zone(struct nsd *nsd, struct query *q, answer_type *answer,
* parent zone to generate the answer if we are
* authoritative for the parent zone.
*/
- zone_type *zone = domain_find_parent_zone(q->zone);
+ zone_type *zone = domain_find_parent_zone(nsd->db, q->zone);
if (zone)
q->zone = zone;
}
diff --git a/usr.sbin/nsd/server.c b/usr.sbin/nsd/server.c
index 530b443de8c..ae27312fbfc 100644
--- a/usr.sbin/nsd/server.c
+++ b/usr.sbin/nsd/server.c
@@ -160,6 +160,11 @@ struct tcp_handler_data
* The number of queries handled by this specific TCP connection.
*/
int query_count;
+
+ /*
+ * The timeout in msec for this tcp connection
+ */
+ int tcp_timeout;
};
/*
@@ -2630,8 +2635,8 @@ handle_tcp_reading(int fd, short event, void* arg)
data->query->tcplen = buffer_remaining(data->query->packet);
data->bytes_transmitted = 0;
- timeout.tv_sec = data->nsd->tcp_timeout;
- timeout.tv_usec = 0L;
+ timeout.tv_sec = data->tcp_timeout / 1000;
+ timeout.tv_usec = (data->tcp_timeout % 1000)*1000;
ev_base = data->event.ev_base;
event_del(&data->event);
@@ -2763,8 +2768,8 @@ handle_tcp_writing(int fd, short event, void* arg)
q->tcplen = buffer_remaining(q->packet);
data->bytes_transmitted = 0;
/* Reset timeout. */
- timeout.tv_sec = data->nsd->tcp_timeout;
- timeout.tv_usec = 0L;
+ timeout.tv_sec = data->tcp_timeout / 1000;
+ timeout.tv_usec = (data->tcp_timeout % 1000)*1000;
ev_base = data->event.ev_base;
event_del(&data->event);
event_set(&data->event, fd, EV_PERSIST | EV_WRITE | EV_TIMEOUT,
@@ -2794,8 +2799,8 @@ handle_tcp_writing(int fd, short event, void* arg)
data->bytes_transmitted = 0;
- timeout.tv_sec = data->nsd->tcp_timeout;
- timeout.tv_usec = 0L;
+ timeout.tv_sec = data->tcp_timeout / 1000;
+ timeout.tv_usec = (data->tcp_timeout % 1000)*1000;
ev_base = data->event.ev_base;
event_del(&data->event);
event_set(&data->event, fd, EV_PERSIST | EV_READ | EV_TIMEOUT,
@@ -2909,8 +2914,13 @@ handle_tcp_accept(int fd, short event, void* arg)
memcpy(&tcp_data->query->addr, &addr, addrlen);
tcp_data->query->addrlen = addrlen;
- timeout.tv_sec = data->nsd->tcp_timeout;
- timeout.tv_usec = 0;
+ tcp_data->tcp_timeout = data->nsd->tcp_timeout * 1000;
+ if (data->nsd->current_tcp_count > data->nsd->maximum_tcp_count/2) {
+ /* very busy, give smaller timeout */
+ tcp_data->tcp_timeout = 200;
+ }
+ timeout.tv_sec = tcp_data->tcp_timeout / 1000;
+ timeout.tv_usec = (tcp_data->tcp_timeout % 1000)*1000;
event_set(&tcp_data->event, s, EV_PERSIST | EV_READ | EV_TIMEOUT,
handle_tcp_reading, tcp_data);
diff --git a/usr.sbin/nsd/util.h b/usr.sbin/nsd/util.h
index 702674fc411..b59b7b69bd1 100644
--- a/usr.sbin/nsd/util.h
+++ b/usr.sbin/nsd/util.h
@@ -198,6 +198,20 @@ write_uint32(void *dst, uint32_t data)
#endif
}
+static inline void
+write_uint64(void *dst, uint64_t data)
+{
+ uint8_t *p = (uint8_t *) dst;
+ p[0] = (uint8_t) ((data >> 56) & 0xff);
+ p[1] = (uint8_t) ((data >> 48) & 0xff);
+ p[2] = (uint8_t) ((data >> 40) & 0xff);
+ p[3] = (uint8_t) ((data >> 32) & 0xff);
+ p[4] = (uint8_t) ((data >> 24) & 0xff);
+ p[5] = (uint8_t) ((data >> 16) & 0xff);
+ p[6] = (uint8_t) ((data >> 8) & 0xff);
+ p[7] = (uint8_t) (data & 0xff);
+}
+
/*
* Copy data allowing for unaligned accesses in network byte order
* (big endian).
@@ -224,6 +238,21 @@ read_uint32(const void *src)
#endif
}
+static inline uint64_t
+read_uint64(const void *src)
+{
+ uint8_t *p = (uint8_t *) src;
+ return
+ ((uint64_t)p[0] << 56) |
+ ((uint64_t)p[1] << 48) |
+ ((uint64_t)p[2] << 40) |
+ ((uint64_t)p[3] << 32) |
+ ((uint64_t)p[4] << 24) |
+ ((uint64_t)p[5] << 16) |
+ ((uint64_t)p[6] << 8) |
+ (uint64_t)p[7];
+}
+
/*
* Print debugging information using log_msg,
* set the logfile as /dev/stdout or /dev/stderr if you like.
diff --git a/usr.sbin/nsd/xfrd-disk.c b/usr.sbin/nsd/xfrd-disk.c
index 3fa863077ea..654e78edfd4 100644
--- a/usr.sbin/nsd/xfrd-disk.c
+++ b/usr.sbin/nsd/xfrd-disk.c
@@ -147,6 +147,7 @@ xfrd_read_state(struct xfrd_state* xfrd)
uint32_t filetime = 0;
uint32_t numzones, i;
region_type *tempregion;
+ time_t soa_refresh;
tempregion = region_create(xalloc, free);
if(!tempregion)
@@ -265,10 +266,15 @@ xfrd_read_state(struct xfrd_state* xfrd)
* 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 > zone->zone_options->pattern->max_refresh_time)
+ soa_refresh = zone->zone_options->pattern->max_refresh_time;
+ else if (soa_refresh < 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
- > ntohl(soa_disk_read.refresh)))
+ > soa_refresh))
{
zone->state = xfrd_zone_refreshing;
xfrd_set_refresh_now(zone);
@@ -564,3 +570,17 @@ xfrd_unlink_xfrfile(struct nsd* nsd, uint64_t number)
strerror(errno));
}
}
+
+uint64_t
+xfrd_get_xfrfile_size(struct nsd* nsd, uint64_t number )
+{
+ char fname[1024];
+ struct stat tempxfr_stat;
+ tempxfrname(fname, sizeof(fname), nsd, number);
+ if( stat( fname, &tempxfr_stat ) < 0 ) {
+ log_msg(LOG_WARNING, "could not get file size %s: %s", fname,
+ strerror(errno));
+ return 0;
+ }
+ return (uint64_t)tempxfr_stat.st_size;
+}
diff --git a/usr.sbin/nsd/xfrd-disk.h b/usr.sbin/nsd/xfrd-disk.h
index 217ecc122b9..b7e2d10b237 100644
--- a/usr.sbin/nsd/xfrd-disk.h
+++ b/usr.sbin/nsd/xfrd-disk.h
@@ -10,8 +10,8 @@
#ifndef XFRD_DISK_H
#define XFRD_DISK_H
-#include <config.h>
struct xfrd_state;
+struct nsd;
/* magic string to identify xfrd state file */
#define XFRD_FILE_MAGIC "NSDXFRD1"
@@ -21,4 +21,15 @@ void xfrd_read_state(struct xfrd_state* xfrd);
/* write xfrd zone state if possible */
void xfrd_write_state(struct xfrd_state* xfrd);
+/* create temp directory */
+void xfrd_make_tempdir(struct nsd* nsd);
+/* rmdir temp directory */
+void xfrd_del_tempdir(struct nsd* nsd);
+/* open temp file, makes directory if needed */
+FILE* xfrd_open_xfrfile(struct nsd* nsd, uint64_t number, char* mode);
+/* unlink temp file */
+void xfrd_unlink_xfrfile(struct nsd* nsd, uint64_t number);
+/* get temp file size */
+uint64_t xfrd_get_xfrfile_size(struct nsd* nsd, uint64_t number );
+
#endif /* XFRD_DISK_H */
diff --git a/usr.sbin/nsd/xfrd.c b/usr.sbin/nsd/xfrd.c
index c2c75eda4df..0eacce799d3 100644
--- a/usr.sbin/nsd/xfrd.c
+++ b/usr.sbin/nsd/xfrd.c
@@ -702,7 +702,12 @@ xfrd_set_timer_refresh(xfrd_zone_t* zone)
return;
}
/* refresh or expire timeout, whichever is earlier */
- set_refresh = zone->soa_disk_acquired + ntohl(zone->soa_disk.refresh);
+ set_refresh = ntohl(zone->soa_disk.refresh);
+ if (set_refresh > zone->zone_options->pattern->max_refresh_time)
+ set_refresh = zone->zone_options->pattern->max_refresh_time;
+ else if (set_refresh < zone->zone_options->pattern->min_refresh_time)
+ set_refresh = zone->zone_options->pattern->min_refresh_time;
+ set_refresh += zone->soa_disk_acquired;
set_expire = zone->soa_disk_acquired + ntohl(zone->soa_disk.expire);
if(set_refresh < set_expire)
set = set_refresh;
@@ -719,6 +724,7 @@ xfrd_set_timer_refresh(xfrd_zone_t* zone)
static void
xfrd_set_timer_retry(xfrd_zone_t* zone)
{
+ time_t set_retry;
/* set timer for next retry or expire timeout if earlier. */
if(zone->soa_disk_acquired == 0) {
/* if no information, use reasonable timeout */
@@ -743,10 +749,14 @@ xfrd_set_timer_retry(xfrd_zone_t* zone)
xfrd_time() + (time_t)ntohl(zone->soa_disk.retry) <
zone->soa_disk_acquired + (time_t)ntohl(zone->soa_disk.expire))
{
- if(ntohl(zone->soa_disk.retry) < XFRD_LOWERBOUND_RETRY)
- xfrd_set_timer(zone, XFRD_LOWERBOUND_RETRY);
- else
- xfrd_set_timer(zone, ntohl(zone->soa_disk.retry));
+ set_retry = ntohl(zone->soa_disk.retry);
+ if(set_retry > zone->zone_options->pattern->max_retry_time)
+ set_retry = zone->zone_options->pattern->max_retry_time;
+ else if(set_retry < zone->zone_options->pattern->min_retry_time)
+ set_retry = zone->zone_options->pattern->min_retry_time;
+ if(set_retry < XFRD_LOWERBOUND_RETRY)
+ set_retry = XFRD_LOWERBOUND_RETRY;
+ xfrd_set_timer(zone, set_retry);
} else {
if(ntohl(zone->soa_disk.expire) < XFRD_LOWERBOUND_RETRY)
xfrd_set_timer(zone, XFRD_LOWERBOUND_RETRY);
@@ -1918,6 +1928,7 @@ xfrd_handle_received_xfr_packet(xfrd_zone_t* zone, buffer_type* packet)
{
xfrd_soa_t soa;
enum xfrd_packet_result res;
+ uint64_t xfrfile_size;
/* parse and check the packet - see if it ends the xfr */
switch((res=xfrd_parse_received_xfr_packet(zone, packet, &soa)))
@@ -1975,6 +1986,15 @@ xfrd_handle_received_xfr_packet(xfrd_zone_t* zone, buffer_type* packet)
"disk", zone->apex_str, zone->master->ip_address_spec,
(int)zone->msg_new_serial));
zone->msg_seq_nr++;
+
+ xfrfile_size = xfrd_get_xfrfile_size(xfrd->nsd, zone->xfrfilenumber);
+ if( zone->zone_options->pattern->size_limit_xfr != 0 &&
+ xfrfile_size > zone->zone_options->pattern->size_limit_xfr ) {
+ /* xfrd_unlink_xfrfile(xfrd->nsd, zone->xfrfilenumber);
+ xfrd_set_reload_timeout(); */
+ log_msg(LOG_INFO, "xfrd : transfered zone data was too large %llu", (long long unsigned)xfrfile_size);
+ return xfrd_packet_bad;
+ }
if(res == xfrd_packet_more) {
/* wait for more */
return xfrd_packet_more;
diff --git a/usr.sbin/nsd/zonec.c b/usr.sbin/nsd/zonec.c
index 666b0cc3dd7..c186171039c 100644
--- a/usr.sbin/nsd/zonec.c
+++ b/usr.sbin/nsd/zonec.c
@@ -953,7 +953,10 @@ zparser_conv_loc(region_type *region, char *str)
}
/* Meters of altitude... */
- (void) strtol(str, &str, 10);
+ if(strtol(str, &str, 10) == LONG_MAX) {
+ zc_error_prev_line("altitude too large, number overflow");
+ return NULL;
+ }
switch(*str) {
case ' ':
case '\0':
@@ -1576,21 +1579,21 @@ zonec_read(const char* name, const char* zonefile, zone_type* zone)
dname = dname_parse(parser->rr_region, name);
if (!dname) {
zc_error("incorrect zone name '%s'", name);
- return 0;
+ return 1;
}
#ifndef ROOT_SERVER
/* Is it a root zone? Are we a root server then? Idiot proof. */
if (dname->label_count == 1) {
zc_error("not configured as a root server");
- return 0;
+ return 1;
}
#endif
/* Open the zone file */
if (!zone_open(zonefile, 3600, CLASS_IN, dname)) {
zc_error("cannot open '%s': %s", zonefile, strerror(errno));
- return 0;
+ return 1;
}
parser->current_zone = zone;