diff options
author | Stuart Henderson <sthen@cvs.openbsd.org> | 2013-09-03 09:21:38 +0000 |
---|---|---|
committer | Stuart Henderson <sthen@cvs.openbsd.org> | 2013-09-03 09:21:38 +0000 |
commit | 3a46b2e4c37dd915f3a6f15fc551ecac02132dbe (patch) | |
tree | 83c2ad6f2db057a64a0974c3127b1c25841989e2 /usr.sbin | |
parent | 6caac9703816ae62cf497fcfe7da3cbaa5da11e4 (diff) |
update to NSD 3.2.16, ok deraadt@ brad@
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/nsd/acx_nlnetlabs.m4 | 38 | ||||
-rw-r--r-- | usr.sbin/nsd/configlexer.lex | 4 | ||||
-rw-r--r-- | usr.sbin/nsd/difffile.c | 8 | ||||
-rw-r--r-- | usr.sbin/nsd/ipc.c | 12 | ||||
-rw-r--r-- | usr.sbin/nsd/namedb.c | 1 | ||||
-rw-r--r-- | usr.sbin/nsd/netio.c | 7 | ||||
-rw-r--r-- | usr.sbin/nsd/netio.h | 7 | ||||
-rw-r--r-- | usr.sbin/nsd/nsd.conf.sample.in | 19 | ||||
-rw-r--r-- | usr.sbin/nsd/nsd.h | 6 | ||||
-rw-r--r-- | usr.sbin/nsd/options.c | 5 | ||||
-rw-r--r-- | usr.sbin/nsd/options.h | 7 | ||||
-rw-r--r-- | usr.sbin/nsd/rrl.c | 83 | ||||
-rw-r--r-- | usr.sbin/nsd/rrl.h | 11 | ||||
-rw-r--r-- | usr.sbin/nsd/xfrd-disk.c | 26 | ||||
-rw-r--r-- | usr.sbin/nsd/xfrd.c | 120 | ||||
-rw-r--r-- | usr.sbin/nsd/xfrd.h | 1 |
16 files changed, 263 insertions, 92 deletions
diff --git a/usr.sbin/nsd/acx_nlnetlabs.m4 b/usr.sbin/nsd/acx_nlnetlabs.m4 index e90c81ea02a..719112645aa 100644 --- a/usr.sbin/nsd/acx_nlnetlabs.m4 +++ b/usr.sbin/nsd/acx_nlnetlabs.m4 @@ -2,7 +2,10 @@ # Copyright 2009, Wouter Wijngaards, NLnet Labs. # BSD licensed. # -# Version 21 +# Version 24 +# 2013-06-25 FLTO has --disable-flto option. +# 2013-05-03 Update W32_SLEEP for newer mingw that links but not defines it. +# 2013-03-22 Fix ACX_RSRC_VERSION for long version numbers. # 2012-02-09 Fix AHX_MEMCMP_BROKEN with undef in compat/memcmp.h. # 2012-01-20 Fix COMPILER_FLAGS_UNBOUND for gcc 4.6.2 assigned-not-used-warns. # 2011-12-05 Fix getaddrinfowithincludes on windows with fedora16 mingw32-gcc. @@ -101,7 +104,7 @@ dnl Calculate comma separated windows-resource numbers from package version. dnl Picks the first three(,0) or four numbers out of the name. dnl $1: variable for the result AC_DEFUN([ACX_RSRC_VERSION], -[$1=[`echo $PACKAGE_VERSION | sed -e 's/^[^0-9]*\([0-9]\)[^0-9]*\([0-9]\)[^0-9]*\([0-9]\)[^0-9]*\([0-9]\).*$/\1,\2,\3,\4/' -e 's/^[^0-9]*\([0-9]\)[^0-9]*\([0-9]\)[^0-9]*\([0-9]\)[^0-9]*$/\1,\2,\3,0/' `] +[$1=[`echo $PACKAGE_VERSION | sed -e 's/^[^0-9]*\([0-9][0-9]*\)[^0-9][^0-9]*\([0-9][0-9]*\)[^0-9][^0-9]*\([0-9][0-9]*\)[^0-9][^0-9]*\([0-9][0-9]*\).*$/\1,\2,\3,\4/' -e 's/^[^0-9]*\([0-9][0-9]*\)[^0-9][^0-9]*\([0-9][0-9]*\)[^0-9][^0-9]*\([0-9][0-9]*\)[^0-9]*$/\1,\2,\3,0/' `] ]) dnl Routine to help check for compiler flags. @@ -405,19 +408,22 @@ int test() { dnl Check if CC supports -flto. dnl in a way that supports clang and suncc (that flag does something else, dnl but fails to link). It sets it in CFLAGS if it works. -AC_DEFUN([ACX_CHECK_FLTO], -[AC_MSG_CHECKING([if $CC supports -flto]) -BAKCFLAGS="$CFLAGS" -CFLAGS="$CFLAGS -flto" -AC_LINK_IFELSE([AC_LANG_PROGRAM([], [])], [ - if $CC $CFLAGS -o conftest conftest.c 2>&1 | grep "warning: no debug symbols in executable" >/dev/null; then - CFLAGS="$BAKCFLAGS" - AC_MSG_RESULT(no) - else - AC_MSG_RESULT(yes) - fi - rm -f conftest conftest.c conftest.o -], [CFLAGS="$BAKCFLAGS" ; AC_MSG_RESULT(no)]) +AC_DEFUN([ACX_CHECK_FLTO], [ + AC_ARG_ENABLE([flto], AS_HELP_STRING([--disable-flto], [Disable link-time optimization])) + AS_IF([test "x$enable_flto" != "xno"], [ + AC_MSG_CHECKING([if $CC supports -flto]) + BAKCFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -flto" + AC_LINK_IFELSE([AC_LANG_PROGRAM([], [])], [ + if $CC $CFLAGS -o conftest conftest.c 2>&1 | grep "warning: no debug symbols in executable" >/dev/null; then + CFLAGS="$BAKCFLAGS" + AC_MSG_RESULT(no) + else + AC_MSG_RESULT(yes) + fi + rm -f conftest conftest.c conftest.o + ], [CFLAGS="$BAKCFLAGS" ; AC_MSG_RESULT(no)]) + ]) ]) dnl Check the printf-format attribute (if any) @@ -1208,7 +1214,7 @@ struct tm *gmtime_r(const time_t *timep, struct tm *result); dnl provide w32 compat definition for sleep AC_DEFUN([AHX_CONFIG_W32_SLEEP], [ -#ifndef HAVE_SLEEP +#if !defined(HAVE_SLEEP) || defined(HAVE_WINDOWS_H) #define sleep(x) Sleep((x)*1000) /* on win32 */ #endif /* HAVE_SLEEP */ ]) diff --git a/usr.sbin/nsd/configlexer.lex b/usr.sbin/nsd/configlexer.lex index d98a4ae0acc..55bf4cfe62a 100644 --- a/usr.sbin/nsd/configlexer.lex +++ b/usr.sbin/nsd/configlexer.lex @@ -99,6 +99,7 @@ ANY [^\"\n\r\\]|\\. server{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_SERVER;} name{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_NAME;} ip-address{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IP_ADDRESS;} +ip-transparent{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IP_TRANSPARENT;} debug-mode{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_DEBUG_MODE;} hide-version{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_HIDE_VERSION;} ip4-only{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IP4_ONLY;} @@ -140,6 +141,9 @@ AXFR { LEXOUT(("v(%s) ", yytext)); return VAR_AXFR;} UDP { LEXOUT(("v(%s) ", yytext)); return VAR_UDP;} rrl-size{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RRL_SIZE;} rrl-ratelimit{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RRL_RATELIMIT;} +rrl-slip{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RRL_SLIP;} +rrl-ipv4-prefix-length{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RRL_IPV4_PREFIX_LENGTH;} +rrl-ipv6-prefix-length{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RRL_IPV6_PREFIX_LENGTH;} rrl-whitelist-ratelimit{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RRL_WHITELIST_RATELIMIT;} rrl-whitelist{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RRL_WHITELIST;} {NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++;} diff --git a/usr.sbin/nsd/difffile.c b/usr.sbin/nsd/difffile.c index 3d87fce5201..2b6d721d878 100644 --- a/usr.sbin/nsd/difffile.c +++ b/usr.sbin/nsd/difffile.c @@ -536,6 +536,14 @@ add_RR(namedb_type* db, const dname_type* dname, /* ignore already existing RR: lenient accepting of messages */ return 1; } + if(domain == zone->apex) { + /* make sure we don't get multiple soa rrs */ + if (type == TYPE_SOA && rrset->rr_count > 0) { + log_msg(LOG_ERR, "diff: multiple soa records for %s", + dname_to_string(dname,0)); + return 0; + } + } /* re-alloc the rrs and add the new */ rrs_old = rrset->rrs; diff --git a/usr.sbin/nsd/ipc.c b/usr.sbin/nsd/ipc.c index a380815b50d..28e1cc5e7ec 100644 --- a/usr.sbin/nsd/ipc.c +++ b/usr.sbin/nsd/ipc.c @@ -134,6 +134,14 @@ child_handle_parent_command(netio_type *ATTR_UNUSED(netio), 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 */ + (void)write(handler->fd, &mode, sizeof(mode)); + ipc_child_quit(data->nsd); + break; case NSD_ZONE_STATE: data->conn->is_reading = 1; data->conn->total_bytes = 0; @@ -200,7 +208,8 @@ 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; return; @@ -208,6 +217,7 @@ parent_handle_xfrd_command(netio_type *ATTR_UNUSED(netio), 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: diff --git a/usr.sbin/nsd/namedb.c b/usr.sbin/nsd/namedb.c index 43a1b8e9bec..5ed3b31baf6 100644 --- a/usr.sbin/nsd/namedb.c +++ b/usr.sbin/nsd/namedb.c @@ -20,7 +20,6 @@ #include "namedb.h" - static domain_type * allocate_domain_info(domain_table_type *table, const dname_type *dname, diff --git a/usr.sbin/nsd/netio.c b/usr.sbin/nsd/netio.c index 135094a82eb..2c64b6d1f67 100644 --- a/usr.sbin/nsd/netio.c +++ b/usr.sbin/nsd/netio.c @@ -25,13 +25,6 @@ int pselect(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, #include <sys/select.h> #endif - -struct netio_handler_list -{ - netio_handler_list_type *next; - netio_handler_type *handler; -}; - netio_type * netio_create(region_type *region) { diff --git a/usr.sbin/nsd/netio.h b/usr.sbin/nsd/netio.h index 13035a0d3b2..c6686afc26f 100644 --- a/usr.sbin/nsd/netio.h +++ b/usr.sbin/nsd/netio.h @@ -137,6 +137,13 @@ struct netio_handler }; +struct netio_handler_list +{ + netio_handler_list_type *next; + netio_handler_type *handler; +}; + + /* * Create a new netio instance using the specified REGION. The netio * instance is cleaned up when the REGION is deallocated. diff --git a/usr.sbin/nsd/nsd.conf.sample.in b/usr.sbin/nsd/nsd.conf.sample.in index cf3e0b0c7bd..fe1a4874c5c 100644 --- a/usr.sbin/nsd/nsd.conf.sample.in +++ b/usr.sbin/nsd/nsd.conf.sample.in @@ -16,6 +16,9 @@ server: # ip-address: 1.2.3.4@5678 # ip-address: 12fe::8ef0 + # Allow binding to non local addresses. Default no. + # ip-transparent: no + # don't answer VERSION.BIND and VERSION.SERVER CHAOS class queries # hide-version: no @@ -108,6 +111,22 @@ server: # rrl-whitelist-ratelimit to 0 to disable ratelimit processing. # rrl-ratelimit: 200 + # Response Rate Limiting, number of packets to discard before + # sending a SLIP response (a truncated one, allowing an honest + # resolver to retry with TCP). Default is 2 (one half of the + # queries will receive a SLIP response, 0 disables SLIP (all + # packets are discarded), 1 means every request will get a + # SLIP response. + # rrl-slip: 2 + + # Response Rate Limiting, IPv4 prefix length. Addresses are + # grouped by netblock. + # rrl-ipv4-prefix-length: 24 + + # Response Rate Limiting, IPv6 prefix length. Addresses are + # grouped by netblock. + # rrl-ipv6-prefix-length: 64 + # Response Rate Limiting, maximum QPS allowed (from one query source) # for whitelisted types. Default 2000. # rrl-whitelist-ratelimit: 2000 diff --git a/usr.sbin/nsd/nsd.h b/usr.sbin/nsd/nsd.h index 1dce4d95477..2dd4676937e 100644 --- a/usr.sbin/nsd/nsd.h +++ b/usr.sbin/nsd/nsd.h @@ -57,6 +57,11 @@ struct nsd_options; * channel content during reload */ #define NSD_QUIT_SYNC 11 +/* + * QUIT_CHILD is sent at exit, to make sure the child has exited so that + * port53 is free when all of nsd's processes have exited at shutdown time + */ +#define NSD_QUIT_CHILD 12 #define NSD_SERVER_MAIN 0x0U #define NSD_SERVER_UDP 0x1U @@ -224,6 +229,7 @@ int server_prepare(struct nsd *nsd); void server_main(struct nsd *nsd); void server_child(struct nsd *nsd); void server_shutdown(struct nsd *nsd); +void server_close_all_sockets(struct nsd_socket sockets[], size_t n); /* extra domain numbers for temporary domains */ #define EXTRA_DOMAIN_NUMBERS 1024 diff --git a/usr.sbin/nsd/options.c b/usr.sbin/nsd/options.c index 721c763383e..39cfa610864 100644 --- a/usr.sbin/nsd/options.c +++ b/usr.sbin/nsd/options.c @@ -34,6 +34,7 @@ nsd_options_t* nsd_options_create(region_type* region) opt->keys = NULL; opt->numkeys = 0; opt->ip_addresses = NULL; + opt->ip_transparent = 0; opt->debug_mode = 0; opt->verbosity = 0; opt->hide_version = 0; @@ -67,6 +68,9 @@ nsd_options_t* nsd_options_create(region_type* region) #ifdef RATELIMIT opt->rrl_size = RRL_BUCKETS; opt->rrl_ratelimit = RRL_LIMIT/2; + opt->rrl_slip = RRL_SLIP; + opt->rrl_ipv4_prefix_length = RRL_IPV4_PREFIX_LENGTH; + opt->rrl_ipv6_prefix_length = RRL_IPV6_PREFIX_LENGTH; opt->rrl_whitelist_ratelimit = RRL_WLIST_LIMIT/2; #endif nsd_options = opt; @@ -647,6 +651,7 @@ acl_options_t* parse_acl_info(region_type* region, char* ip, const char* key) acl->use_axfr_only = 0; acl->allow_udp = 0; acl->ixfr_disabled = 0; + acl->bad_xfr_count = 0; acl->key_options = 0; acl->is_ipv6 = 0; acl->port = 0; diff --git a/usr.sbin/nsd/options.h b/usr.sbin/nsd/options.h index 5845b6eaa9f..cab7d5749cf 100644 --- a/usr.sbin/nsd/options.h +++ b/usr.sbin/nsd/options.h @@ -38,6 +38,7 @@ struct nsd_options { /* list of ip adresses to bind to (or NULL for all) */ ip_address_option_t* ip_addresses; + int ip_transparent; int debug_mode; int verbosity; int hide_version; @@ -69,6 +70,11 @@ struct nsd_options { size_t rrl_size; /** max qps for queries, 0 is nolimit */ size_t rrl_ratelimit; + /** ratio of slipped responses, 0 is noslip */ + size_t rrl_slip; + /** ip prefix length */ + size_t rrl_ipv4_prefix_length; + size_t rrl_ipv6_prefix_length; /** max qps for whitelisted queries, 0 is nolimit */ size_t rrl_whitelist_ratelimit; #endif @@ -122,6 +128,7 @@ struct acl_options { uint8_t use_axfr_only; uint8_t allow_udp; time_t ixfr_disabled; + int bad_xfr_count; /* ip address range */ const char* ip_address_spec; diff --git a/usr.sbin/nsd/rrl.c b/usr.sbin/nsd/rrl.c index 01da9d75ff1..65f3788ea59 100644 --- a/usr.sbin/nsd/rrl.c +++ b/usr.sbin/nsd/rrl.c @@ -7,6 +7,7 @@ #include "config.h" #include <errno.h> #include <ctype.h> +#include "dns.h" #include "rrl.h" #include "util.h" #include "lookup3.h" @@ -33,6 +34,8 @@ struct rrl_bucket { /* rate, in queries per second, which due to rate=r(t)+r(t-1)/2 is * equal to double the queries per second */ uint32_t rate; + /* the full hash */ + uint32_t hash; /* counter for queries arrived in this second */ uint32_t counter; /* timestamp, which time is the time of the counter, the rate is from @@ -46,6 +49,10 @@ struct rrl_bucket { static struct rrl_bucket* rrl_array = NULL; static size_t rrl_array_size = RRL_BUCKETS; static uint32_t rrl_ratelimit = RRL_LIMIT; /* 2x qps */ +static uint8_t rrl_slip_ratio = RRL_SLIP; +static uint8_t rrl_ipv4_prefixlen = RRL_IPV4_PREFIX_LENGTH; +static uint8_t rrl_ipv6_prefixlen = RRL_IPV6_PREFIX_LENGTH; +static uint64_t rrl_ipv6_mask; /* max prefixlen 64 */ static uint32_t rrl_whitelist_ratelimit = RRL_WLIST_LIMIT; /* 2x qps */ /* the array of mmaps for the children (saved between reloads) */ @@ -83,7 +90,8 @@ static char* wiredname2str(const uint8_t* dname) return buf; } -void rrl_mmap_init(int numch, size_t numbuck, size_t lm, size_t wlm) +void rrl_mmap_init(int numch, size_t numbuck, size_t lm, size_t wlm, size_t sm, + size_t plf, size_t pls) { #ifdef HAVE_MMAP size_t i; @@ -91,6 +99,15 @@ void rrl_mmap_init(int numch, size_t numbuck, size_t lm, size_t wlm) if(numbuck != 0) rrl_array_size = numbuck; rrl_ratelimit = lm*2; + rrl_slip_ratio = sm; + rrl_ipv4_prefixlen = plf; + rrl_ipv6_prefixlen = pls; + if (pls <= 32) { + rrl_ipv6_mask = ((uint64_t) htonl(0xffffffff << (32-pls))) << 32; + } else { + rrl_ipv6_mask = ((uint64_t) htonl(0xffffffff << (64-pls))) | + (((uint64_t)0xffffffff)<<32); + } rrl_whitelist_ratelimit = wlm*2; #ifdef HAVE_MMAP /* allocate the ratelimit hashtable in a memory map so it is @@ -99,7 +116,7 @@ void rrl_mmap_init(int numch, size_t numbuck, size_t lm, size_t wlm) rrl_maps = (void**)xalloc(sizeof(void*)*rrl_maps_num); for(i=0; i<rrl_maps_num; i++) { rrl_maps[i] = mmap(NULL, - sizeof(struct rrl_bucket)*rrl_array_size, + sizeof(struct rrl_bucket)*rrl_array_size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); if(rrl_maps[i] == MAP_FAILED) { log_msg(LOG_ERR, "rrl: mmap failed: %s", @@ -116,12 +133,6 @@ void rrl_mmap_init(int numch, size_t numbuck, size_t lm, size_t wlm) #endif } -void rrl_set_limit(size_t lm, size_t wlm) -{ - rrl_ratelimit = lm*2; - rrl_whitelist_ratelimit = wlm*2; -} - void rrl_init(size_t ch) { if(!rrl_maps || ch >= rrl_maps_num) @@ -135,7 +146,6 @@ void rrl_init(size_t ch) * for genuine queries and the target for reflected packets */ static uint64_t rrl_get_source(query_type* query, uint16_t* c2) { - /* we take a /24 for IPv4 and /64 for IPv6 */ /* note there is an IPv6 subnet, that maps * to the same buckets as IPv4 space, but there is a flag in c2 * that makes the hash different */ @@ -143,17 +153,17 @@ static uint64_t rrl_get_source(query_type* query, uint16_t* c2) if( ((struct sockaddr_in*)&query->addr)->sin_family == AF_INET) { *c2 = 0; return ((struct sockaddr_in*)&query->addr)-> - sin_addr.s_addr & htonl(0xffffff00); + sin_addr.s_addr & htonl(0xffffffff << (32-rrl_ipv4_prefixlen)); } else { uint64_t s; *c2 = rrl_ip6; memmove(&s, &((struct sockaddr_in6*)&query->addr)->sin6_addr, sizeof(s)); - return s; + return s & rrl_ipv6_mask; } #else *c2 = 0; - return query->addr.sin_addr.s_addr & htonl(0xffffff00); + return query->addr.sin_addr.s_addr & htonl(0xffffffff << (32-rrl_ipv4_prefixlen)); #endif } @@ -170,7 +180,11 @@ static const char* rrlsource2str(uint64_t s, uint16_t c2) memmove(&a6, &s, sizeof(s)); if(!inet_ntop(AF_INET6, &a6, buf, sizeof(buf))) strlcpy(buf, "[ip6 ntop failed]", sizeof(buf)); - else strlcat(buf, "/64", sizeof(buf)); + else { + static char prefix[4]; + snprintf(prefix, sizeof(prefix), "/%d", rrl_ipv6_prefixlen); + strlcat(buf, &prefix[0], sizeof(buf)); + } return buf; } #endif @@ -178,7 +192,11 @@ static const char* rrlsource2str(uint64_t s, uint16_t c2) a4.s_addr = (uint32_t)s; if(!inet_ntop(AF_INET, &a4, buf, sizeof(buf))) strlcpy(buf, "[ip4 ntop failed]", sizeof(buf)); - else strlcat(buf, "/24", sizeof(buf)); + else { + static char prefix[4]; + snprintf(prefix, sizeof(prefix), "/%d", rrl_ipv4_prefixlen); + strlcat(buf, &prefix[0], sizeof(buf)); + } return buf; } @@ -216,7 +234,7 @@ const char* rrltype2str(enum rrl_type c) /** classify the query in a number of different types, each has separate * ratelimiting, so that positive queries are not impeded by others */ -static uint16_t rrl_classify(query_type* query, const uint8_t** d, +static uint16_t rrl_classify(query_type* query, const uint8_t** d, size_t* d_len) { if(RCODE(query->packet) == RCODE_NXDOMAIN) { @@ -292,7 +310,7 @@ static void examine_query(query_type* query, uint32_t* hash, uint64_t* source, *source = rrl_get_source(query, &c2); c = rrl_classify(query, &dname, &dname_len); - if(query->zone && query->zone->opts && + if(query->zone && query->zone->opts && (query->zone->opts->rrl_whitelist & c)) *lm = rrl_whitelist_ratelimit; if(*lm == 0) return; @@ -334,15 +352,21 @@ rrl_msg(query_type* query, const char* str) const uint8_t* d = NULL; size_t d_len; uint64_t s; + char address[128]; if(verbosity < 2) return; + if (addr2ip(query->addr, address, sizeof(address))) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "addr2ip failed")); + strlcpy(address, "[unknown]", sizeof(address)); + } s = rrl_get_source(query, &c2); c = rrl_classify(query, &d, &d_len) | c2; - if(query->zone && query->zone->opts && + if(query->zone && query->zone->opts && (query->zone->opts->rrl_whitelist & c)) wl = 1; - log_msg(LOG_INFO, "ratelimit %s %s type %s%s target %s", + log_msg(LOG_INFO, "ratelimit %s %s type %s%s target %s query %s %s", str, d?wiredname2str(d):"", rrltype2str(c), - wl?"(whitelisted)":"", rrlsource2str(s, c2)); + wl?"(whitelisted)":"", rrlsource2str(s, c2), + address, rrtype_to_string(query->qtype)); } /** true if the query used to be blocked by the ratelimit */ @@ -362,14 +386,23 @@ uint32_t rrl_update(query_type* query, uint32_t hash, uint64_t source, (long long unsigned)source, hash, b->rate, b->counter, b->stamp)); /* check if different source */ - if(b->source != source || b->flags != flags) { + if(b->source != source || b->flags != flags || b->hash != hash) { /* initialise */ /* potentially the wrong limit here, used lower nonwhitelim */ if(verbosity >=2 && - used_to_block(b->rate, b->counter, rrl_ratelimit)) - log_msg(LOG_INFO, "ratelimit unblock ~ type %s target %s", + used_to_block(b->rate, b->counter, rrl_ratelimit)) { + char address[128]; + if (addr2ip(query->addr, address, sizeof(address))) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "addr2ip failed")); + strlcpy(address, "[unknown]", sizeof(address)); + } + log_msg(LOG_INFO, "ratelimit unblock ~ type %s target %s query %s %s (%s collision)", rrltype2str(b->flags), - rrlsource2str(b->source, b->flags)); + rrlsource2str(b->source, b->flags), + address, rrtype_to_string(query->qtype), + (b->hash!=hash?"bucket":"hash")); + } + b->hash = hash; b->source = source; b->flags = flags; b->counter = 1; @@ -443,8 +476,8 @@ int rrl_process_query(query_type* query) query_state_type rrl_slip(query_type* query) { - /* discard half the packets, randomly */ - if((random() & 0x1)) { + /* discard number of packets, randomly */ + if((rrl_slip_ratio > 0) && ((rrl_slip_ratio == 1) || ((random() % rrl_slip_ratio) == 0))) { /* set TC on the rest */ TC_SET(query->packet); ANCOUNT_SET(query->packet, 0); diff --git a/usr.sbin/nsd/rrl.h b/usr.sbin/nsd/rrl.h index fae8fbf2343..48dbb53b8cb 100644 --- a/usr.sbin/nsd/rrl.h +++ b/usr.sbin/nsd/rrl.h @@ -30,14 +30,21 @@ enum rrl_type { #define RRL_BUCKETS 1000000 /** default rrl limit, in 2x qps , the default is 200 qps */ #define RRL_LIMIT 400 +/** default slip */ +#define RRL_SLIP 2 +/** default prefix lengths */ +#define RRL_IPV4_PREFIX_LENGTH 24 +#define RRL_IPV6_PREFIX_LENGTH 64 /** default whitelist rrl limit, in 2x qps, default is thus 2000 qps */ #define RRL_WLIST_LIMIT 4000 /** * Initialize for n children (optional, otherwise no mmaps used) * ratelimits lm and wlm are in qps (this routines x2s them for internal use). + * plf and pls are in prefix lengths. */ -void rrl_mmap_init(int numch, size_t numbuck, size_t lm, size_t wlm); +void rrl_mmap_init(int numch, size_t numbuck, size_t lm, size_t wlm, size_t sm, + size_t plf, size_t pls); /** * Initialize rate limiting (for this child server process) @@ -65,7 +72,5 @@ enum rrl_type rrlstr2type(const char* s); /** for unit test, update rrl bucket; return rate */ uint32_t rrl_update(query_type* query, uint32_t hash, uint64_t source, uint16_t flags, int32_t now, uint32_t lm); -/** set the rate limit counters, pass variables in qps */ -void rrl_set_limit(size_t lm, size_t wlm); #endif /* RRL_H */ diff --git a/usr.sbin/nsd/xfrd-disk.c b/usr.sbin/nsd/xfrd-disk.c index bb869ed9d40..6c052b08f76 100644 --- a/usr.sbin/nsd/xfrd-disk.c +++ b/usr.sbin/nsd/xfrd-disk.c @@ -297,7 +297,7 @@ xfrd_read_state(struct xfrd_state* xfrd) return; } - DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: read %d zones from state file", numzones)); + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: read %d zones from state file", (int)numzones)); fclose(in); region_destroy(tempregion); } @@ -308,23 +308,23 @@ neato_timeout(FILE* out, const char* str, uint32_t secs) { fprintf(out, "%s", str); if(secs <= 0) { - fprintf(out, " %ds", secs); + fprintf(out, " %ds", (int)secs); return; } if(secs >= 3600*24) { - fprintf(out, " %dd", secs/(3600*24)); + fprintf(out, " %dd", (int)secs/(3600*24)); secs = secs % (3600*24); } if(secs >= 3600) { - fprintf(out, " %dh", secs/3600); + fprintf(out, " %dh", (int)secs/3600); secs = secs%3600; } if(secs >= 60) { - fprintf(out, " %dm", secs/60); + fprintf(out, " %dm", (int)secs/60); secs = secs%60; } if(secs > 0) { - fprintf(out, " %ds", secs); + fprintf(out, " %ds", (int)secs); } } @@ -371,17 +371,17 @@ xfrd_write_state_soa(FILE* out, const char* id, fprintf(out, " ago\n"); fprintf(out, "\t%s: %u %u %u %u", id, - ntohs(soa->type), ntohs(soa->klass), - ntohl(soa->ttl), ntohs(soa->rdata_count)); + (unsigned)ntohs(soa->type), (unsigned)ntohs(soa->klass), + (unsigned)ntohl(soa->ttl), (unsigned)ntohs(soa->rdata_count)); fprintf(out, " "); xfrd_write_dname(out, soa->prim_ns); fprintf(out, " "); xfrd_write_dname(out, soa->email); - fprintf(out, " %u", ntohl(soa->serial)); - fprintf(out, " %u", ntohl(soa->refresh)); - fprintf(out, " %u", ntohl(soa->retry)); - fprintf(out, " %u", ntohl(soa->expire)); - fprintf(out, " %u\n", ntohl(soa->minimum)); + fprintf(out, " %u", (unsigned)ntohl(soa->serial)); + fprintf(out, " %u", (unsigned)ntohl(soa->refresh)); + fprintf(out, " %u", (unsigned)ntohl(soa->retry)); + fprintf(out, " %u", (unsigned)ntohl(soa->expire)); + fprintf(out, " %u\n", (unsigned)ntohl(soa->minimum)); fprintf(out, "\t#"); neato_timeout(out, " refresh =", ntohl(soa->refresh)); neato_timeout(out, " retry =", ntohl(soa->retry)); diff --git a/usr.sbin/nsd/xfrd.c b/usr.sbin/nsd/xfrd.c index ea24abad853..e217cd4ac19 100644 --- a/usr.sbin/nsd/xfrd.c +++ b/usr.sbin/nsd/xfrd.c @@ -664,15 +664,18 @@ xfrd_handle_incoming_soa(xfrd_zone_t* zone, xfrd_set_refresh_now(zone); return; } - if(zone->soa_nsd_acquired && soa->serial == zone->soa_nsd.serial) + if(zone->soa_nsd_acquired && soa->serial == zone->soa_nsd.serial) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s has already been updated " + "to serial %u (at time %u)", zone->apex_str, + ntohl(zone->soa_nsd.serial), (unsigned) zone->soa_nsd_acquired)); return; - + } if(zone->soa_disk_acquired && soa->serial == zone->soa_disk.serial) { /* soa in disk has been loaded in memory */ log_msg(LOG_INFO, "Zone %s serial %u is updated to %u.", - zone->apex_str, ntohl(zone->soa_nsd.serial), - ntohl(soa->serial)); + zone->apex_str, (unsigned)ntohl(zone->soa_nsd.serial), + (unsigned)ntohl(soa->serial)); zone->soa_nsd = zone->soa_disk; zone->soa_nsd_acquired = zone->soa_disk_acquired; if((uint32_t)xfrd_time() - zone->soa_disk_acquired @@ -716,7 +719,7 @@ xfrd_handle_incoming_soa(xfrd_zone_t* zone, /* user must have manually provided zone data */ DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s serial %u from unknown source. refreshing", - zone->apex_str, ntohl(soa->serial))); + zone->apex_str, (unsigned)ntohl(soa->serial))); zone->soa_nsd = *soa; zone->soa_disk = *soa; zone->soa_nsd_acquired = acquired; @@ -823,8 +826,19 @@ xfrd_udp_read(xfrd_zone_t* zone) xfrd_make_request(zone); break; case xfrd_packet_more: + case xfrd_packet_drop: + /* drop packet */ + xfrd_udp_release(zone); + /* query next server */ + xfrd_make_request(zone); + break; case xfrd_packet_bad: default: + zone->master->bad_xfr_count++; + if (zone->master->bad_xfr_count > 2) { + zone->master->ixfr_disabled = time(NULL); + zone->master->bad_xfr_count = 0; + } /* drop packet */ xfrd_udp_release(zone); /* query next server */ @@ -900,6 +914,7 @@ xfrd_bind_local_interface(int sockd, acl_options_t* ifc, acl_options_t* acl, #else struct sockaddr_in frm; #endif /* INET6 */ + int ret = 1; if (!ifc) /* no outgoing interface set */ return 1; @@ -913,7 +928,7 @@ xfrd_bind_local_interface(int sockd, acl_options_t* ifc, acl_options_t* acl, DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: bind() %s to %s socket", ifc->ip_address_spec, tcp? "tcp":"udp")); - + ret = 0; frm_len = xfrd_acl_sockaddr_frm(ifc, &frm); if (tcp) { @@ -954,13 +969,13 @@ xfrd_bind_local_interface(int sockd, acl_options_t* ifc, acl_options_t* acl, "failed: %s", ifc->ip_address_spec, tcp? "tcp":"udp", strerror(errno))); + + log_msg(LOG_WARNING, "xfrd: could not bind source address:port to " + "socket: %s", strerror(errno)); /* try another */ ifc = ifc->next; } - - log_msg(LOG_WARNING, "xfrd: could not bind source address:port to " - "socket: %s", strerror(errno)); - return 0; + return ret; } void @@ -1022,7 +1037,7 @@ xfrd_send_ixfr_request_udp(xfrd_zone_t* zone) DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd sent udp request for ixfr=%u for zone %s to %s", - ntohl(zone->soa_disk.serial), + (unsigned)ntohl(zone->soa_disk.serial), zone->apex_str, zone->master->ip_address_spec)); return fd; } @@ -1068,36 +1083,66 @@ xfrd_xfr_check_rrs(xfrd_zone_t* zone, buffer_type* packet, size_t count, int *done, xfrd_soa_t* soa) { /* first RR has already been checked */ + uint32_t tmp_serial = 0; uint16_t type, rrlen; size_t i, soapos; + for(i=0; i<count; ++i,++zone->msg_rr_count) { - if(!packet_skip_dname(packet)) + if (*done) { + /** + * We are done, but there are more RRs coming. Ignore + * trailing garbage. + */ + DEBUG(DEBUG_XFRD,1, (LOG_WARNING, "xfrd: zone %s xfr is " + "done, ignore trailing garbage", zone->apex_str)); + return 1; + } + if(!packet_skip_dname(packet)) { + DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr unable " + "to skip owner name", zone->apex_str)); return 0; - if(!buffer_available(packet, 10)) + } + if(!buffer_available(packet, 10)) { + DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr hdr " + "too small", zone->apex_str)); return 0; + } soapos = buffer_position(packet); type = buffer_read_u16(packet); (void)buffer_read_u16(packet); /* class */ (void)buffer_read_u32(packet); /* ttl */ rrlen = buffer_read_u16(packet); - if(!buffer_available(packet, rrlen)) + if(!buffer_available(packet, rrlen)) { + DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr pkt " + "too small", zone->apex_str)); return 0; + } if(type == TYPE_SOA) { /* check the SOAs */ size_t mempos = buffer_position(packet); buffer_set_position(packet, soapos); - if(!xfrd_parse_soa_info(packet, soa)) + if(!xfrd_parse_soa_info(packet, soa)) { + DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr " + "unable to parse soainfo", zone->apex_str)); return 0; + } if(zone->msg_rr_count == 1 && ntohl(soa->serial) != zone->msg_new_serial) { /* 2nd RR is SOA with lower serial, this is an IXFR */ zone->msg_is_ixfr = 1; - if(!zone->soa_disk_acquired) + if(!zone->soa_disk_acquired) { + DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr " + "got ixfr but need axfr", zone->apex_str)); return 0; /* got IXFR but need AXFR */ - if(ntohl(soa->serial) != ntohl(zone->soa_disk.serial)) + } + if(ntohl(soa->serial) != ntohl(zone->soa_disk.serial)) { + DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr " + "bad start serial", zone->apex_str)); return 0; /* bad start serial in IXFR */ + } zone->msg_old_serial = ntohl(soa->serial); + tmp_serial = ntohl(soa->serial); } else if(ntohl(soa->serial) == zone->msg_new_serial) { /* saw another SOA of new serial. */ @@ -1108,6 +1153,22 @@ xfrd_xfr_check_rrs(xfrd_zone_t* zone, buffer_type* packet, size_t count, *done = 1; } } + else if (zone->msg_is_ixfr) { + /* some additional checks */ + if(ntohl(soa->serial) > zone->msg_new_serial) { + DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr " + "bad middle serial", zone->apex_str)); + return 0; /* bad middle serial in IXFR */ + } + if(ntohl(soa->serial) < tmp_serial) { + DEBUG(DEBUG_XFRD,1, (LOG_ERR, "xfrd: zone %s xfr " + "serial decreasing not allowed", zone->apex_str)); + return 0; /* middle serial decreases in IXFR */ + } + /** serial ok, update tmp serial */ + tmp_serial = ntohl(soa->serial); + + } buffer_set_position(packet, mempos); } buffer_skip(packet, rrlen); @@ -1299,10 +1360,10 @@ xfrd_parse_received_xfr_packet(xfrd_zone_t* zone, buffer_type* packet, return xfrd_packet_newlease; } /* try next master */ - return xfrd_packet_bad; + return xfrd_packet_drop; } DEBUG(DEBUG_XFRD,1, (LOG_INFO, "IXFR reply has ok serial (have \ -%u, reply %u).", ntohl(zone->soa_disk.serial), ntohl(soa->serial))); +%u, reply %u).", (unsigned)ntohl(zone->soa_disk.serial), (unsigned)ntohl(soa->serial))); /* serial is newer than soa_disk */ if(ancount == 1) { /* single record means it is like a notify */ @@ -1379,6 +1440,7 @@ xfrd_handle_received_xfr_packet(xfrd_zone_t* zone, buffer_type* packet) return xfrd_packet_tcp; case xfrd_packet_notimpl: case xfrd_packet_bad: + case xfrd_packet_drop: default: { /* rollback */ @@ -1587,8 +1649,8 @@ xfrd_handle_incoming_notify(xfrd_zone_t* zone, xfrd_soa_t* soa) DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: ignored notify %s %u old serial, zone valid " "(soa disk serial %u)", zone->apex_str, - ntohl(soa->serial), - ntohl(zone->soa_disk.serial))); + (unsigned)ntohl(soa->serial), + (unsigned)ntohl(zone->soa_disk.serial))); return 0; /* ignore notify with old serial, we have a valid zone */ } if(soa == 0) { @@ -1649,9 +1711,15 @@ xfrd_check_failed_updates() soa time is before the time of the reload cmd. */ xfrd_soa_t dumped_soa = zone->soa_disk; log_msg(LOG_ERR, "xfrd: zone %s: soa serial %u " - "update failed, restarting " - "transfer (notified zone)", - zone->apex_str, ntohl(zone->soa_disk.serial)); + "update failed (acquired: %u), restarting " + "transfer (notified zone)", + zone->apex_str, ntohl(zone->soa_disk.serial), + (unsigned) zone->soa_disk_acquired); + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s: nsd has " + "soa serial %u (acquired: %u, reload cmd sent: " + "%u)", zone->apex_str, ntohl(zone->soa_nsd.serial), + (unsigned) zone->soa_nsd_acquired, + (unsigned) xfrd->reload_cmd_last_sent)); /* revert the soa; it has not been acquired properly */ zone->soa_disk_acquired = zone->soa_nsd_acquired; zone->soa_disk = zone->soa_nsd; @@ -1665,8 +1733,8 @@ xfrd_check_failed_updates() if(xfrd->need_to_send_reload == 0 && xfrd->reload_handler.timeout == NULL) { log_msg(LOG_ERR, "xfrd: zone %s: needs " - "to be loaded. reload lost? " - "try again", zone->apex_str); + "to be loaded. reload lost? " + "try again", zone->apex_str); xfrd_set_reload_timeout(); } } diff --git a/usr.sbin/nsd/xfrd.h b/usr.sbin/nsd/xfrd.h index 49337035277..e4d6a278259 100644 --- a/usr.sbin/nsd/xfrd.h +++ b/usr.sbin/nsd/xfrd.h @@ -178,6 +178,7 @@ struct xfrd_zone { enum xfrd_packet_result { xfrd_packet_bad, /* drop the packet/connection */ + xfrd_packet_drop, /* drop the connection, but not report bad */ xfrd_packet_more, /* more packets to follow on tcp */ xfrd_packet_notimpl, /* server responded with NOTIMPL or FORMATERR */ xfrd_packet_tcp, /* try tcp connection */ |