diff options
Diffstat (limited to 'usr.sbin/nsd')
30 files changed, 581 insertions, 140 deletions
diff --git a/usr.sbin/nsd/Makefile.in b/usr.sbin/nsd/Makefile.in index 4e6915d4461..39076034e6c 100644 --- a/usr.sbin/nsd/Makefile.in +++ b/usr.sbin/nsd/Makefile.in @@ -81,7 +81,7 @@ MANUALS=nsd.8 nsd-checkconf.8 nsd-checkzone.8 nsd-control.8 nsd.conf.5 COMMON_OBJ=answer.o axfr.o buffer.o configlexer.o configparser.o dname.o dns.o edns.o iterated_hash.o lookup3.o namedb.o nsec3.o options.o packet.o query.o rbtree.o radtree.o rdata.o region-allocator.o rrl.o tsig.o tsig-openssl.o udb.o udbradtree.o udbzone.o util.o bitset.o popen3.o XFRD_OBJ=xfrd-disk.o xfrd-notify.o xfrd-tcp.o xfrd.o remote.o $(DNSTAP_OBJ) NSD_OBJ=$(COMMON_OBJ) $(XFRD_OBJ) difffile.o ipc.o mini_event.o netio.o nsd.o server.o dbaccess.o dbcreate.o zlexer.o zonec.o zparser.o -ALL_OBJ=$(NSD_OBJ) nsd-checkconf.o nsd-checkzone.o nsd-control.o nsd-mem.o +ALL_OBJ=$(NSD_OBJ) nsd-checkconf.o nsd-checkzone.o nsd-control.o nsd-mem.o xfr-inspect.o NSD_CHECKCONF_OBJ=$(COMMON_OBJ) nsd-checkconf.o NSD_CHECKZONE_OBJ=$(COMMON_OBJ) $(XFRD_OBJ) dbaccess.o dbcreate.o difffile.o ipc.o mini_event.o netio.o server.o zonec.o zparser.o zlexer.o nsd-checkzone.o NSD_CONTROL_OBJ=$(COMMON_OBJ) nsd-control.o @@ -193,9 +193,6 @@ audit: nsd nsd-checkconf nsd-checkzone nsd-control nsd-mem checksec ./checksec --file=nsd-control ./checksec --file=nsd-mem -test check: cutest - ./cutest - clean: rm -f *.o $(TARGETS) $(MANUALS) cutest popen3_echo udb-inspect xfr-inspect nsd-mem diff --git a/usr.sbin/nsd/config.h.in b/usr.sbin/nsd/config.h.in index 67cd876e427..35cd47f2e82 100644 --- a/usr.sbin/nsd/config.h.in +++ b/usr.sbin/nsd/config.h.in @@ -9,6 +9,9 @@ /* NSD default chroot directory */ #undef CHROOTDIR +/* Command line arguments used with configure */ +#undef CONFCMDLINE + /* NSD config dir */ #undef CONFIGDIR diff --git a/usr.sbin/nsd/configlexer.lex b/usr.sbin/nsd/configlexer.lex index 8469bbce35d..bb4dd3499c4 100644 --- a/usr.sbin/nsd/configlexer.lex +++ b/usr.sbin/nsd/configlexer.lex @@ -215,6 +215,7 @@ identity{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IDENTITY;} version{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_VERSION;} nsid{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_NSID;} logfile{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_LOGFILE;} +log-only-syslog{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_LOG_ONLY_SYSLOG;} server-count{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_SERVER_COUNT;} tcp-count{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_TCP_COUNT;} tcp-reject-overflow{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_TCP_REJECT_OVERFLOW;} @@ -290,6 +291,7 @@ max-refresh-time{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MAX_REFRESH_TIM 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;} +min-expire-time{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MIN_EXPIRE_TIME;} multi-master-check{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MULTI_MASTER_CHECK;} tls-service-key{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_TLS_SERVICE_KEY;} tls-service-ocsp{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_TLS_SERVICE_OCSP;} diff --git a/usr.sbin/nsd/configparser.y b/usr.sbin/nsd/configparser.y index 8b898153c94..2834e66e38f 100644 --- a/usr.sbin/nsd/configparser.y +++ b/usr.sbin/nsd/configparser.y @@ -33,6 +33,7 @@ extern config_parser_state_type *cfg_parser; static void append_acl(struct acl_options **list, struct acl_options *acl); static int parse_boolean(const char *str, int *bln); +static int parse_expire_expr(const char *str, long long *num, uint8_t *expr); static int parse_number(const char *str, long long *num); static int parse_range(const char *str, long long *low, long long *high); %} @@ -76,6 +77,7 @@ static int parse_range(const char *str, long long *low, long long *high); %token VAR_ZONELISTFILE %token VAR_DATABASE %token VAR_LOGFILE +%token VAR_LOG_ONLY_SYSLOG %token VAR_PIDFILE %token VAR_DIFFFILE %token VAR_XFRDFILE @@ -160,6 +162,7 @@ static int parse_range(const char *str, long long *low, long long *high); %token VAR_MIN_REFRESH_TIME %token VAR_MAX_RETRY_TIME %token VAR_MIN_RETRY_TIME +%token VAR_MIN_EXPIRE_TIME %token VAR_MULTI_MASTER_CHECK %token VAR_SIZE_LIMIT_XFR %token VAR_ZONESTATS @@ -291,6 +294,8 @@ server_option: } | VAR_LOGFILE STRING { cfg_parser->opt->logfile = region_strdup(cfg_parser->opt->region, $2); } + | VAR_LOG_ONLY_SYSLOG boolean + { cfg_parser->opt->log_only_syslog = $2; } | VAR_TCP_COUNT number { if ($2 > 0) { @@ -854,7 +859,19 @@ pattern_or_zone_option: { cfg_parser->pattern->min_retry_time = $2; cfg_parser->pattern->min_retry_time_is_default = 0; - } ; + } + | VAR_MIN_EXPIRE_TIME STRING + { + long long num; + uint8_t expr; + + if (!parse_expire_expr($2, &num, &expr)) { + yyerror("expected an expire time in seconds or \"refresh+retry+1\""); + YYABORT; /* trigger a parser error */ + } + cfg_parser->pattern->min_expire_time = num; + cfg_parser->pattern->min_expire_time_expr = expr; + }; ip_address: STRING @@ -916,6 +933,21 @@ parse_boolean(const char *str, int *bln) } static int +parse_expire_expr(const char *str, long long *num, uint8_t *expr) +{ + if(parse_number(str, num)) { + *expr = EXPIRE_TIME_HAS_VALUE; + return 1; + } + if(strcmp(str, REFRESHPLUSRETRYPLUS1_STR) == 0) { + *num = 0; + *expr = REFRESHPLUSRETRYPLUS1; + return 1; + } + return 0; +} + +static int parse_number(const char *str, long long *num) { /* ensure string consists entirely of digits */ diff --git a/usr.sbin/nsd/configure b/usr.sbin/nsd/configure index 00191db9e5d..4fd89ab9e62 100644 --- a/usr.sbin/nsd/configure +++ b/usr.sbin/nsd/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for NSD 4.3.1. +# Generated by GNU Autoconf 2.69 for NSD 4.3.2. # # Report bugs to <nsd-bugs@nlnetlabs.nl>. # @@ -580,8 +580,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='NSD' PACKAGE_TARNAME='nsd' -PACKAGE_VERSION='4.3.1' -PACKAGE_STRING='NSD 4.3.1' +PACKAGE_VERSION='4.3.2' +PACKAGE_STRING='NSD 4.3.2' PACKAGE_BUGREPORT='nsd-bugs@nlnetlabs.nl' PACKAGE_URL='' @@ -685,6 +685,7 @@ infodir docdir oldincludedir includedir +runstatedir localstatedir sharedstatedir sysconfdir @@ -801,6 +802,7 @@ datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' +runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' @@ -1053,6 +1055,15 @@ do | -silent | --silent | --silen | --sile | --sil) silent=yes ;; + -runstatedir | --runstatedir | --runstatedi | --runstated \ + | --runstate | --runstat | --runsta | --runst | --runs \ + | --run | --ru | --r) + ac_prev=runstatedir ;; + -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ + | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ + | --run=* | --ru=* | --r=*) + runstatedir=$ac_optarg ;; + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ @@ -1190,7 +1201,7 @@ fi for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ - libdir localedir mandir + libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. @@ -1303,7 +1314,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures NSD 4.3.1 to adapt to many kinds of systems. +\`configure' configures NSD 4.3.2 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1343,6 +1354,7 @@ Fine tuning of the installation directories: --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] @@ -1364,7 +1376,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of NSD 4.3.1:";; + short | recursive ) echo "Configuration of NSD 4.3.2:";; esac cat <<\_ACEOF @@ -1524,7 +1536,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -NSD configure 4.3.1 +NSD configure 4.3.2 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2233,7 +2245,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by NSD $as_me 4.3.1, which was +It was created by NSD $as_me 4.3.2, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2595,6 +2607,13 @@ ac_config_headers="$ac_config_headers config.h" +cmdln="`echo $@ | sed -e 's/\\\\/\\\\\\\\/g' | sed -e 's/"/\\\\"/'g`" + +cat >>confdefs.h <<_ACEOF +#define CONFCMDLINE "$cmdln" +_ACEOF + + CFLAGS="$CFLAGS" ac_ext=c ac_cpp='$CPP $CPPFLAGS' @@ -10814,7 +10833,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by NSD $as_me 4.3.1, which was +This file was extended by NSD $as_me 4.3.2, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -10876,7 +10895,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -NSD config.status 4.3.1 +NSD config.status 4.3.2 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/usr.sbin/nsd/configure.ac b/usr.sbin/nsd/configure.ac index 4e011b65ed2..7bb86fce127 100644 --- a/usr.sbin/nsd/configure.ac +++ b/usr.sbin/nsd/configure.ac @@ -5,7 +5,7 @@ dnl sinclude(acx_nlnetlabs.m4) sinclude(dnstap/dnstap.m4) -AC_INIT(NSD,4.3.1,nsd-bugs@nlnetlabs.nl) +AC_INIT(NSD,4.3.2,nsd-bugs@nlnetlabs.nl) AC_CONFIG_HEADER([config.h]) # @@ -19,6 +19,9 @@ AC_ARG_VAR(EGREP, [location of the egrep program]) AC_ARG_VAR(LEX, [location of the lex program with GNU extensions (flex)]) AC_ARG_VAR(YACC, [location of the yacc program with GNU extensions (bison)]) +cmdln="`echo $@ | sed -e 's/\\\\/\\\\\\\\/g' | sed -e 's/"/\\\\"/'g`" +AC_DEFINE_UNQUOTED(CONFCMDLINE, ["$cmdln"], [Command line arguments used with configure]) + CFLAGS="$CFLAGS" AC_AIX if test "$ac_cv_header_minix_config_h" = "yes"; then diff --git a/usr.sbin/nsd/doc/ChangeLog b/usr.sbin/nsd/doc/ChangeLog index 09ea79bafd3..ba80174afdf 100644 --- a/usr.sbin/nsd/doc/ChangeLog +++ b/usr.sbin/nsd/doc/ChangeLog @@ -1,3 +1,69 @@ +7 July 2020: Wouter + - Tag for 4.3.2rc1. + +6 July 2020: Wouter + - Fix compile includes for xfr-inspect tool on FreeBSD. + - Add tpkg/run_vm.sh that runs test when in a virtual machine. + - Merge #112 from jaredmauch: log old and new serials when NSD + rejects an IXFR due to an old serial number. + - Fix bug034 test for vm test changes. + +22 June 2020: Wouter + - Remove errno reset behaviour from sendmmsg and recvmmsg + replacement functions. + - Fix unit test for different nsd-control-setup -h exit code. + +19 June 2020: Wouter + - Merge #108 from Nomis: Make the max-retry-time description clearer. + - Retry when udp send buffer is full to wait until buffer space is + available. + +18 June 2020: Wouter + - Do not log EAGAIN errors for sendmmsg, to stop log spam on OpenBSD. + +17 June 2020: Wouter + - Fix #107: nsd -v shows configure line, openssl version and libevent version. + +27 May 2020: Wouter + - Fix unlink of pidfile warning if not possible due to permissions, + nsd can display the message at high verbosity levels. + - Update contrib/nsd.service for chown of nsd.log and /var/log in + ReadWritePaths. + - Removed contrib/nsd.service, example is too complicated and not + useful. + +15 May 2020: Wouter + - Merge PR#102 from and0x000: add missing default in documentation + for drop-updates. + - Fix checkconf test for log-only-syslog option. + +14 May 2020: Wouter + - Document default value for tcp-timeout. + +13 May 2020: Jeroen + - Fix #99: Fix copying of socket properties with reuseport enabled. + +24 April 2020: Wouter + - Fix #97: EDNS unknown version: query not in response. + +21 April 2020: Wouter + - Fix #96: log-only-syslog: yes sets to only use syslog, fixes + that the default configuration and systemd results in duplicate + log messages. + +20 April 2020: Wouter + - Fix #95: Removed make test check because tpkg not included in + release tarballs. + - Fix unused parameter compile warnings. + +16 April 2020: Wouter + - Tag for 4.3.1 release and track 4.3.2 release in code repository. + - note sha256 digest algo use in makedist.sh. + - Fix for posix shell syntax for trap in nsd-control-setup. + - Fix to omit the listen-on lines from log at startup, unless verbose. + - Fix uninitialised values for bindtodevice option at startup with + reuseport and multiple interfaces. + 8 April 2020: Wouter - Tag for 4.3.1rc2. diff --git a/usr.sbin/nsd/doc/RELNOTES b/usr.sbin/nsd/doc/RELNOTES index f1d30850703..298542e3487 100644 --- a/usr.sbin/nsd/doc/RELNOTES +++ b/usr.sbin/nsd/doc/RELNOTES @@ -1,5 +1,45 @@ NSD RELEASE NOTES +4.3.2 +================ +FEATURES: + - Fix #96: log-only-syslog: yes sets to only use syslog, fixes + that the default configuration and systemd results in duplicate + log messages. + - Fix #107: nsd -v shows configure line, openssl version and libevent version. + - Fix #103 with #110: min-expire-time option. To provide a lower + bound for expire period. Expressed in number of seconds or + refresh+retry+1. +BUG FIXES: + - Fix for posix shell syntax for trap in nsd-control-setup + - Fix to omit the listen-on lines from log at startup, unless verbose. + - Fix uninitialised values for bindtodevice option at startup with + reuseport and multiple interfaces. + - Fix #95: Removed make test check because tpkg not included in + release tarballs. + - Fix unused parameter compile warnings. + - Fix #97: EDNS unknown version: query not in response. + - Fix #99: Fix copying of socket properties with reuseport enabled. + - Document default value for tcp-timeout. + - Merge PR#102 from and0x000: add missing default in documentation + for drop-updates. + - Fix unlink of pidfile warning if not possible due to permissions, + nsd can display the message at high verbosity levels. + - Removed contrib/nsd.service, example is too complicated and not + useful. + - Do not log EAGAIN errors for sendmmsg, to stop log spam on OpenBSD. + - Merge #108 from Nomis: Make the max-retry-time description clearer. + - Retry when udp send buffer is full to wait until buffer space is + available. + - Remove errno reset behaviour from sendmmsg and recvmmsg + replacement functions. + - Fix unit test for different nsd-control-setup -h exit code. + - Merge #112 from jaredmauch: log old and new serials when NSD + rejects an IXFR due to an old serial number. + - Fix #106: Adhere better to xfrd bounds. Refresh and retry times. + - Fix #105: Clearing hash_tree means just emptying the tree. + + 4.3.1 ================ BUG FIXES: diff --git a/usr.sbin/nsd/namedb.c b/usr.sbin/nsd/namedb.c index d034649af1a..3ee97ca1e68 100644 --- a/usr.sbin/nsd/namedb.c +++ b/usr.sbin/nsd/namedb.c @@ -289,21 +289,6 @@ domain_table_deldomain(namedb_type* db, domain_type* domain) } } -/** clear hash tree */ -void -hash_tree_clear(rbtree_type* tree) -{ - rbnode_type* n; - if(!tree) return; - - /* note that elements are no longer in the tree */ - for(n=rbtree_first(tree); n!=RBTREE_NULL; n=rbtree_next(n)) { - n->key = NULL; - } - tree->count = 0; - tree->root = RBTREE_NULL; -} - void hash_tree_delete(region_type* region, rbtree_type* tree) { region_recycle(region, tree, sizeof(rbtree_type)); @@ -316,7 +301,9 @@ void zone_add_domain_in_hash_tree(region_type* region, rbtree_type** tree, { if(!*tree) *tree = rbtree_create(region, cmpf); - if(node->key) return; + if(node->key && node->key == domain + && rbtree_search(*tree, domain) == node) + return; memset(node, 0, sizeof(rbnode_type)); node->key = domain; rbtree_insert(*tree, node); diff --git a/usr.sbin/nsd/namedb.h b/usr.sbin/nsd/namedb.h index 4eb4d6852ed..ad43da86751 100644 --- a/usr.sbin/nsd/namedb.h +++ b/usr.sbin/nsd/namedb.h @@ -231,7 +231,6 @@ void zone_add_domain_in_hash_tree(region_type* region, rbtree_type** tree, int (*cmpf)(const void*, const void*), domain_type* domain, rbnode_type* node); void zone_del_domain_in_hash_tree(rbtree_type* tree, rbnode_type* node); -void hash_tree_clear(rbtree_type* tree); void hash_tree_delete(region_type* region, rbtree_type* tree); void prehash_clear(domain_table_type* table); void prehash_add(domain_table_type* table, domain_type* domain); diff --git a/usr.sbin/nsd/nsd-checkconf.8.in b/usr.sbin/nsd/nsd-checkconf.8.in index 37179ffb0f6..a5c985cbd81 100644 --- a/usr.sbin/nsd/nsd-checkconf.8.in +++ b/usr.sbin/nsd/nsd-checkconf.8.in @@ -1,4 +1,4 @@ -.TH "nsd\-checkconf" "8" "Apr 16, 2020" "NLnet Labs" "nsd 4.3.1" +.TH "nsd\-checkconf" "8" "Jul 14, 2020" "NLnet Labs" "nsd 4.3.2" .\" Copyright (c) 2001\-2008, NLnet Labs. All rights reserved. .\" See LICENSE for the license. .SH "NAME" diff --git a/usr.sbin/nsd/nsd-checkconf.c b/usr.sbin/nsd/nsd-checkconf.c index 0808d85dc73..b392c71113b 100644 --- a/usr.sbin/nsd/nsd-checkconf.c +++ b/usr.sbin/nsd/nsd-checkconf.c @@ -317,6 +317,7 @@ config_print_zone(nsd_options_type* opt, const char* k, int s, const char *o, 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(min_expire_time, o, zone->pattern); ZONE_GET_INT(size_limit_xfr, o, zone->pattern); #ifdef RATELIMIT ZONE_GET_RRL(rrl_whitelist, o, zone->pattern); @@ -348,6 +349,7 @@ config_print_zone(nsd_options_type* opt, const char* k, int s, const char *o, 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(min_expire_time, o, p); ZONE_GET_INT(size_limit_xfr, o, p); #ifdef RATELIMIT ZONE_GET_RRL(rrl_whitelist, o, p); @@ -375,6 +377,7 @@ config_print_zone(nsd_options_type* opt, const char* k, int s, const char *o, SERV_GET_BIN(confine_to_zone, o); SERV_GET_BIN(refuse_any, o); SERV_GET_BIN(tcp_reject_overflow, o); + SERV_GET_BIN(log_only_syslog, o); /* str */ SERV_GET_PATH(final, database, o); SERV_GET_STR(identity, o); @@ -482,6 +485,10 @@ static void print_zone_content_elems(pattern_options_type* pat) 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->min_expire_time_expr == REFRESHPLUSRETRYPLUS1) + printf("\tmin-expire-time: " REFRESHPLUSRETRYPLUS1_STR "\n"); + else if(pat->min_expire_time_expr == EXPIRE_TIME_HAS_VALUE) + printf("\tmin-expire-time: %d\n", pat->min_expire_time); if(pat->size_limit_xfr != 0) printf("\tsize-limit-xfr: %llu\n", (long long unsigned)pat->size_limit_xfr); @@ -515,6 +522,7 @@ config_test_print_server(nsd_options_type* opt) print_string_var("version:", opt->version); print_string_var("nsid:", opt->nsid); print_string_var("logfile:", opt->logfile); + printf("\tlog-only-syslog: %s\n", opt->log_only_syslog?"yes":"no"); printf("\tserver-count: %d\n", opt->server_count); if(opt->cpu_affinity) { cpu_option_type *n; diff --git a/usr.sbin/nsd/nsd-checkzone.8.in b/usr.sbin/nsd/nsd-checkzone.8.in index 7a57213f686..a77b9ad5266 100644 --- a/usr.sbin/nsd/nsd-checkzone.8.in +++ b/usr.sbin/nsd/nsd-checkzone.8.in @@ -1,4 +1,4 @@ -.TH "nsd\-checkzone" "8" "Apr 16, 2020" "NLnet Labs" "nsd 4.3.1" +.TH "nsd\-checkzone" "8" "Jul 14, 2020" "NLnet Labs" "nsd 4.3.2" .\" Copyright (c) 2014, NLnet Labs. All rights reserved. .\" See LICENSE for the license. .SH "NAME" diff --git a/usr.sbin/nsd/nsd-control-setup.sh.in b/usr.sbin/nsd/nsd-control-setup.sh.in index e0c52b70042..ab9b1eaf4c5 100644 --- a/usr.sbin/nsd/nsd-control-setup.sh.in +++ b/usr.sbin/nsd/nsd-control-setup.sh.in @@ -110,7 +110,7 @@ shift $((OPTIND - 1)) echo "setup in directory $DESTDIR" cd "$DESTDIR" -trap cleanup SIGINT +trap cleanup INT # === # Generate server certificate diff --git a/usr.sbin/nsd/nsd-control.8.in b/usr.sbin/nsd/nsd-control.8.in index cdb2104ee93..261ee64250c 100644 --- a/usr.sbin/nsd/nsd-control.8.in +++ b/usr.sbin/nsd/nsd-control.8.in @@ -1,4 +1,4 @@ -.TH "nsd\-control" "8" "Apr 16, 2020" "NLnet Labs" "nsd 4.3.1" +.TH "nsd\-control" "8" "Jul 14, 2020" "NLnet Labs" "nsd 4.3.2" .\" Copyright (c) 2011, NLnet Labs. All rights reserved. .\" See LICENSE for the license. .SH "NAME" diff --git a/usr.sbin/nsd/nsd.8.in b/usr.sbin/nsd/nsd.8.in index 06a96b15e29..543ba6c4348 100644 --- a/usr.sbin/nsd/nsd.8.in +++ b/usr.sbin/nsd/nsd.8.in @@ -1,9 +1,9 @@ -.TH "NSD" "8" "Apr 16, 2020" "NLnet Labs" "NSD 4.3.1" +.TH "NSD" "8" "Jul 14, 2020" "NLnet Labs" "NSD 4.3.2" .\" Copyright (c) 2001\-2008, NLnet Labs. All rights reserved. .\" See LICENSE for the license. .SH "NAME" .B nsd -\- Name Server Daemon (NSD) version 4.3.1. +\- Name Server Daemon (NSD) version 4.3.2. .SH "SYNOPSIS" .B nsd .RB [ \-4 ] diff --git a/usr.sbin/nsd/nsd.c b/usr.sbin/nsd/nsd.c index 46f5fb18c48..3d6fe866dda 100644 --- a/usr.sbin/nsd/nsd.c +++ b/usr.sbin/nsd/nsd.c @@ -113,6 +113,31 @@ version(void) { fprintf(stderr, "%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION); fprintf(stderr, "Written by NLnet Labs.\n\n"); + fprintf(stderr, "Configure line: %s\n", CONFCMDLINE); +#ifdef USE_MINI_EVENT + fprintf(stderr, "Event loop: internal (uses select)\n"); +#else +# if defined(HAVE_EV_LOOP) || defined(HAVE_EV_DEFAULT_LOOP) + fprintf(stderr, "Event loop: %s %s (uses %s)\n", + "libev", + nsd_event_vs(), + nsd_event_method()); +# else + fprintf(stderr, "Event loop: %s %s (uses %s)\n", + "libevent", + nsd_event_vs(), + nsd_event_method()); +# endif +#endif +#ifdef HAVE_SSL + fprintf(stderr, "Linked with %s\n\n", +# ifdef SSLEAY_VERSION + SSLeay_version(SSLEAY_VERSION) +# else + OpenSSL_version(OPENSSL_VERSION) +# endif + ); +#endif fprintf(stderr, "Copyright (C) 2001-2006 NLnet Labs. This is free software.\n" "There is NO warranty; not even for MERCHANTABILITY or FITNESS\n" @@ -357,7 +382,7 @@ find_device( len = strlen(ifa->ifa_name); } if (len < sizeof(sock->device)) { - strlcpy(sock->device, ifa->ifa_name, len); + strlcpy(sock->device, ifa->ifa_name, len+1); return 1; } } @@ -522,12 +547,12 @@ print_sockets( addrport2str(&udp[i].addr.ai_addr, sockbuf, sizeof(sockbuf)); print_socket_servers(&udp[i], serverbuf, serverbufsz); nsd_bitset_or(servers, servers, udp[i].servers); - log_msg(LOG_NOTICE, fmt, sockbuf, "udp", serverbuf); + VERBOSITY(3, (LOG_NOTICE, fmt, sockbuf, "udp", serverbuf)); assert(tcp[i].servers->size == servercnt); addrport2str(&tcp[i].addr.ai_addr, sockbuf, sizeof(sockbuf)); print_socket_servers(&tcp[i], serverbuf, serverbufsz); nsd_bitset_or(servers, servers, tcp[i].servers); - log_msg(LOG_NOTICE, fmt, sockbuf, "tcp", serverbuf); + VERBOSITY(3, (LOG_NOTICE, fmt, sockbuf, "tcp", serverbuf)); } @@ -641,9 +666,14 @@ unlinkpid(const char* file) close(fd); /* unlink pidfile */ - if (unlink(file) == -1) - log_msg(LOG_WARNING, "failed to unlink pidfile %s: %s", - file, strerror(errno)); + if (unlink(file) == -1) { + /* this unlink may not work if the pidfile is located + * outside of the chroot/workdir or we no longer + * have permissions */ + VERBOSITY(3, (LOG_WARNING, + "failed to unlink pidfile %s: %s", + file, strerror(errno))); + } } } @@ -1287,7 +1317,9 @@ main(int argc, char *argv[]) /* Set up the logging */ log_open(LOG_PID, FACILITY, nsd.log_filename); - if (!nsd.log_filename) + if(nsd.options->log_only_syslog) + log_set_log_function(log_only_syslog); + else if (!nsd.log_filename) log_set_log_function(log_syslog); else if (nsd.uid && nsd.gid) { if(chown(nsd.log_filename, nsd.uid, nsd.gid) != 0) diff --git a/usr.sbin/nsd/nsd.conf.5.in b/usr.sbin/nsd/nsd.conf.5.in index dadafa12c11..0203aa49249 100644 --- a/usr.sbin/nsd/nsd.conf.5.in +++ b/usr.sbin/nsd/nsd.conf.5.in @@ -1,4 +1,4 @@ -.TH "nsd.conf" "5" "Apr 16, 2020" "NLnet Labs" "nsd 4.3.1" +.TH "nsd.conf" "5" "Jul 14, 2020" "NLnet Labs" "nsd 4.3.2" .\" Copyright (c) 2001\-2008, NLnet Labs. All rights reserved. .\" See LICENSE for the license. .SH "NAME" @@ -138,7 +138,7 @@ clause. There may only be one .B server: clause. .TP -.B ip\-address:\fR <ip4 or ip6>[@port] [servers] +.B ip\-address:\fR <ip4 or ip6>[@port] [servers] [bindtodevice] [setfib] NSD will bind to the listed ip\-address. Can be given multiple times to bind multiple ip\-addresses. Optionally, a port number can be given. If none are given NSD listens to the wildcard interface. Same as commandline option @@ -156,7 +156,7 @@ send to the internet, and it picks the wrong one. Typically needed for anycast instances. Use ip-transparent to be able to list addresses that turn on later (typical for certain load-balancing). .TP -.B interface:\fR <ip4 or ip6>[@port] [servers] [setfib] +.B interface:\fR <ip4 or ip6>[@port] [servers] [bindtodevice] [setfib] Same as ip\-address (for easy of compatibility with unbound.conf). .TP .B ip\-transparent:\fR <yes or no> @@ -176,11 +176,6 @@ than 1 (such as, equal to the number of cpus). The default is no. It works on Linux, but does not work on FreeBSD, and likely does not work on other systems. .TP -.B bindtodevice:\fR <yes or no> -Use the SO_BINDTODEVICE socket option to bind the socket to the device to -ensure responses go out the same interface the corresponding query came in on -and skip interface selection by the kernel. -.TP .B send\-buffer\-size:\fR <number> Set the send buffer size for query-servicing sockets. Set to 0 to use the default settings. .TP @@ -241,6 +236,12 @@ Log messages to the logfile. The default is to log to stderr and syslog (with facility LOG_DAEMON). Same as commandline option .BR \-l . .TP +.B log\-only\-syslog:\fR <yes or no> +Log messages only to syslog. Useful with systemd so that print to stderr +does not cause duplicate log strings in journald. Before syslog has +been opened, the server uses stderr. Stderr is also used if syslog is +not available. Default is no. +.TP .B server\-count:\fR <number> Start this many NSD servers. Default is 1. Same as commandline option @@ -277,6 +278,7 @@ Default is 0, meaning there is no maximum. .TP .B tcp\-timeout:\fR <number> Overrides the default TCP timeout. This also affects zone transfers over TCP. +The default is 120 seconds. .TP .B tcp-mss:\fR <number> Maximum segment size (MSS) of TCP socket on which the server responds @@ -393,7 +395,7 @@ Prevent NSD from replying with the identity string on CHAOS class queries. Default is no. .TP .B drop\-updates:\fR <yes or no> -If set to yes, drop received packets with the UPDATE opcode. +If set to yes, drop received packets with the UPDATE opcode. Default is no. .TP .B use\-systemd:\fR <yes or no> This option is deprecated and ignored. If compiled with libsystemd, @@ -736,13 +738,21 @@ SOA record is used, but this option restricts that value. 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. +Limit retry time for secondary zones. This is the timer which retries after +a failed fetch attempt for the zone. Normally the value from the SOA record is +used, followed by an exponential backoff, but this option restricts that value. .TP .B min\-retry\-time:\fR <seconds> Limit retry time for secondary zones. .TP +.B min\-expire\-time:\fR <seconds or refresh+retry+1> +Limit expire time for secondary zones. The value can be expressed either by a +number of seconds, or the string "refresh+retry+1". With the latter the expire +time will be lower bound to the refresh plus the retry value from the SOA +record, plus 1. The refresh and retry values will be subject to the bounds +configured with max\-refresh\-time, min\-refresh\-time, max\-retry\-time and +min\-retry\-time if given. +.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 3ea1782e6ee..8fb0ad88ce3 100644 --- a/usr.sbin/nsd/nsd.conf.sample.in +++ b/usr.sbin/nsd/nsd.conf.sample.in @@ -118,6 +118,9 @@ server: # facility LOG_DAEMON). stderr disappears when daemon goes to bg. # logfile: "@logfile@" + # log only to syslog. + # log-only-syslog: no + # File to store pid for nsd in. # pidfile: "@pidfile@" @@ -354,6 +357,10 @@ remote-control: #min-refresh-time: 0 #max-retry-time: 1209600 #min-retry-time: 0 + # Lower bound of expire interval in seconds. The value can be "refresh+retry+1" + # in which case the lower bound of expire interval is the sum of the refresh and + # retry values (limited to the bounds given with the above parameters), plus 1. + #min-expire-time: 0 # Slave server tries zone transfer to all masters and picks highest # zone version available, for when masters have different versions. diff --git a/usr.sbin/nsd/nsd.h b/usr.sbin/nsd/nsd.h index 9f460494207..86b8ad66ee2 100644 --- a/usr.sbin/nsd/nsd.h +++ b/usr.sbin/nsd/nsd.h @@ -337,6 +337,8 @@ void server_main(struct nsd *nsd); void server_child(struct nsd *nsd); void server_shutdown(struct nsd *nsd) ATTR_NORETURN; void server_close_all_sockets(struct nsd_socket sockets[], size_t n); +const char* nsd_event_vs(void); +const char* nsd_event_method(void); struct event_base* nsd_child_event_base(void); void service_remaining_tcp(struct nsd* nsd); /* extra domain numbers for temporary domains */ diff --git a/usr.sbin/nsd/nsec3.c b/usr.sbin/nsd/nsec3.c index 566b69ca225..ef7c5ee6f96 100644 --- a/usr.sbin/nsd/nsec3.c +++ b/usr.sbin/nsd/nsec3.c @@ -396,6 +396,31 @@ nsec3_chain_find_prev(struct zone* zone, struct domain* domain) return NULL; } + +/** clear hash tree. Called from nsec3_clear_precompile() only. */ +static void +hash_tree_clear(rbtree_type* tree) +{ + if(!tree) return; + + /* Previously (before commit 4ca61188b3f7a0e077476875810d18a5d439871f + * and/or svn commit 4776) prehashes and corresponding rbtree nodes + * were part of struct nsec3_domain_data. Clearing the hash_tree would + * then mean setting the key value of the nodes to NULL to indicate + * absence of the prehash. + * But since prehash structs are separatly allocated, this is no longer + * necessary as currently the prehash structs are simply recycled and + * NULLed. + * + * rbnode_type* n; + * for(n=rbtree_first(tree); n!=RBTREE_NULL; n=rbtree_next(n)) { + * n->key = NULL; + * } + */ + tree->count = 0; + tree->root = RBTREE_NULL; +} + void nsec3_clear_precompile(struct namedb* db, zone_type* zone) { diff --git a/usr.sbin/nsd/options.c b/usr.sbin/nsd/options.c index 3fb67ffdd34..e42af4be234 100644 --- a/usr.sbin/nsd/options.c +++ b/usr.sbin/nsd/options.c @@ -66,6 +66,7 @@ nsd_options_create(region_type* region) opt->version = 0; opt->nsid = 0; opt->logfile = 0; + opt->log_only_syslog = 0; opt->log_time_ascii = 1; opt->round_robin = 0; /* also packet.h::round_robin */ opt->minimal_responses = 1; /* also packet.h::minimal_responses */ @@ -754,6 +755,16 @@ zone_options_create(region_type* region) /* true is booleans are the same truth value */ #define booleq(x,y) ( ((x) && (y)) || (!(x) && !(y)) ) +/* true is min_expire_time_expr has either an equal known value + * or none of these known values but booleanally equal + */ +#define expire_expr_eq(x,y) ( ( (x) == REFRESHPLUSRETRYPLUS1 \ + && (y) == REFRESHPLUSRETRYPLUS1 ) \ + || ( (x) != REFRESHPLUSRETRYPLUS1 \ + && (y) != REFRESHPLUSRETRYPLUS1 \ + && booleq((x), (y)))) + + int acl_equal(struct acl_options* p, struct acl_options* q) { @@ -816,6 +827,8 @@ pattern_options_create(region_type* region) p->max_retry_time_is_default = 1; p->min_retry_time = 0; p->min_retry_time_is_default = 1; + p->min_expire_time = 0; + p->min_expire_time_expr = EXPIRE_TIME_IS_DEFAULT; #ifdef RATELIMIT p->rrl_whitelist = 0; #endif @@ -947,6 +960,8 @@ copy_pat_fixed(region_type* region, struct pattern_options* orig, 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; + orig->min_expire_time = p->min_expire_time; + orig->min_expire_time_expr = p->min_expire_time_expr; #ifdef RATELIMIT orig->rrl_whitelist = p->rrl_whitelist; #endif @@ -1033,6 +1048,9 @@ pattern_options_equal(struct pattern_options* p, struct pattern_options* q) 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; + if(p->min_expire_time != q->min_expire_time) return 0; + if(!expire_expr_eq(p->min_expire_time_expr, + q->min_expire_time_expr)) return 0; #ifdef RATELIMIT if(p->rrl_whitelist != q->rrl_whitelist) return 0; #endif @@ -1197,6 +1215,8 @@ pattern_options_marshal(struct buffer* b, struct pattern_options* p) 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); + marshal_u32(b, p->min_expire_time); + marshal_u8(b, p->min_expire_time_expr); marshal_u8(b, p->multi_master_check); } @@ -1229,6 +1249,8 @@ pattern_options_unmarshal(region_type* r, struct buffer* 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); + p->min_expire_time = unmarshal_u32(b); + p->min_expire_time_expr = unmarshal_u8(b); p->multi_master_check = unmarshal_u8(b); return p; } @@ -1999,6 +2021,10 @@ config_apply_pattern(struct pattern_options *dest, const char* name) dest->min_retry_time = pat->min_retry_time; dest->min_retry_time_is_default = 0; } + if(!expire_time_is_default(pat->min_expire_time_expr)) { + dest->min_expire_time = pat->min_expire_time; + dest->min_expire_time_expr = pat->min_expire_time_expr; + } dest->size_limit_xfr = pat->size_limit_xfr; #ifdef RATELIMIT dest->rrl_whitelist |= pat->rrl_whitelist; diff --git a/usr.sbin/nsd/options.h b/usr.sbin/nsd/options.h index 52605a02009..943620f3b4e 100644 --- a/usr.sbin/nsd/options.h +++ b/usr.sbin/nsd/options.h @@ -78,6 +78,7 @@ struct nsd_options { const char* identity; const char* version; const char* logfile; + int log_only_syslog; int server_count; struct cpu_option* cpu_affinity; struct cpu_map_option* service_cpu_affinity; @@ -192,6 +193,17 @@ struct cpu_map_option { }; /* + * Defines for min_expire_time_expr value + */ +#define EXPIRE_TIME_HAS_VALUE 0 +#define EXPIRE_TIME_IS_DEFAULT 1 +#define REFRESHPLUSRETRYPLUS1 2 +#define REFRESHPLUSRETRYPLUS1_STR "refresh+retry+1" +#define expire_time_is_default(x) (!( (x) == REFRESHPLUSRETRYPLUS1 \ + || (x) == EXPIRE_TIME_HAS_VALUE )) + + +/* * Pattern of zone options, used to contain options for zone(s). */ struct pattern_options { @@ -221,6 +233,12 @@ struct pattern_options { uint8_t max_retry_time_is_default; uint32_t min_retry_time; uint8_t min_retry_time_is_default; + uint32_t min_expire_time; + /* min_expir_time_expr is either a known value (REFRESHPLUSRETRYPLUS1 + * or EXPIRE_EXPR_HAS_VALUE) or else min_expire_time is the default. + * This can be tested with expire_time_is_default(x) define. + */ + uint8_t min_expire_time_expr; uint64_t size_limit_xfr; uint8_t multi_master_check; } ATTR_PACKED; diff --git a/usr.sbin/nsd/query.c b/usr.sbin/nsd/query.c index e0e5cf493b3..56eabd6ab19 100644 --- a/usr.sbin/nsd/query.c +++ b/usr.sbin/nsd/query.c @@ -1530,7 +1530,17 @@ query_process(query_type *q, nsd_type *nsd) * BADVERS is created with Ext. RCODE, followed by RCODE. * Ext. RCODE is set to 1, RCODE must be 0 (getting 0x10 = 16). * Thus RCODE = NOERROR = NSD_RC_OK. */ - return query_error(q, NSD_RC_OK); + RCODE_SET(q->packet, NSD_RC_OK); + buffer_clear(q->packet); + buffer_set_position(q->packet, + QHEADERSZ + 4 + q->qname->name_size); + QR_SET(q->packet); + AD_CLR(q->packet); + QDCOUNT_SET(q->packet, 1); + ANCOUNT_SET(q->packet, 0); + NSCOUNT_SET(q->packet, 0); + ARCOUNT_SET(q->packet, 0); + return QUERY_PROCESSED; } query_prepare_response(q); diff --git a/usr.sbin/nsd/server.c b/usr.sbin/nsd/server.c index 2db997a667e..5849129052b 100644 --- a/usr.sbin/nsd/server.c +++ b/usr.sbin/nsd/server.c @@ -1304,10 +1304,16 @@ server_init(struct nsd *nsd) nsd->tcp = xrealloc(nsd->tcp, ifs * sizeof(*nsd->tcp)); region_add_cleanup(nsd->region, free, nsd->udp); region_add_cleanup(nsd->region, free, nsd->tcp); + if(ifs > nsd->ifs) { + memset(&nsd->udp[nsd->ifs], 0, + (ifs-nsd->ifs)*sizeof(*nsd->udp)); + memset(&nsd->tcp[nsd->ifs], 0, + (ifs-nsd->ifs)*sizeof(*nsd->tcp)); + } for(i = nsd->ifs; i < ifs; i++) { - nsd->udp[i].addr = nsd->udp[i%nsd->ifs].addr; - nsd->udp[i].servers = nsd->udp[i%nsd->ifs].servers; + nsd->udp[i] = nsd->udp[i%nsd->ifs]; + nsd->udp[i].s = -1; if(open_udp_socket(nsd, &nsd->udp[i], &reuseport) == -1) { return -1; } @@ -2713,6 +2719,51 @@ server_process_query_udp(struct nsd *nsd, struct query *query) #endif } +const char* +nsd_event_vs(void) +{ +#ifdef USE_MINI_EVENT + return ""; +#else + return event_get_version(); +#endif +} + +#if !defined(USE_MINI_EVENT) && defined(EV_FEATURE_BACKENDS) +static const char* ub_ev_backend2str(int b) +{ + switch(b) { + case EVBACKEND_SELECT: return "select"; + case EVBACKEND_POLL: return "poll"; + case EVBACKEND_EPOLL: return "epoll"; + case EVBACKEND_KQUEUE: return "kqueue"; + case EVBACKEND_DEVPOLL: return "devpoll"; + case EVBACKEND_PORT: return "evport"; + } + return "unknown"; +} +#endif + +const char* +nsd_event_method(void) +{ +#ifdef USE_MINI_EVENT + return "select"; +#else + struct event_base* b = nsd_child_event_base(); + const char* m = "?"; +# ifdef EV_FEATURE_BACKENDS + m = ub_ev_backend2str(ev_backend((struct ev_loop*)b)); +# elif defined(HAVE_EVENT_BASE_GET_METHOD) + m = event_base_get_method(b); +# endif +# ifdef MEMCLEAN + event_base_free(b); +# endif + return m; +#endif +} + struct event_base* nsd_child_event_base(void) { @@ -3003,7 +3054,7 @@ server_child(struct nsd *nsd) static void remaining_tcp_timeout(int ATTR_UNUSED(fd), short event, void* arg) { int* timed_out = (int*)arg; - assert(event & EV_TIMEOUT); + assert(event & EV_TIMEOUT); (void)event; /* wake up the service tcp thread, note event is no longer * registered */ *timed_out = 1; @@ -3120,15 +3171,12 @@ static int nsd_recvmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags, struct timespec *timeout) { - int orig_errno; unsigned int vpos = 0; ssize_t rcvd; /* timeout is ignored, ensure caller does not expect it to work */ - assert(timeout == NULL); + assert(timeout == NULL); (void)timeout; - orig_errno = errno; - errno = 0; while(vpos < vlen) { rcvd = recvfrom(sockfd, msgvec[vpos].msg_hdr.msg_iov->iov_base, @@ -3149,7 +3197,6 @@ nsd_recvmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, /* error will be picked up next time */ return (int)vpos; } else if(errno == 0) { - errno = orig_errno; return 0; } else if(errno == EAGAIN) { return 0; @@ -3166,12 +3213,9 @@ nsd_recvmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, static int nsd_sendmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags) { - int orig_errno; unsigned int vpos = 0; ssize_t snd; - orig_errno = errno; - errno = 0; while(vpos < vlen) { assert(msgvec[vpos].msg_hdr.msg_iovlen == 1); snd = sendto(sockfd, @@ -3191,7 +3235,6 @@ nsd_sendmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags) if(vpos) { return (int)vpos; } else if(errno == 0) { - errno = orig_errno; return 0; } @@ -3322,13 +3365,36 @@ handle_udp(int fd, short event, void* arg) while(i<recvcount) { sent = nsd_sendmmsg(fd, &msgs[i], recvcount-i, 0); if(sent == -1) { + if(errno == ENOBUFS || +#ifdef EWOULDBLOCK + errno == EWOULDBLOCK || +#endif + errno == EAGAIN) { + /* block to wait until send buffer avail */ + int flag; + if((flag = fcntl(fd, F_GETFL)) == -1) { + log_msg(LOG_ERR, "cannot fcntl F_GETFL: %s", strerror(errno)); + flag = 0; + } + flag &= ~O_NONBLOCK; + if(fcntl(fd, F_SETFL, flag) == -1) + log_msg(LOG_ERR, "cannot fcntl F_SETFL 0: %s", strerror(errno)); + sent = nsd_sendmmsg(fd, &msgs[i], recvcount-i, 0); + flag |= O_NONBLOCK; + if(fcntl(fd, F_SETFL, flag) == -1) + log_msg(LOG_ERR, "cannot fcntl F_SETFL O_NONBLOCK: %s", strerror(errno)); + if(sent != -1) { + i += sent; + continue; + } + } /* don't log transient network full errors, unless * on higher verbosity */ if(!(errno == ENOBUFS && verbosity < 1) && #ifdef EWOULDBLOCK - !(errno == EWOULDBLOCK && verbosity < 1) && + errno != EWOULDBLOCK && #endif - !(errno == EAGAIN && verbosity < 1)) { + errno != EAGAIN) { const char* es = strerror(errno); char a[48]; addr2str(&queries[i]->addr, a, sizeof(a)); diff --git a/usr.sbin/nsd/util.c b/usr.sbin/nsd/util.c index d7bfac69eb1..a7d56fdd459 100644 --- a/usr.sbin/nsd/util.c +++ b/usr.sbin/nsd/util.c @@ -181,6 +181,17 @@ log_syslog(int priority, const char *message) } void +log_only_syslog(int priority, const char *message) +{ +#ifdef HAVE_SYSLOG_H + syslog(priority, "%s", message); +#else /* !HAVE_SYSLOG_H */ + /* no syslog, use stderr */ + log_file(priority, message); +#endif +} + +void log_set_log_function(log_function_type *log_function) { current_log_function = log_function; diff --git a/usr.sbin/nsd/util.h b/usr.sbin/nsd/util.h index 89d4963a846..2e7ccf7bf48 100644 --- a/usr.sbin/nsd/util.h +++ b/usr.sbin/nsd/util.h @@ -75,6 +75,11 @@ log_function_type log_file; log_function_type log_syslog; /* + * The function used to log to syslog only. + */ +log_function_type log_only_syslog; + +/* * Set the logging function to use (log_file or log_syslog). */ void log_set_log_function(log_function_type *log_function); diff --git a/usr.sbin/nsd/xfrd-disk.c b/usr.sbin/nsd/xfrd-disk.c index 180302bbf17..325405d812e 100644 --- a/usr.sbin/nsd/xfrd-disk.c +++ b/usr.sbin/nsd/xfrd-disk.c @@ -311,7 +311,8 @@ xfrd_read_state(struct xfrd_state* xfrd) && zone->soa_nsd.serial == soa_nsd_read.serial) { xfrd_deactivate_zone(zone); zone->state = state; - xfrd_set_timer(zone, timeout); + xfrd_set_timer(zone, + within_refresh_bounds(zone, timeout)); } if((zone->soa_nsd_acquired == 0 && soa_nsd_acquired_read == 0 && soa_disk_acquired_read == 0) || @@ -320,7 +321,8 @@ xfrd_read_state(struct xfrd_state* xfrd) * storm of attempts on some master servers */ xfrd_deactivate_zone(zone); zone->state = state; - xfrd_set_timer(zone, timeout); + xfrd_set_timer(zone, + within_retry_bounds(zone, timeout)); } /* handle as an incoming SOA. */ diff --git a/usr.sbin/nsd/xfrd.c b/usr.sbin/nsd/xfrd.c index 21a37448dff..65d6d955af6 100644 --- a/usr.sbin/nsd/xfrd.c +++ b/usr.sbin/nsd/xfrd.c @@ -40,8 +40,6 @@ #define XFRD_UDP_TIMEOUT 10 /* seconds, before a udp request times out */ #define XFRD_NO_IXFR_CACHE 172800 /* 48h before retrying ixfr's after notimpl */ -#define XFRD_LOWERBOUND_REFRESH 1 /* seconds, smallest refresh timeout */ -#define XFRD_LOWERBOUND_RETRY 1 /* seconds, smallest retry timeout */ #define XFRD_MAX_ROUNDS 1 /* max number of rounds along the masters */ #define XFRD_TSIG_MAX_UNSIGNED 103 /* max number of packets without tsig in a tcp stream. */ /* rfc recommends 100, +3 for offbyone errors/interoperability. */ @@ -761,38 +759,28 @@ xfrd_set_timer_refresh(xfrd_zone_type* zone) { time_t set_refresh; time_t set_expire; - time_t set_min; time_t set; if(zone->soa_disk_acquired == 0 || zone->state != xfrd_zone_ok) { xfrd_set_timer_retry(zone); return; } /* refresh or expire timeout, whichever is earlier */ - set_refresh = ntohl(zone->soa_disk.refresh); - if (set_refresh > (time_t)zone->zone_options->pattern->max_refresh_time) - set_refresh = zone->zone_options->pattern->max_refresh_time; - else if (set_refresh < (time_t)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; - else set = set_expire; - set_min = zone->soa_disk_acquired + XFRD_LOWERBOUND_REFRESH; - if(set < set_min) - set = set_min; - if(set < xfrd_time()) - set = 0; - else set -= xfrd_time(); - if(set > XFRD_TRANSFER_TIMEOUT_MAX) - set = XFRD_TRANSFER_TIMEOUT_MAX; - xfrd_set_timer(zone, set); + set_refresh = bound_soa_disk_refresh(zone); + set_expire = bound_soa_disk_expire(zone); + set = zone->soa_disk_acquired + ( set_refresh < set_expire + ? set_refresh : set_expire ); + + /* set point in time to period for xfrd_set_timer() */ + xfrd_set_timer(zone, within_refresh_bounds(zone, + set > xfrd_time() + ? set - xfrd_time() : XFRD_LOWERBOUND_REFRESH)); } static void xfrd_set_timer_retry(xfrd_zone_type* zone) { time_t set_retry; + time_t set_expire; int mult; /* perform exponential backoff in all the cases */ if(zone->fresh_xfr_timeout == 0) @@ -810,37 +798,36 @@ xfrd_set_timer_retry(xfrd_zone_type* zone) /* set timer for next retry or expire timeout if earlier. */ if(zone->soa_disk_acquired == 0) { - /* if no information, use reasonable timeout */ - xfrd_set_timer(zone, zone->fresh_xfr_timeout - + random_generate(zone->fresh_xfr_timeout)); - } else if(zone->state == xfrd_zone_expired || - xfrd_time() + (time_t)ntohl(zone->soa_disk.retry)*mult < - zone->soa_disk_acquired + (time_t)ntohl(zone->soa_disk.expire)) - { - set_retry = ntohl(zone->soa_disk.retry); - set_retry *= mult; - if(set_retry > XFRD_TRANSFER_TIMEOUT_MAX) - set_retry = XFRD_TRANSFER_TIMEOUT_MAX; - if(set_retry > (time_t)zone->zone_options->pattern->max_retry_time) - set_retry = zone->zone_options->pattern->max_retry_time; - else if(set_retry < (time_t)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; + /* if no information, use reasonable timeout + * within configured and defined bounds + */ + xfrd_set_timer(zone, + within_retry_bounds(zone, zone->fresh_xfr_timeout + + random_generate(zone->fresh_xfr_timeout))); + return; + } + /* exponential backoff within configured and defined bounds */ + set_retry = within_retry_bounds(zone, + ntohl(zone->soa_disk.retry) * mult); + if(zone->state == xfrd_zone_expired) { xfrd_set_timer(zone, set_retry); - } else { - set_retry = ntohl(zone->soa_disk.expire); - if(set_retry > XFRD_TRANSFER_TIMEOUT_MAX) - set_retry = XFRD_TRANSFER_TIMEOUT_MAX; - if(set_retry < XFRD_LOWERBOUND_RETRY) - xfrd_set_timer(zone, XFRD_LOWERBOUND_RETRY); - else { - if(zone->soa_disk_acquired + set_retry < xfrd_time()) - xfrd_set_timer(zone, XFRD_LOWERBOUND_RETRY); - else xfrd_set_timer(zone, zone->soa_disk_acquired + - set_retry - xfrd_time()); - } + return; } + /* retry or expire timeout, whichever is earlier */ + set_expire = zone->soa_disk_acquired + bound_soa_disk_expire(zone); + if(xfrd_time() + set_retry < set_expire) { + xfrd_set_timer(zone, set_retry); + return; + } + /* Not expired, but next retry will be > than expire timeout. + * Retry when the expire timeout runs out. + * set_expire is below retry upper bounds (if statement above), + * but not necessarily above lower bounds, + * so use within_retry_bounds() again. + */ + xfrd_set_timer(zone, within_retry_bounds(zone, + set_expire > xfrd_time() + ? set_expire - xfrd_time() : XFRD_LOWERBOUND_RETRY)); } void @@ -889,13 +876,15 @@ xfrd_handle_zone(int ATTR_UNUSED(fd), short event, void* arg) if(zone->soa_disk_acquired) { if (zone->state != xfrd_zone_expired && - xfrd_time() >= zone->soa_disk_acquired + (time_t)ntohl(zone->soa_disk.expire)) { + xfrd_time() >= zone->soa_disk_acquired + + bound_soa_disk_expire(zone)) { /* zone expired */ log_msg(LOG_ERR, "xfrd: zone %s has expired", zone->apex_str); xfrd_set_zone_state(zone, xfrd_zone_expired); } else if(zone->state == xfrd_zone_ok && - xfrd_time() >= zone->soa_disk_acquired + (time_t)ntohl(zone->soa_disk.refresh)) { + xfrd_time() >= zone->soa_disk_acquired + + bound_soa_disk_refresh(zone)) { /* zone goes to refreshing state. */ DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s is refreshing", zone->apex_str)); xfrd_set_zone_state(zone, xfrd_zone_refreshing); @@ -1152,6 +1141,8 @@ xfrd_set_timer(xfrd_zone_type* zone, time_t t) { int fd = zone->zone_handler.ev_fd; int fl = ((fd == -1)?EV_TIMEOUT:zone->zone_handler_flags); + if(t > XFRD_TRANSFER_TIMEOUT_MAX) + t = XFRD_TRANSFER_TIMEOUT_MAX; /* randomize the time, within 90%-100% of original */ /* not later so zones cannot expire too late */ /* only for times far in the future */ @@ -1180,6 +1171,7 @@ void xfrd_handle_incoming_soa(xfrd_zone_type* zone, xfrd_soa_type* soa, time_t acquired) { + time_t seconds_since_acquired; if(soa == NULL) { /* nsd no longer has a zone in memory */ zone->soa_nsd_acquired = 0; @@ -1201,22 +1193,22 @@ xfrd_handle_incoming_soa(xfrd_zone_type* zone, xfrd->write_zonefile_needed = 1; /* reset exponential backoff, we got a normal timer now */ zone->fresh_xfr_timeout = 0; - if(xfrd_time() - zone->soa_disk_acquired - < (time_t)ntohl(zone->soa_disk.refresh)) + seconds_since_acquired = + xfrd_time() > zone->soa_disk_acquired + ? xfrd_time() - zone->soa_disk_acquired : 0; + if(seconds_since_acquired < bound_soa_disk_refresh(zone)) { /* zone ok, wait for refresh time */ xfrd_set_zone_state(zone, xfrd_zone_ok); zone->round_num = -1; xfrd_set_timer_refresh(zone); - } else if(xfrd_time() - zone->soa_disk_acquired - < (time_t)ntohl(zone->soa_disk.expire)) + } else if(seconds_since_acquired < bound_soa_disk_expire(zone)) { /* zone refreshing */ xfrd_set_zone_state(zone, xfrd_zone_refreshing); xfrd_set_refresh_now(zone); } - if(xfrd_time() - zone->soa_disk_acquired - >= (time_t)ntohl(zone->soa_disk.expire)) { + if(seconds_since_acquired >= bound_soa_disk_expire(zone)) { /* zone expired */ xfrd_set_zone_state(zone, xfrd_zone_expired); xfrd_set_refresh_now(zone); @@ -1950,12 +1942,12 @@ xfrd_parse_received_xfr_packet(xfrd_zone_type* zone, buffer_type* packet, if(zone->soa_disk_acquired != 0 && zone->state != xfrd_zone_expired /* if expired - accept anything */ && compare_serial(ntohl(soa->serial), ntohl(zone->soa_disk.serial)) < 0) { - DEBUG(DEBUG_XFRD,1, (LOG_INFO, - "xfrd: zone %s ignoring old serial from %s", - zone->apex_str, zone->master->ip_address_spec)); - VERBOSITY(1, (LOG_INFO, - "xfrd: zone %s ignoring old serial from %s", - zone->apex_str, zone->master->ip_address_spec)); + DEBUG(DEBUG_XFRD,1, (LOG_INFO, + "xfrd: zone %s ignoring old serial (%u/%u) from %s", + zone->apex_str, ntohl(zone->soa_disk.serial), ntohl(soa->serial), zone->master->ip_address_spec)); + VERBOSITY(1, (LOG_INFO, + "xfrd: zone %s ignoring old serial (%u/%u) from %s", + zone->apex_str, ntohl(zone->soa_disk.serial), ntohl(soa->serial), zone->master->ip_address_spec)); region_destroy(tempregion); return xfrd_packet_bad; } diff --git a/usr.sbin/nsd/xfrd.h b/usr.sbin/nsd/xfrd.h index c58780c7065..4890ce0b53f 100644 --- a/usr.sbin/nsd/xfrd.h +++ b/usr.sbin/nsd/xfrd.h @@ -246,6 +246,85 @@ enum xfrd_packet_result { #define XFRD_TRANSFER_TIMEOUT_START 10 /* empty zone timeout is between x and 2*x seconds */ #define XFRD_TRANSFER_TIMEOUT_MAX 86400 /* empty zone timeout max expbackoff */ +#define XFRD_LOWERBOUND_REFRESH 1 /* seconds, smallest refresh timeout */ +#define XFRD_LOWERBOUND_RETRY 1 /* seconds, smallest retry timeout */ + +/* + * return refresh period + * within configured and defined lower and upper bounds + */ +static inline time_t +within_refresh_bounds(xfrd_zone_type* zone, time_t refresh) +{ + return (time_t)zone->zone_options->pattern->max_refresh_time < refresh + ? (time_t)zone->zone_options->pattern->max_refresh_time + : (time_t)zone->zone_options->pattern->min_refresh_time > refresh + ? (time_t)zone->zone_options->pattern->min_refresh_time + : XFRD_LOWERBOUND_REFRESH > refresh + ? XFRD_LOWERBOUND_REFRESH : refresh; +} + +/* + * return the zone's refresh period (from the on disk stored SOA) + * within configured and defined lower and upper bounds + */ +static inline time_t +bound_soa_disk_refresh(xfrd_zone_type* zone) +{ + return within_refresh_bounds(zone, ntohl(zone->soa_disk.refresh)); +} + +/* + * return retry period + * within configured and defined lower and upper bounds + */ +static inline time_t +within_retry_bounds(xfrd_zone_type* zone, time_t retry) +{ + return (time_t)zone->zone_options->pattern->max_retry_time < retry + ? (time_t)zone->zone_options->pattern->max_retry_time + : (time_t)zone->zone_options->pattern->min_retry_time > retry + ? (time_t)zone->zone_options->pattern->min_retry_time + : XFRD_LOWERBOUND_RETRY > retry + ? XFRD_LOWERBOUND_RETRY : retry; +} + +/* + * return the zone's retry period (from the on disk stored SOA) + * within configured and defined lower and upper bounds + */ +static inline time_t +bound_soa_disk_retry(xfrd_zone_type* zone) +{ + return within_retry_bounds(zone, ntohl(zone->soa_disk.retry)); +} + +/* + * return expire period + * within configured and defined lower bounds + */ +static inline time_t +within_expire_bounds(xfrd_zone_type* zone, time_t expire) +{ + switch (zone->zone_options->pattern->min_expire_time_expr) { + case EXPIRE_TIME_HAS_VALUE: + return (time_t)zone->zone_options->pattern->min_expire_time > expire + ? (time_t)zone->zone_options->pattern->min_expire_time : expire; + + case REFRESHPLUSRETRYPLUS1: + return bound_soa_disk_refresh(zone) + bound_soa_disk_retry(zone) + 1 > expire + ? bound_soa_disk_refresh(zone) + bound_soa_disk_retry(zone) + 1 : expire; + default: + return expire; + } +} + +/* return the zone's expire period (from the on disk stored SOA) */ +static inline time_t +bound_soa_disk_expire(xfrd_zone_type* zone) +{ + return within_expire_bounds(zone, ntohl(zone->soa_disk.expire)); +} extern xfrd_state_type* xfrd; |