Nothing to extravagant, if you want this in 6.5
now would be a good time to test and report back :)

>From the release announcement:

4.1.27
================
FEATURES:
- Deny ANY with only one RR in response, by default.  Patch from
  Daisuke Higashi.  The deny-any statement in nsd.conf sets ANY
  queries over UDP to be further moved to TCP as well.
  Also no additional section processig for type ANY, reducing
  the response size.
- Fix #4215: on-the-fly change of TSIG keys with patch from Igor, adds
  nsd-control print_tsig, update_tsig, add_tsig, assoc_tsig
  and del_tsig.  These changes are gone after reload, edit the
  config file (or a file included from it) to make changes that
  last after restart.
BUG FIXES:
- Fix #4213: disable-ipv6 and dnstap compile error.
- Fix to reduce region_log_stats if condition, this removes a
  debug statement.
- Fix for FreeBSD port with dnstap enabled.
- Fix to remove unused code.
- Fix #6: nsd-control-setup: Change validity time to a shorter
  period (<2038).
- Fix unused definition in header remote.h.
- Fix #4236: IPV4_MINIMAL_RESPONSE_SIZE=1480 is slightly too big.
- Fix #4235: IP_PMTUDISC_OMIT on IPv4/UDP sockets.
- Fixed radtree_insert memory leak.
- Fixed access recycled variable.

diff --git Makefile.in Makefile.in
index fbfc44be33b..0fba1027b88 100644
--- Makefile.in
+++ Makefile.in
@@ -310,7 +310,7 @@ configparser.c configparser.h:      $(srcdir)/configparser.y
        $(YACC) -d -o configparser.c $(srcdir)/configparser.y
 
 # dnstap
-dnstap.o:      $(srcdir)/dnstap/dnstap.c config.h \
+dnstap.o:      $(srcdir)/dnstap/dnstap.c config.h dnstap/dnstap_config.h \
        dnstap/dnstap.pb-c.c dnstap/dnstap.pb-c.h $(srcdir)/dnstap/dnstap.h \
        $(srcdir)/util.h $(srcdir)/options.h $(srcdir)/rbtree.h \
        $(srcdir)/region-allocator.h
@@ -347,6 +347,9 @@ depend:
                        -e 's?$$(srcdir)/configlexer.c?configlexer.c?g' \
                        -e 's?$$(srcdir)/configparser.c?configparser.c?g' \
                        -e 's?$$(srcdir)/configparser.h?configparser.h?g' \
+                       -e 's?$$(srcdir)/dnstap/dnstap_config.h??g' \
+                       -e 
's?$$(srcdir)/dnstap/dnstap.pb-c.c?dnstap/dnstap.pb-c.c?g' \
+                       -e 
's?$$(srcdir)/dnstap/dnstap.pb-c.h?dnstap/dnstap.pb-c.h?g' \
                        -e 's?$$(srcdir)/zlexer.c?zlexer.c?g' \
                        -e 's?$$(srcdir)/zparser.c?zparser.c?g' \
                        -e 's?$$(srcdir)/zparser.h?zparser.h?g' \
