diff options
author | Florian Obser <florian@cvs.openbsd.org> | 2016-08-31 07:31:21 +0000 |
---|---|---|
committer | Florian Obser <florian@cvs.openbsd.org> | 2016-08-31 07:31:21 +0000 |
commit | 866be3316a80e2e2b9d2760399423d457227312c (patch) | |
tree | 9fbec078c3279dd957706999c843990538c73c52 /usr.sbin/nsd | |
parent | 78573f5c573f70e6432b9eb8014b673dbd3d9f43 (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.h | 29 | ||||
-rw-r--r-- | usr.sbin/nsd/configlexer.lex | 5 | ||||
-rw-r--r-- | usr.sbin/nsd/configparser.y | 56 | ||||
-rw-r--r-- | usr.sbin/nsd/configure.ac | 2 | ||||
-rw-r--r-- | usr.sbin/nsd/ipc.c | 638 | ||||
-rw-r--r-- | usr.sbin/nsd/namedb.c | 6 | ||||
-rw-r--r-- | usr.sbin/nsd/namedb.h | 2 | ||||
-rw-r--r-- | usr.sbin/nsd/nsd-checkconf.c | 34 | ||||
-rw-r--r-- | usr.sbin/nsd/nsd.conf.5.in | 20 | ||||
-rw-r--r-- | usr.sbin/nsd/nsd.conf.sample.in | 9 | ||||
-rw-r--r-- | usr.sbin/nsd/nsec3.c | 4 | ||||
-rw-r--r-- | usr.sbin/nsd/options.c | 91 | ||||
-rw-r--r-- | usr.sbin/nsd/options.h | 9 | ||||
-rw-r--r-- | usr.sbin/nsd/query.c | 7 | ||||
-rw-r--r-- | usr.sbin/nsd/server.c | 26 | ||||
-rw-r--r-- | usr.sbin/nsd/util.h | 29 | ||||
-rw-r--r-- | usr.sbin/nsd/xfrd-disk.c | 22 | ||||
-rw-r--r-- | usr.sbin/nsd/xfrd-disk.h | 13 | ||||
-rw-r--r-- | usr.sbin/nsd/xfrd.c | 30 | ||||
-rw-r--r-- | usr.sbin/nsd/zonec.c | 11 |
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; |