@@ -403,7 +406,8 @@ namedb.o: $(srcdir)/namedb.c config.h $(srcdir)/namedb.h 
$(srcdir)/dname.h $(src
  $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/rbtree.h 
$(srcdir)/nsec3.h
 netio.o: $(srcdir)/netio.c config.h $(srcdir)/netio.h 
$(srcdir)/region-allocator.h $(srcdir)/util.h
 nsd.o: $(srcdir)/nsd.c config.h $(srcdir)/nsd.h $(srcdir)/dns.h 
$(srcdir)/edns.h $(srcdir)/buffer.h $(srcdir)/region-allocator.h \
- $(srcdir)/util.h $(srcdir)/options.h $(srcdir)/rbtree.h $(srcdir)/tsig.h 
$(srcdir)/dname.h $(srcdir)/remote.h $(srcdir)/xfrd-disk.h
+ $(srcdir)/util.h $(srcdir)/options.h $(srcdir)/rbtree.h $(srcdir)/tsig.h 
$(srcdir)/dname.h $(srcdir)/remote.h $(srcdir)/xfrd-disk.h \
+ $(srcdir)/dnstap/dnstap_collector.h
 nsd-checkconf.o: $(srcdir)/nsd-checkconf.c config.h $(srcdir)/tsig.h 
$(srcdir)/buffer.h \
  $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/dname.h 
$(srcdir)/options.h $(srcdir)/rbtree.h $(srcdir)/rrl.h $(srcdir)/query.h \
  $(srcdir)/namedb.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/nsd.h 
$(srcdir)/edns.h $(srcdir)/packet.h
@@ -442,7 +446,8 @@ rrl.o: $(srcdir)/rrl.c config.h $(srcdir)/rrl.h 
$(srcdir)/query.h $(srcdir)/name
 server.o: $(srcdir)/server.c config.h $(srcdir)/axfr.h $(srcdir)/nsd.h 
$(srcdir)/dns.h $(srcdir)/edns.h $(srcdir)/buffer.h \
  $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/query.h 
$(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/radtree.h $(srcdir)/rbtree.h \
  $(srcdir)/packet.h $(srcdir)/tsig.h $(srcdir)/netio.h $(srcdir)/xfrd.h 
$(srcdir)/options.h $(srcdir)/xfrd-tcp.h $(srcdir)/xfrd-disk.h \
- $(srcdir)/difffile.h $(srcdir)/udb.h $(srcdir)/nsec3.h $(srcdir)/ipc.h 
$(srcdir)/remote.h $(srcdir)/lookup3.h $(srcdir)/rrl.h
+ $(srcdir)/difffile.h $(srcdir)/udb.h $(srcdir)/nsec3.h $(srcdir)/ipc.h 
$(srcdir)/remote.h $(srcdir)/lookup3.h $(srcdir)/rrl.h \
+ $(srcdir)/dnstap/dnstap_collector.h
 tsig.o: $(srcdir)/tsig.c config.h $(srcdir)/tsig.h $(srcdir)/buffer.h 
$(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/dname.h \
  $(srcdir)/tsig-openssl.h $(srcdir)/dns.h $(srcdir)/packet.h 
$(srcdir)/namedb.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/query.h 
$(srcdir)/nsd.h \
  $(srcdir)/edns.h
@@ -458,7 +463,7 @@ util.o: $(srcdir)/util.c config.h $(srcdir)/util.h 
$(srcdir)/region-allocator.h
 xfrd.o: $(srcdir)/xfrd.c config.h $(srcdir)/xfrd.h $(srcdir)/rbtree.h 
$(srcdir)/region-allocator.h $(srcdir)/namedb.h \
  $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/util.h $(srcdir)/dns.h 
$(srcdir)/radtree.h $(srcdir)/options.h $(srcdir)/tsig.h $(srcdir)/xfrd-tcp.h \
  $(srcdir)/xfrd-disk.h $(srcdir)/xfrd-notify.h $(srcdir)/netio.h 
$(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/packet.h $(srcdir)/rdata.h \
- $(srcdir)/difffile.h $(srcdir)/udb.h $(srcdir)/ipc.h $(srcdir)/remote.h 
$(srcdir)/rrl.h $(srcdir)/query.h
+ $(srcdir)/difffile.h $(srcdir)/udb.h $(srcdir)/ipc.h $(srcdir)/remote.h 
$(srcdir)/rrl.h $(srcdir)/query.h $(srcdir)/dnstap/dnstap_collector.h
 xfrd-disk.o: $(srcdir)/xfrd-disk.c config.h $(srcdir)/xfrd-disk.h 
$(srcdir)/xfrd.h $(srcdir)/rbtree.h \
  $(srcdir)/region-allocator.h $(srcdir)/namedb.h $(srcdir)/dname.h 
$(srcdir)/buffer.h $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/radtree.h \
  $(srcdir)/options.h $(srcdir)/tsig.h $(srcdir)/nsd.h $(srcdir)/edns.h
diff --git config.h.in config.h.in
index 67296ca99b7..75c96b60b81 100644
--- config.h.in
+++ config.h.in
@@ -178,6 +178,9 @@
 /* Define to 1 if you have the `localtime_r' function. */
 #undef HAVE_LOCALTIME_R
 
+/* Define to 1 if you have the <login_cap.h> header file. */
+#undef HAVE_LOGIN_CAP_H
+
 /* Define to 1 if your system has a GNU libc compatible `malloc' function, and
    to 0 otherwise. */
 #undef HAVE_MALLOC
diff --git configure configure
index 47736231022..88a1f1d8af5 100644
--- configure
+++ 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.1.26.
+# Generated by GNU Autoconf 2.69 for NSD 4.1.27.
 #
 # Report bugs to <[email protected]>.
 #
@@ -580,8 +580,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='NSD'
 PACKAGE_TARNAME='nsd'
-PACKAGE_VERSION='4.1.26'
-PACKAGE_STRING='NSD 4.1.26'
+PACKAGE_VERSION='4.1.27'
+PACKAGE_STRING='NSD 4.1.27'
 PACKAGE_BUGREPORT='[email protected]'
 PACKAGE_URL=''
 
@@ -1296,7 +1296,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.1.26 to adapt to many kinds of systems.
+\`configure' configures NSD 4.1.27 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1357,7 +1357,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of NSD 4.1.26:";;
+     short | recursive ) echo "Configuration of NSD 4.1.27:";;
    esac
   cat <<\_ACEOF
 
@@ -1512,7 +1512,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-NSD configure 4.1.26
+NSD configure 4.1.27
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2221,7 +2221,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.1.26, which was
+It was created by NSD $as_me 4.1.27, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -8025,6 +8025,73 @@ _ACEOF
 fi
 done
 
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing 
setusercontext" >&5
+$as_echo_n "checking for library containing setusercontext... " >&6; }
+if ${ac_cv_search_setusercontext+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char setusercontext ();
+int
+main ()
+{
+return setusercontext ();
+  ;
+  return 0;
+}
+_ACEOF
+for ac_lib in '' util; do
+  if test -z "$ac_lib"; then
+    ac_res="none required"
+  else
+    ac_res=-l$ac_lib
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+  fi
+  if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_search_setusercontext=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext
+  if ${ac_cv_search_setusercontext+:} false; then :
+  break
+fi
+done
+if ${ac_cv_search_setusercontext+:} false; then :
+
+else
+  ac_cv_search_setusercontext=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_setusercontext" 
>&5
+$as_echo "$ac_cv_search_setusercontext" >&6; }
+ac_res=$ac_cv_search_setusercontext
+if test "$ac_res" != no; then :
+  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+  for ac_header in login_cap.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "login_cap.h" 
"ac_cv_header_login_cap_h" "$ac_includes_default"
+if test "x$ac_cv_header_login_cap_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LOGIN_CAP_H 1
+_ACEOF
+
+fi
+
+done
+
+fi
+
 for ac_func in tzset alarm chroot dup2 endpwent gethostname memset memcpy 
pwrite socket strcasecmp strchr strdup strerror strncasecmp strtol writev 
getaddrinfo getnameinfo freeaddrinfo gai_strerror sigaction sigprocmask 
strptime strftime localtime_r setusercontext glob initgroups setresuid setreuid 
setresgid setregid getpwnam mmap ppoll clock_gettime accept4
 do :
   as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
@@ -10127,7 +10194,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.1.26, which was
+This file was extended by NSD $as_me 4.1.27, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -10189,7 +10256,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.1.26
+NSD config.status 4.1.27
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
diff --git configure.ac configure.ac
index 22e3e21e2ae..f2e384c8443 100644
--- configure.ac
+++ configure.ac
@@ -5,7 +5,7 @@ dnl
 sinclude(acx_nlnetlabs.m4)
 sinclude(dnstap/dnstap.m4)
 
-AC_INIT(NSD,4.1.26,[email protected])
+AC_INIT(NSD,4.1.27,[email protected])
 AC_CONFIG_HEADER([config.h])
 
 CFLAGS="$CFLAGS"
@@ -627,6 +627,7 @@ AC_SYS_LARGEFILE
 AC_CHECK_SIZEOF(void*)
 AC_CHECK_SIZEOF(off_t)
 AC_CHECK_FUNCS([arc4random arc4random_uniform])
+AC_SEARCH_LIBS([setusercontext],[util],[AC_CHECK_HEADERS([login_cap.h])])
 AC_CHECK_FUNCS([tzset alarm chroot dup2 endpwent gethostname memset memcpy 
pwrite socket strcasecmp strchr strdup strerror strncasecmp strtol writev 
getaddrinfo getnameinfo freeaddrinfo gai_strerror sigaction sigprocmask 
strptime strftime localtime_r setusercontext glob initgroups setresuid setreuid 
setresgid setregid getpwnam mmap ppoll clock_gettime accept4])
 
 AC_ARG_ENABLE(recvmmsg, AC_HELP_STRING([--enable-recvmmsg], [Enable recvmmsg 
and sendmmsg compilation, faster but some kernel versions may have 
implementation problems for IPv6]))
diff --git difffile.c difffile.c
index 4182db6bcff..b3aee0b4530 100644
--- difffile.c
+++ difffile.c
@@ -1381,6 +1381,7 @@ apply_ixfr_for_zone(nsd_type* nsd, zone_type* zonedb, 
FILE* in,
 #endif /* NSEC3 */
                zonedb->is_changed = 1;
                if(nsd->db->udb) {
+                       assert(z.base);
                        ZONE(&z)->is_changed = 1;
                        ZONE(&z)->mtime = time_end_0;
                        ZONE(&z)->mtime_nsec = time_end_1*1000;
diff --git dnstap/dnstap.c dnstap/dnstap.c
index fb724a8fc7c..8286441c2d8 100644
--- dnstap/dnstap.c
+++ dnstap/dnstap.c
@@ -319,7 +319,7 @@ dt_msg_fill_net(struct dt_msg *dm,
                *has_port = 1;
        } else if (ss->ss_family == AF_INET) {
 #else
-       if (ss->ss_family == AF_INET) {
+       if (ss->sin_family == AF_INET) {
 #endif /* INET6 */
                struct sockaddr_in *s = (struct sockaddr_in *) ss;
 
diff --git namedb.c namedb.c
index 4528b6e4f15..9ca5ffcbebb 100644
--- namedb.c
+++ namedb.c
@@ -278,11 +278,14 @@ do_deldomain(namedb_type* db, domain_type* domain)
 void
 domain_table_deldomain(namedb_type* db, domain_type* domain)
 {
+       domain_type* parent;
+
        while(domain_can_be_deleted(domain)) {
+               parent = domain->parent;
                /* delete it */
                do_deldomain(db, domain);
                /* test parent */
-               domain = domain->parent;
+               domain = parent;
        }
 }
 
diff --git nsd-checkconf.8.in nsd-checkconf.8.in
index dccf95d690d..18d669750e5 100644
--- nsd-checkconf.8.in
+++ nsd-checkconf.8.in
@@ -1,4 +1,4 @@
-.TH "nsd\-checkconf" "8" "Dec  4, 2018" "NLnet Labs" "nsd 4.1.26"
+.TH "nsd\-checkconf" "8" "Mar 25, 2019" "NLnet Labs" "nsd 4.1.27"
 .\" Copyright (c) 2001\-2008, NLnet Labs. All rights reserved.
 .\" See LICENSE for the license.
 .SH "NAME"
diff --git nsd-checkzone.8.in nsd-checkzone.8.in
index afadb4c9215..b6e2499b1a6 100644
--- nsd-checkzone.8.in
+++ nsd-checkzone.8.in
@@ -1,4 +1,4 @@
-.TH "nsd\-checkzone" "8" "Dec  4, 2018" "NLnet Labs" "nsd 4.1.26"
+.TH "nsd\-checkzone" "8" "Mar 25, 2019" "NLnet Labs" "nsd 4.1.27"
 .\" Copyright (c) 2014, NLnet Labs. All rights reserved.
 .\" See LICENSE for the license.
 .SH "NAME"
diff --git nsd-control-setup.sh.in nsd-control-setup.sh.in
index b39c11ce722..705caefbe84 100644
--- nsd-control-setup.sh.in
+++ nsd-control-setup.sh.in
@@ -43,7 +43,7 @@ SERVERNAME=nsd
 CLIENTNAME=nsd-control
 
 # validity period for certificates
-DAYS=7200
+DAYS=3650
 
 # size of keys in bits
 BITS=3072
diff --git nsd-control.8.in nsd-control.8.in
index 40ca171c2c4..b6fe680b89b 100644
--- nsd-control.8.in
+++ nsd-control.8.in
@@ -1,4 +1,4 @@
-.TH "nsd\-control" "8" "Dec  4, 2018" "NLnet Labs" "nsd 4.1.26"
+.TH "nsd\-control" "8" "Mar 25, 2019" "NLnet Labs" "nsd 4.1.27"
 .\" Copyright (c) 2011, NLnet Labs. All rights reserved.
 .\" See LICENSE for the license.
 .SH "NAME"
@@ -152,6 +152,31 @@ is also stable.
 .TP
 .B verbosity <number>
 Change logging verbosity.
+.TP
+.B print_tsig [<key_name>]
+print the secret and algorithm for the TSIG key with that name.
+Or list all the tsig keys with their name, secret and algorithm.
+.TP
+.B update_tsig <name> <secret>
+Change existing TSIG key with name to the new secret.  The secret is
+a base64 encoded string.  The changes are only in-memory and are gone next
+restart, for lasting changes edit the nsd.conf file or a file included from it.
+.TP
+.B add_tsig <name> <secret> [algo]
+Add a new TSIG key with the given name, secret and algorithm.  Without
+algorithm a default (hmac-sha256) algorithm is used.  The secret is a
+base64 encoded string.  The changes are only in-memory and are gone next
+restart, for lasting changes edit the nsd.conf file or a file included from it.
+.TP
+.B assoc_tsig <zone> <key_name>
+Associate the zone with the given tsig.  The access control lists for
+notify, allow-notify, provide-xfr and request-xfr are adjusted to use the
+given key.
+.TP
+.B del_tsig <key_name>
+Delete the TSIG key with the given name.  Prints error if the key is still
+in use by some zone.  The changes are only in-memory and are gone next
+restart, for lasting changes edit the nsd.conf file or a file included from it.
 .SH "EXIT CODE"
 The nsd\-control program exits with status code 1 on error, 0 on success.
 .SH "SET UP"
diff --git nsd-control.c nsd-control.c
index b83fc0c2fe3..efac9ac530d 100644
--- nsd-control.c
+++ nsd-control.c
@@ -101,6 +101,11 @@ usage()
        printf("  zonestatus [<zone>]           print state, serial, 
activity\n");
        printf("  serverpid                     get pid of server process\n");
        printf("  verbosity <number>            change logging detail\n");
+       printf("  print_tsig [<key_name>]       print tsig with <name> the 
secret and algo\n");
+       printf("  update_tsig <name> <secret>   change existing tsig with 
<name> to a new <secret>\n");
+       printf("  add_tsig <name> <secret> [algo] add new key with the given 
parameters\n");
+       printf("  assoc_tsig <zone> <key_name>  associate <zone> with given 
tsig <key_name> name\n");
+       printf("  del_tsig <key_name>           delete tsig <key_name> from 
configuration\n");
        exit(1);
 }
 
diff --git nsd.8.in nsd.8.in
index afb19cbfe66..1d58c5ad877 100644
--- nsd.8.in
+++ nsd.8.in
@@ -1,9 +1,9 @@
-.TH "NSD" "8" "Dec  4, 2018" "NLnet Labs" "NSD 4.1.26"
+.TH "NSD" "8" "Mar 25, 2019" "NLnet Labs" "NSD 4.1.27"
 .\" Copyright (c) 2001\-2008, NLnet Labs. All rights reserved.
 .\" See LICENSE for the license.
 .SH "NAME"
 .B nsd
-\- Name Server Daemon (NSD) version 4.1.26.
+\- Name Server Daemon (NSD) version 4.1.27.
 .SH "SYNOPSIS"
 .B nsd
 .RB [ \-4 ] 
diff --git nsd.c nsd.c
index 57bae3232d7..c773fc99d34 100644
--- nsd.c
+++ nsd.c
@@ -21,7 +21,9 @@
 #include <grp.h>
 #endif /* HAVE_GRP_H */
 #ifdef HAVE_SETUSERCONTEXT
+#ifdef HAVE_LOGIN_CAP_H
 #include <login_cap.h>
+#endif /* HAVE_LOGIN_CAP_H */
 #endif /* HAVE_SETUSERCONTEXT */
 
 #include <assert.h>
@@ -952,7 +954,7 @@ main(int argc, char *argv[])
                int fd;
 
                /* Take off... */
-               switch ((nsd.pid = fork())) {
+               switch (fork()) {
                case 0:
                        /* Child */
                        break;
diff --git nsd.conf.5.in nsd.conf.5.in
index 348fcda8fe3..a9c10213149 100644
--- nsd.conf.5.in
+++ nsd.conf.5.in
@@ -1,4 +1,4 @@
-.TH "nsd.conf" "5" "Dec  4, 2018" "NLnet Labs" "nsd 4.1.26"
+.TH "nsd.conf" "5" "Mar 25, 2019" "NLnet Labs" "nsd 4.1.27"
 .\" Copyright (c) 2001\-2008, NLnet Labs. All rights reserved.
 .\" See LICENSE for the license.
 .SH "NAME"
diff --git options.h options.h
index a83eb383e47..1d30156bbbe 100644
--- options.h
+++ options.h
@@ -337,6 +337,8 @@ struct key_options* key_options_find(struct nsd_options* 
opt, const char* name);
 void key_options_remove(struct nsd_options* opt, const char* name);
 int key_options_equal(struct key_options* p, struct key_options* q);
 void key_options_add_modify(struct nsd_options* opt, struct key_options* key);
+void key_options_setup(region_type* region, struct key_options* key);
+void key_options_desetup(region_type* region, struct key_options* key);
 /* read in zone list file. Returns false on failure */
 int parse_zone_list_file(struct nsd_options* opt);
 /* create zone entry and add to the zonelist file */
diff --git packet.h packet.h
index 8540dcfdf6a..097e9660f31 100644
--- packet.h
+++ packet.h
@@ -140,7 +140,7 @@ struct query;
 #define        MAXRRSPP                10240    /* Maximum number of rr's per 
packet */
 #define MAX_COMPRESSED_DNAMES  MAXRRSPP /* Maximum number of compressed 
domains. */
 #define MAX_COMPRESSION_OFFSET  16383   /* Compression pointers are 14 bit. */
-#define IPV4_MINIMAL_RESPONSE_SIZE 1480         /* Recommended minimal edns 
size for IPv4 */
+#define IPV4_MINIMAL_RESPONSE_SIZE 1460         /* Recommended minimal edns 
size for IPv4 */
 #define IPV6_MINIMAL_RESPONSE_SIZE 1220         /* Recommended minimal edns 
size for IPv6 */
 
 /* use round robin rotation */
diff --git query.c query.c
index 48261d87404..e2a56e9aea0 100644
--- query.c
+++ query.c
@@ -693,7 +693,8 @@ answer_needs_ns(struct query* query)
        assert(query);
        /* Currently, only troublesome for DNSKEY and DS,
          * cuz their RRSETs are quite large. */
-       return (query->qtype != TYPE_DNSKEY && query->qtype != TYPE_DS);
+       return (query->qtype != TYPE_DNSKEY && query->qtype != TYPE_DS
+               && query->qtype != TYPE_ANY);
 }
 
 static int
@@ -969,6 +970,9 @@ answer_domain(struct nsd* nsd, struct query *q, answer_type 
*answer,
                        {
                                add_rrset(q, answer, ANSWER_SECTION, domain, 
rrset);
                                ++added;
+                               /* minimize response size with one RR,
+                                * according to RFC 8482(4.1). */
+                               break;
                        }
                }
                if (added == 0) {
@@ -1182,8 +1186,10 @@ answer_authoritative(struct nsd   *nsd,
                         * No match and no wildcard.  Include NSEC
                         * proving there is no wildcard.
                         */
-                       nsec_domain = 
find_covering_nsec(closest_encloser->wildcard_child_closest_match, q->zone, 
&nsec_rrset);
-                       if (nsec_domain) {
+                       if(closest_encloser && (nsec_domain =
+                               find_covering_nsec(closest_encloser->
+                                       wildcard_child_closest_match, q->zone,
+                                       &nsec_rrset)) != NULL) {
                                add_rrset(q, answer, AUTHORITY_SECTION, 
nsec_domain, nsec_rrset);
                        }
                }
diff --git radtree.c radtree.c
index c3ac8661a73..873f0f8eba5 100644
--- radtree.c
+++ radtree.c
@@ -510,7 +510,10 @@ struct radnode* radix_insert(struct radtree* rt, uint8_t* 
k,
                        /* add a root to point to new node */
                        n = (struct radnode*)region_alloc_zero(rt->region,
                                sizeof(*n));
-                       if(!n) return NULL;
+                       if(!n) {
+                               region_recycle(rt->region, add, sizeof(*add));
+                               return NULL;
+                       }
                        if(!radnode_array_space(rt->region, n, k[0])) {
                                region_recycle(rt->region, n->array,
                                        n->capacity*sizeof(struct radsel));
diff --git region-allocator.c region-allocator.c
index 8b5d2c77989..638c861bdb3 100644
--- region-allocator.c
+++ region-allocator.c
@@ -491,7 +491,7 @@ region_dump_stats(region_type *region, FILE *out)
                (unsigned long) region->chunk_count,
                (unsigned long) region->cleanup_count,
                (unsigned long) region->recycle_size);
-       if(1 && region->recycle_bin) {
+       if(region->recycle_bin) {
                /* print details of the recycle bin */
                size_t i;
                for(i=0; i<region->large_object_size; i++) {
@@ -541,7 +541,7 @@ region_log_stats(region_type *region)
        len = strlen(str);
        str+=len;
        strl-=len;
-       if(1 && region->recycle_bin) {
+       if(region->recycle_bin) {
                /* print details of the recycle bin */
                size_t i;
                for(i=0; i<region->large_object_size; i++) {
diff --git remote.c remote.c
index 5cc36489d37..147535f1a23 100644
--- remote.c
+++ remote.c
@@ -743,12 +743,22 @@ state_list_remove_elem(struct rc_state** list, struct 
rc_state* todel)
 static void
 stats_list_remove_elem(struct rc_state** list, struct rc_state* todel)
 {
-       while(*list) {
-               if( (*list) == todel) {
-                       *list = (*list)->stats_next;
-                       return;
+       struct rc_state* prev = NULL;
+       struct rc_state* n = *list;
+       while(n) {
+               /* delete this one? */
+               if(n == todel) {
+                       if(prev) prev->next = n->next;
+                       else    (*list) = n->next;
+                       /* go on and delete further elements */
+                       /* prev = prev; */
+                       n = n->next;
+                       continue;
                }
-               list = &(*list)->stats_next;
+
+               /* go to the next element */
+               prev = n;
+               n = n->next;
        }
 }
 
@@ -1191,11 +1201,35 @@ find_arg2(RES* ssl, char* arg, char** arg2)
                as[0]=0;
                return 1;
        }
+       *arg2 = NULL;
        ssl_printf(ssl, "error could not find next argument "
                "after %s\n", arg);
        return 0;
 }
 
+/** find second and third arguments, modifies string,
+ * does not print error for missing arg3 so that if it does not find an
+ * arg3, the caller can use two arguments. */
+static int
+find_arg3(RES* ssl, char* arg, char** arg2, char** arg3)
+{
+       if(find_arg2(ssl, arg, arg2)) {
+               char* as;
+               *arg3 = *arg2;
+               as = strrchr(arg, ' ');
+               if(as) {
+                       as[0]=0;
+                       *arg2 = as+1;
+                       while(isspace((unsigned char)*as) && as > arg)
+                               as--;
+                       as[0]=0;
+                       return 1;
+               }
+       }
+       *arg3 = NULL;
+       return 0;
+}
+
 /** do the status command */
 static void
 do_status(RES* ssl, xfrd_state_type* xfrd)
@@ -1930,6 +1964,275 @@ do_serverpid(RES* ssl, xfrd_state_type* xfrd)
        (void)ssl_printf(ssl, "%u\n", (unsigned)xfrd->reload_pid);
 }
 
+/** do the print_tsig command: printout tsig info */
+static void
+do_print_tsig(RES* ssl, xfrd_state_type* xfrd, char* arg)
+{
+       if(*arg == '\0') {
+               struct key_options* key;
+               RBTREE_FOR(key, struct key_options*, xfrd->nsd->options->keys) {
+                       if(!ssl_printf(ssl, "key: name: \"%s\" secret: \"%s\" 
algorithm: %s\n", key->name, key->secret, key->algorithm))
+                               return;
+               }
+               return;
+       } else {
+               struct key_options* key_opts = 
key_options_find(xfrd->nsd->options, arg);
+               if(!key_opts) {
+                       if(!ssl_printf(ssl, "error: no such key with name: 
%s\n", arg))
+                               return;
+                       return;
+               } else {
+                       if(!ssl_printf(ssl, "key: name: \"%s\" secret: \"%s\" 
algorithm: %s\n", arg, key_opts->secret, key_opts->algorithm))
+                               return;
+               }
+       }
+}
+
+/** do the update_tsig command: change existing tsig to new secret */
+static void
+do_update_tsig(RES* ssl, xfrd_state_type* xfrd, char* arg)
+{
+       struct region* region = xfrd->nsd->options->region;
+       char* arg2 = NULL;
+       uint8_t data[65536]; /* 64K */
+       struct key_options* key_opt;
+
+       if(*arg == '\0') {
+               if(!ssl_printf(ssl, "error: missing argument (keyname)\n"))
+                       return;
+               return;
+       }
+       if(!find_arg2(ssl, arg, &arg2)) {
+               if(!ssl_printf(ssl, "error: missing argument (secret)\n"))
+                       return;
+               return;
+       }
+       key_opt = key_options_find(xfrd->nsd->options, arg);
+       if(!key_opt) {
+               if(!ssl_printf(ssl, "error: no such key with name: %s\n", arg))
+                       return;
+               memset(arg2, 0xdd, strlen(arg2));
+               return;
+       }
+       if(__b64_pton(arg2, data, sizeof(data)) == -1) {
+               if(!ssl_printf(ssl, "error: the secret: %s is not in b64 
format\n", arg2))
+                       return;
+               memset(data, 0xdd, sizeof(data)); /* wipe secret */
+               memset(arg2, 0xdd, strlen(arg2));
+               return;
+       }
+       log_msg(LOG_INFO, "changing secret provided with the key: %s with old 
secret %s and algo: %s\n", arg, key_opt->secret, key_opt->algorithm);
+       if(key_opt->secret) {
+               /* wipe old secret */
+               memset(key_opt->secret, 0xdd, strlen(key_opt->secret));
+               region_recycle(region, key_opt->secret,
+                       strlen(key_opt->secret)+1);
+       }
+       key_opt->secret = region_strdup(region, arg2);
+       log_msg(LOG_INFO, "the key: %s has new secret %s and algorithm: %s\n", 
arg, key_opt->secret, key_opt->algorithm);
+       /* wipe secret from temp parse buffer */
+       memset(arg2, 0xdd, strlen(arg2));
+       memset(data, 0xdd, sizeof(data));
+
+       key_options_desetup(region, key_opt);
+       key_options_setup(region, key_opt);
+       task_new_add_key(xfrd->nsd->task[xfrd->nsd->mytask], xfrd->last_task,
+               key_opt);
+       xfrd_set_reload_now(xfrd);
+
+       send_ok(ssl);
+}
+
+/** do the add tsig command, add new key with name, secret and algo given */
+static void
+do_add_tsig(RES* ssl, xfrd_state_type* xfrd, char* arg)
+{
+       char* arg2 = NULL;
+       char* arg3 = NULL;
+       uint8_t data[65536]; /* 64KB */
+       uint8_t dname[MAXDOMAINLEN+1];
+       char algo[256];
+       region_type* region = xfrd->nsd->options->region;
+       struct key_options* new_key_opt;
+
+       if(*arg == '\0') {
+               if(!ssl_printf(ssl, "error: missing argument (keyname)\n"))
+                       return;
+               return;
+       }
+       if(!find_arg3(ssl, arg, &arg2, &arg3)) {
+               strlcpy(algo, "hmac-sha256", sizeof(algo));
+       } else {
+               strlcpy(algo, arg3, sizeof(algo));
+       }
+       if(!arg2) {
+               if(!ssl_printf(ssl, "error: missing argument (secret)\n"))
+                       return;
+               return;
+       }
+       if(key_options_find(xfrd->nsd->options, arg)) {
+               if(!ssl_printf(ssl, "error: key %s already exists\n", arg))
+                       return;
+               memset(arg2, 0xdd, strlen(arg2));
+               return;
+       }
+       if(__b64_pton(arg2, data, sizeof(data)) == -1) {
+               if(!ssl_printf(ssl, "error: the secret: %s is not in b64 
format\n", arg2))
+                       return;
+               memset(data, 0xdd, sizeof(data)); /* wipe secret */
+               memset(arg2, 0xdd, strlen(arg2));
+               return;
+       }
+       memset(data, 0xdd, sizeof(data)); /* wipe secret from temp buffer */
+       if(!dname_parse_wire(dname, arg)) {
+               if(!ssl_printf(ssl, "error: could not parse key name: %s\n", 
arg))
+                       return;
+               memset(arg2, 0xdd, strlen(arg2));
+               return;
+       }
+       if(tsig_get_algorithm_by_name(algo) == NULL) {
+               if(!ssl_printf(ssl, "error: unknown algorithm: %s\n", algo))
+                       return;
+               memset(arg2, 0xdd, strlen(arg2));
+               return;
+       }
+       log_msg(LOG_INFO, "adding key with name: %s and secret: %s with algo: 
%s\n", arg, arg2, algo);
+       new_key_opt = key_options_create(region);
+       new_key_opt->name = region_strdup(region, arg);
+       new_key_opt->secret = region_strdup(region, arg2);
+       new_key_opt->algorithm = region_strdup(region, algo);
+       add_key(xfrd, new_key_opt);
+
+       /* wipe secret from temp buffer */
+       memset(arg2, 0xdd, strlen(arg2));
+       send_ok(ssl);
+}
+
+/** set acl entries to use the given TSIG key */
+static void
+zopt_set_acl_to_tsig(struct acl_options* acl, struct region* region,
+       const char* key_name, struct key_options* key_opt)
+{
+       while(acl) {
+               if(acl->blocked) {
+                       acl = acl->next;
+                       continue;
+               }
+               acl->nokey = 0;
+               if(acl->key_name)
+                       region_recycle(region, (void*)acl->key_name,
+                               strlen(acl->key_name)+1);
+               acl->key_name = region_strdup(region, key_name);
+               acl->key_options = key_opt;
+               acl = acl->next;
+       }
+}
+
+/** do the assoc_tsig command: associate the zone to use the tsig name */
+static void
+do_assoc_tsig(RES* ssl, xfrd_state_type* xfrd, char* arg)
+{
+       region_type* region = xfrd->nsd->options->region;
+       char* arg2 = NULL;
+       struct zone_options* zone;
+       struct key_options* key_opt;
+
+       if(*arg == '\0') {
+               if(!ssl_printf(ssl, "error: missing argument (zonename)\n"))
+                       return;
+               return;
+       }
+       if(!find_arg2(ssl, arg, &arg2)) {
+               if(!ssl_printf(ssl, "error: missing argument (keyname)\n"))
+                       return;
+               return;
+       }
+
+       if(!get_zone_arg(ssl, xfrd, arg, &zone))
+               return;
+       if(!zone) {
+               if(!ssl_printf(ssl, "error: missing argument (zone)\n"))
+                       return;
+               return;
+       }
+       key_opt = key_options_find(xfrd->nsd->options, arg2);
+       if(!key_opt) {
+               if(!ssl_printf(ssl, "error: key: %s does not exist\n", arg2))
+                       return;
+               return;
+       }
+
+       zopt_set_acl_to_tsig(zone->pattern->allow_notify, region, arg2,
+               key_opt);
+       zopt_set_acl_to_tsig(zone->pattern->notify, region, arg2, key_opt);
+       zopt_set_acl_to_tsig(zone->pattern->request_xfr, region, arg2,
+               key_opt);
+       zopt_set_acl_to_tsig(zone->pattern->provide_xfr, region, arg2,
+               key_opt);
+
+       task_new_add_pattern(xfrd->nsd->task[xfrd->nsd->mytask],
+               xfrd->last_task, zone->pattern);
+       xfrd_set_reload_now(xfrd);
+
+       send_ok(ssl);
+}
+
+/** see if TSIG key is used in the acl */
+static int
+acl_contains_tsig_key(struct acl_options* acl, const char* name)
+{
+       while(acl) {
+               if(acl->key_name && strcmp(acl->key_name, name) == 0)
+                       return 1;
+               acl = acl->next;
+       }
+       return 0;
+}
+
+/** do the del_tsig command, remove an (unused) tsig */
+static void
+do_del_tsig(RES* ssl, xfrd_state_type* xfrd, char* arg) {
+       int used_key = 0;
+       struct zone_options* zone;
+       struct key_options* key_opt;
+
+       if(*arg == '\0') {
+               if(!ssl_printf(ssl, "error: missing argument (keyname)\n"))
+                       return;
+               return;
+       }
+       key_opt = key_options_find(xfrd->nsd->options, arg);
+       if(!key_opt) {
+               if(!ssl_printf(ssl, "key %s does not exist, nothing to be 
deleted\n", arg))
+                       return;
+               return;
+       }
+       RBTREE_FOR(zone, struct zone_options*, xfrd->nsd->options->zone_options)
+       {
+               if(acl_contains_tsig_key(zone->pattern->allow_notify, arg) ||
+                  acl_contains_tsig_key(zone->pattern->notify, arg) ||
+                  acl_contains_tsig_key(zone->pattern->request_xfr, arg) ||
+                  acl_contains_tsig_key(zone->pattern->provide_xfr, arg)) {
+                       if(!ssl_printf(ssl, "zone %s uses key %s\n",
+                               zone->name, arg))
+                               return;
+                       used_key = 1;
+                       break;
+               }
+       }
+
+       if(used_key) {
+               if(!ssl_printf(ssl, "error: key: %s is in use and cannot be 
deleted\n", arg))
+                       return;
+               return;
+       } else {
+               remove_key(xfrd, arg);
+               log_msg(LOG_INFO, "key: %s is successfully deleted\n", arg);
+       }
+
+       send_ok(ssl);
+}
+
 /** check for name with end-of-string, space or tab after it */
 static int
 cmdcmp(char* p, const char* cmd, size_t len)
@@ -1983,6 +2286,16 @@ execute_cmd(struct daemon_remote* rc, RES* ssl, char* 
cmd, struct rc_state* rs)
                do_repattern(ssl, rc->xfrd);
        } else if(cmdcmp(p, "serverpid", 9)) {
                do_serverpid(ssl, rc->xfrd);
+       } else if(cmdcmp(p, "print_tsig", 10)) {
+               do_print_tsig(ssl, rc->xfrd, skipwhite(p+10));
+       } else if(cmdcmp(p, "update_tsig", 11)) {
+               do_update_tsig(ssl, rc->xfrd, skipwhite(p+11));
+       } else if(cmdcmp(p, "add_tsig", 8)) {
+               do_add_tsig(ssl, rc->xfrd, skipwhite(p+8));
+       } else if(cmdcmp(p, "assoc_tsig", 10)) {
+               do_assoc_tsig(ssl, rc->xfrd, skipwhite(p+10));
+       } else if(cmdcmp(p, "del_tsig", 8)) {
+               do_del_tsig(ssl, rc->xfrd, skipwhite(p+8));
        } else {
                (void)ssl_printf(ssl, "error unknown command '%s'\n", p);
        }
diff --git remote.h remote.h
index 0a8738ab4a9..fb8fd699a0f 100644
--- remote.h
+++ remote.h
@@ -50,7 +50,6 @@ struct nsd_options;
 
 /* private, defined in remote.c to keep ssl.h out of this header */
 struct daemon_remote;
-struct rc_state;
 
 /* the remote control needs less backlog than the tcp53 service */
 #define TCP_BACKLOG_REMOTE 16 /* listen() tcp backlog */
diff --git server.c server.c
index edde352117b..ff2f62fff82 100644
--- server.c
+++ server.c
@@ -740,13 +740,55 @@ server_init_ifs(struct nsd *nsd, size_t from, size_t to, 
int* reuseport_works)
 #endif
 #if defined(AF_INET)
                if (addr->ai_family == AF_INET) {
-#  if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
-                       int action = IP_PMTUDISC_DONT;
-                       if (setsockopt(nsd->udp[i].s, IPPROTO_IP, 
-                               IP_MTU_DISCOVER, &action, sizeof(action)) < 0)
-                       {
-                               log_msg(LOG_ERR, "setsockopt(..., 
IP_MTU_DISCOVER, IP_PMTUDISC_DONT...) failed: %s",
-                                       strerror(errno));
+#  if defined(IP_MTU_DISCOVER)
+                       int mtudisc_disabled = 0;
+#   if defined(IP_PMTUDISC_OMIT)
+               /* Try IP_PMTUDISC_OMIT first */
+
+               /*
+                * Linux 3.15 has IP_PMTUDISC_OMIT which makes sockets
+                * ignore PMTU information and send packets with DF=0.
+                * Fragmentation is allowed if and only if the packet
+                * size exceeds the outgoing interface MTU or the packet
+                * encounters smaller MTU link in network.
+                * This mitigates DNS fragmentation attacks by preventing
+                * forged PMTU information.
+                * FreeBSD already has same semantics without setting
+                * the option.
+                */
+                       int action_omit = IP_PMTUDISC_OMIT;
+                       if (!mtudisc_disabled) {
+                               if(setsockopt(nsd->udp[i].s, IPPROTO_IP,
+                                       IP_MTU_DISCOVER, &action_omit,
+                                       sizeof(action_omit)) < 0)
+                               {
+                                       log_msg(LOG_ERR, "setsockopt(..., 
IP_MTU_DISCOVER, IP_PMTUDISC_OMIT...) failed: %s",
+                                               strerror(errno));
+                               } else {
+                                       mtudisc_disabled = 1;
+                               }
+                       }       
+#   endif /* IP_PMTUDISC_OMIT */
+#   if defined(IP_PMTUDISC_DONT)
+                       /* 
+                        * Use IP_PMTUDISC_DONT
+                        * if IP_PMTUDISC_OMIT failed / undefined
+                        */
+                       if (!mtudisc_disabled) {
+                               int action_dont = IP_PMTUDISC_DONT;
+                               if (setsockopt(nsd->udp[i].s, IPPROTO_IP, 
+                                       IP_MTU_DISCOVER, &action_dont,
+                                       sizeof(action_dont)) < 0)
+                               {
+                                       log_msg(LOG_ERR, "setsockopt(..., 
IP_MTU_DISCOVER, IP_PMTUDISC_DONT...) failed: %s",
+                                               strerror(errno));
+                               } else {
+                                       mtudisc_disabled = 1;
+                               }
+                       }
+#   endif /* IP_PMTUDISC_DONT */
+                       /* exit if all methods to disable PMTUD failed */
+                       if(!mtudisc_disabled) {
                                return -1;
                        }
 #  elif defined(IP_DONTFRAG)
diff --git udb.h udb.h
index 4cbe4031880..8d7ee137ad1 100644
--- udb.h
+++ udb.h
@@ -165,7 +165,9 @@ struct udb_glob_d {
        volatile uint64_t rb_size;
        /** segment of move rollback, for an XL chunk that overlaps. */
        volatile uint64_t rb_seg;
-       /** linked list for content-listing, 0 if empty */
+       /** linked list for content-listing, 0 if empty;
+        * this pointer is unused; and could be removed if the database
+        * format is modified or updated. */
        udb_rel_ptr content_list;
        /** user global data pointer */
        udb_rel_ptr user_global;


-- 
I'm not entirely sure you are real.

Reply via email to