summaryrefslogtreecommitdiffstats
path: root/debian/patches
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--debian/patches/0001-non-linux.diff42
-rw-r--r--debian/patches/0002-multiarch.diff29
-rw-r--r--debian/patches/0003-min-cache-ttl.diff254
-rw-r--r--debian/patches/0004-library_paths.diff136
-rw-r--r--debian/patches/0005-resource_missing_include.diff24
-rw-r--r--debian/patches/0006-prepare_native_pkcs11.diff246
-rw-r--r--debian/patches/0007-ctxstart_no_sighandling.diff22
-rw-r--r--debian/patches/0008-reproducible_build.diff68
-rw-r--r--debian/patches/0009-Add_--install-layout=deb_to_setup.py_call.patch24
-rw-r--r--debian/patches/0010-skip-rtld-deepbind-for-dyndb.diff24
-rw-r--r--debian/patches/0011-keymgr-dont-immediately-delete.diff236
-rw-r--r--debian/patches/0012-CVE-2018-5743-Limiting-simultaneous-TCP-clients-is-i.patch912
-rw-r--r--debian/patches/0013-Replace-atomic-operations-in-bin-named-client.c-with.patch128
-rw-r--r--debian/patches/0014-Disable-broken-Ed448-support.patch508
-rw-r--r--debian/patches/0015-move-item_out-test-inside-lock-in-dns_dispatch_getne.patch54
-rw-r--r--debian/patches/0016-Set-a-limit-on-number-of-simultaneous-pipelined-TCP-.patch117
-rw-r--r--debian/patches/0017-libns-Rename-ns_tcpconn-refs-member-to-clients.patch82
-rw-r--r--debian/patches/0018-CVE-2020-8616.patch212
-rw-r--r--debian/patches/0019-CVE-2020-8617.patch33
-rw-r--r--debian/patches/0020-Remove-INSIST-from-from-new_reference.patch568
-rw-r--r--debian/patches/0021-Always-keep-a-copy-of-the-message.patch55
-rw-r--r--debian/patches/0022-Fix-crash-in-pk11_numbits-when-native-pkcs11-is-used.patch400
-rw-r--r--debian/patches/0023-Wait-more-than-1-second-for-NSEC3-chain-changes.patch71
-rw-r--r--debian/patches/0024-Update-policy-subdomain-was-incorrectly-treated-as-z.patch27
-rw-r--r--debian/patches/0025-Add-a-test-for-update-policy-subdomain.patch73
-rw-r--r--debian/patches/0026-Add-a-test-for-update-policy-zonesub.patch111
-rw-r--r--debian/patches/0027-CVE-2020-8625.patch25
-rw-r--r--debian/patches/0028-Free-resources-when-gss_accept_sec_context-fails.patch28
-rw-r--r--debian/patches/0029-Check-SOA-owner-names-in-zone-transfers.patch40
-rw-r--r--debian/patches/0030-Address-inconsistencies-in-checking-added-RRsets.patch46
-rw-r--r--debian/patches/0031-Unload-a-zone-if-a-transfer-breaks-its-SOA-record.patch40
-rw-r--r--debian/patches/0032-Handle-DNAME-lookup-via-itself.patch41
-rw-r--r--debian/patches/0033-Disable-lame-ttl-cache.patch70
-rw-r--r--debian/patches/0034-Enable-lame-response-detection-even-with-disabled-la.patch48
-rw-r--r--debian/patches/0035-CVE-2021-25220.patch203
-rw-r--r--debian/patches/series35
36 files changed, 5032 insertions, 0 deletions
diff --git a/debian/patches/0001-non-linux.diff b/debian/patches/0001-non-linux.diff
new file mode 100644
index 0000000..9a5c95f
--- /dev/null
+++ b/debian/patches/0001-non-linux.diff
@@ -0,0 +1,42 @@
+From: Debian DNS Packaging <pkg-dns-devel@lists.alioth.debian.org>
+Date: Fri, 24 Nov 2017 16:26:53 +0000
+Subject: _non-linux
+
+ FTBFS on kfreebsd, hurd
+
+ Addresses-Debian-Bug: 741285, 746540
+ Signed-off-by: LaMont Jones <lamont@debian.org>
+---
+ configure.in | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+diff --git a/configure.in b/configure.in
+index 9aa7e2d..bf028fe 100644
+--- a/configure.in
++++ b/configure.in
+@@ -462,7 +462,7 @@ case "$host" in
+ # as it breaks how the two halves (Basic and Advanced) of the IPv6
+ # Socket API were designed to be used but we have to live with it.
+ # Define _GNU_SOURCE to pull in the IPv6 Advanced Socket API.
+- *-linux* | *-kfreebsd*-gnu*)
++ *-linux* | *-kfreebsd*-gnu* | *-gnu*)
+ STD_CDEFINES="$STD_CDEFINES -D_GNU_SOURCE"
+ CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE"
+ ;;
+@@ -1327,7 +1327,7 @@ then
+ # LinuxThreads requires some changes to the way we
+ # deal with signals.
+ #
+- *-linux*)
++ *-linux*|*-kfreebsd*-gnu)
+ AC_DEFINE(HAVE_LINUXTHREADS)
+ ;;
+ #
+@@ -2780,7 +2780,6 @@ case "$host" in
+ ;;
+ *)
+ AC_CHECK_LIB(socket, socket)
+- AC_CHECK_LIB(nsl, inet_addr)
+ ;;
+ esac
+
diff --git a/debian/patches/0002-multiarch.diff b/debian/patches/0002-multiarch.diff
new file mode 100644
index 0000000..3ba24f4
--- /dev/null
+++ b/debian/patches/0002-multiarch.diff
@@ -0,0 +1,29 @@
+From: Debian DNS Packaging <pkg-dns-devel@lists.alioth.debian.org>
+Date: Fri, 24 Nov 2017 16:26:53 +0000
+Subject: _multiarch
+
+---
+ isc-config.sh.in | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/isc-config.sh.in b/isc-config.sh.in
+index a8a0a89..1e7903e 100644
+--- a/isc-config.sh.in
++++ b/isc-config.sh.in
+@@ -13,7 +13,6 @@ prefix=@prefix@
+ exec_prefix=@exec_prefix@
+ exec_prefix_set=
+ includedir=@includedir@
+-libdir=@libdir@
+
+ usage()
+ {
+@@ -132,7 +131,7 @@ if test x"$echo_libs" = x"true"; then
+ if test x"${exec_prefix_set}" = x"true"; then
+ libs="-L${exec_prefix}/lib"
+ else
+- libs="-L${libdir}"
++ libs=
+ fi
+ if test x"$libirs" = x"true" ; then
+ libs="$libs -lirs"
diff --git a/debian/patches/0003-min-cache-ttl.diff b/debian/patches/0003-min-cache-ttl.diff
new file mode 100644
index 0000000..13453fd
--- /dev/null
+++ b/debian/patches/0003-min-cache-ttl.diff
@@ -0,0 +1,254 @@
+From: Debian DNS Packaging <pkg-dns-devel@lists.alioth.debian.org>
+Date: Fri, 24 Nov 2017 16:26:54 +0000
+Subject: _min-cache-ttl
+
+ Add min-cache-ttl and min-ncache-ttl keywords
+
+ Sometimes it is useful to set a 'floor' on the TTL for records
+ to be cached. Some sites like to use ridiculously low TTLs for
+ some reason, and that often is not compatible with slow links.
+
+ Signed-off-by: Michael Milligan <milli@acmeps.com>
+ Signed-off-by: LaMont Jones <lamont@debian.org>
+---
+ bin/named/config.c | 2 ++
+ bin/named/server.c | 12 ++++++++++++
+ bin/tests/named.conf | 2 ++
+ lib/dns/include/dns/ncache.h | 6 ++++--
+ lib/dns/include/dns/view.h | 2 ++
+ lib/dns/ncache.c | 18 ++++++++++++------
+ lib/dns/resolver.c | 22 ++++++++++++++++------
+ lib/isccfg/namedconf.c | 2 ++
+ 8 files changed, 52 insertions(+), 14 deletions(-)
+
+diff --git a/bin/named/config.c b/bin/named/config.c
+index 2732a8f..d22ee4b 100644
+--- a/bin/named/config.c
++++ b/bin/named/config.c
+@@ -182,6 +182,8 @@ options {\n\
+ max-recursion-depth 7;\n\
+ max-recursion-queries 75;\n\
+ message-compression yes;\n\
++ min-ncache-ttl 0; /* 0 hours */\n\
++ min-cache-ttl 0; /* 0 seconds */\n\
+ # min-roots <obsolete>;\n\
+ minimal-any false;\n\
+ minimal-responses false;\n\
+diff --git a/bin/named/server.c b/bin/named/server.c
+index 7f87ccf..149458e 100644
+--- a/bin/named/server.c
++++ b/bin/named/server.c
+@@ -3706,6 +3706,18 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist,
+ if (view->maxncachettl > 7 * 24 * 3600)
+ view->maxncachettl = 7 * 24 * 3600;
+
++ obj = NULL;
++ result = ns_config_get(maps, "min-cache-ttl", &obj);
++ INSIST(result == ISC_R_SUCCESS);
++ view->mincachettl = cfg_obj_asuint32(obj);
++
++ obj = NULL;
++ result = ns_config_get(maps, "min-ncache-ttl", &obj);
++ INSIST(result == ISC_R_SUCCESS);
++ view->minncachettl = cfg_obj_asuint32(obj);
++ if (view->minncachettl > 7 * 24 * 3600)
++ view->minncachettl = 7 * 24 * 3600;
++
+ /*
+ * Configure the view's cache.
+ *
+diff --git a/bin/tests/named.conf b/bin/tests/named.conf
+index 5673e98..34435b6 100644
+--- a/bin/tests/named.conf
++++ b/bin/tests/named.conf
+@@ -46,6 +46,7 @@ options {
+ memstatistics-file "named.memstats"; // _PATH_MEMSTATS
+
+ max-cache-ttl 999;
++ min-cache-ttl 666;
+ auth-nxdomain yes; // always set AA on NXDOMAIN.
+ // don't set this to 'no' unless
+ // you know what you're doing -- older
+@@ -148,6 +149,7 @@ options {
+ min-refresh-time 777;
+
+ max-ncache-ttl 333;
++ min-ncache-ttl 222;
+ min-roots 15;
+ serial-queries 34;
+
+diff --git a/lib/dns/include/dns/ncache.h b/lib/dns/include/dns/ncache.h
+index 2942c26..c2e1e7b 100644
+--- a/lib/dns/include/dns/ncache.h
++++ b/lib/dns/include/dns/ncache.h
+@@ -56,12 +56,14 @@ ISC_LANG_BEGINDECLS
+
+ isc_result_t
+ dns_ncache_add(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
+- dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t maxttl,
++ dns_rdatatype_t covers, isc_stdtime_t now,
++ dns_ttl_t minttl, dns_ttl_t maxttl,
+ dns_rdataset_t *addedrdataset);
+ isc_result_t
+ dns_ncache_addoptout(dns_message_t *message, dns_db_t *cache,
+ dns_dbnode_t *node, dns_rdatatype_t covers,
+- isc_stdtime_t now, dns_ttl_t maxttl,
++ isc_stdtime_t now,
++ dns_ttl_t minttl, dns_ttl_t maxttl,
+ bool optout, dns_rdataset_t *addedrdataset);
+ /*%<
+ * Convert the authority data from 'message' into a negative cache
+diff --git a/lib/dns/include/dns/view.h b/lib/dns/include/dns/view.h
+index 8e21298..3b3ecc9 100644
+--- a/lib/dns/include/dns/view.h
++++ b/lib/dns/include/dns/view.h
+@@ -153,6 +153,8 @@ struct dns_view {
+ bool sendcookie;
+ dns_ttl_t maxcachettl;
+ dns_ttl_t maxncachettl;
++ dns_ttl_t mincachettl;
++ dns_ttl_t minncachettl;
+ uint32_t nta_lifetime;
+ uint32_t nta_recheck;
+ char *nta_file;
+diff --git a/lib/dns/ncache.c b/lib/dns/ncache.c
+index c5078de..9cde098 100644
+--- a/lib/dns/ncache.c
++++ b/lib/dns/ncache.c
+@@ -45,7 +45,8 @@
+
+ static isc_result_t
+ addoptout(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
+- dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t maxttl,
++ dns_rdatatype_t covers, isc_stdtime_t now,
++ dns_ttl_t minttl, dns_ttl_t maxttl,
+ bool optout, bool secure,
+ dns_rdataset_t *addedrdataset);
+
+@@ -95,26 +96,29 @@ copy_rdataset(dns_rdataset_t *rdataset, isc_buffer_t *buffer) {
+
+ isc_result_t
+ dns_ncache_add(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
+- dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t maxttl,
++ dns_rdatatype_t covers, isc_stdtime_t now,
++ dns_ttl_t minttl, dns_ttl_t maxttl,
+ dns_rdataset_t *addedrdataset)
+ {
+- return (addoptout(message, cache, node, covers, now, maxttl,
++ return (addoptout(message, cache, node, covers, now, minttl, maxttl,
+ false, false, addedrdataset));
+ }
+
+ isc_result_t
+ dns_ncache_addoptout(dns_message_t *message, dns_db_t *cache,
+ dns_dbnode_t *node, dns_rdatatype_t covers,
+- isc_stdtime_t now, dns_ttl_t maxttl,
++ isc_stdtime_t now,
++ dns_ttl_t minttl, dns_ttl_t maxttl,
+ bool optout, dns_rdataset_t *addedrdataset)
+ {
+- return (addoptout(message, cache, node, covers, now, maxttl,
++ return (addoptout(message, cache, node, covers, now, minttl, maxttl,
+ optout, true, addedrdataset));
+ }
+
+ static isc_result_t
+ addoptout(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
+- dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t maxttl,
++ dns_rdatatype_t covers, isc_stdtime_t now,
++ dns_ttl_t minttl, dns_ttl_t maxttl,
+ bool optout, bool secure,
+ dns_rdataset_t *addedrdataset)
+ {
+@@ -181,6 +185,8 @@ addoptout(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
+ type == dns_rdatatype_nsec3) {
+ if (ttl > rdataset->ttl)
+ ttl = rdataset->ttl;
++ if (ttl < minttl)
++ ttl = minttl;
+ if (trust > rdataset->trust)
+ trust = rdataset->trust;
+ /*
+diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c
+index 0abf4de..1d76504 100644
+--- a/lib/dns/resolver.c
++++ b/lib/dns/resolver.c
+@@ -564,7 +564,9 @@ static bool fctx_unlink(fetchctx_t *fctx);
+ static isc_result_t ncache_adderesult(dns_message_t *message,
+ dns_db_t *cache, dns_dbnode_t *node,
+ dns_rdatatype_t covers,
+- isc_stdtime_t now, dns_ttl_t maxttl,
++ isc_stdtime_t now,
++ dns_ttl_t minttl,
++ dns_ttl_t maxttl,
+ bool optout,
+ bool secure,
+ dns_rdataset_t *ardataset,
+@@ -5015,7 +5017,7 @@ validated(isc_task_t *task, isc_event_t *event) {
+ ttl = 0;
+
+ result = ncache_adderesult(fctx->rmessage, fctx->cache, node,
+- covers, now, ttl, vevent->optout,
++ covers, now, fctx->res->view->minncachettl, ttl, vevent->optout,
+ vevent->secure, ardataset, &eresult);
+ if (result != ISC_R_SUCCESS)
+ goto noanswer_response;
+@@ -5480,6 +5482,12 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo,
+ rdataset->ttl = res->view->maxcachettl;
+ }
+
++ /*
++ * Enforce configured minimum cache TTL.
++ */
++ if (rdataset->ttl < res->view->mincachettl)
++ rdataset->ttl = res->view->mincachettl;
++
+ /*
+ * Mark the rdataset as being prefetch eligible.
+ */
+@@ -5868,7 +5876,8 @@ cache_message(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, isc_stdtime_t now)
+ */
+ static isc_result_t
+ ncache_adderesult(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
+- dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t maxttl,
++ dns_rdatatype_t covers, isc_stdtime_t now,
++ dns_ttl_t minttl, dns_ttl_t maxttl,
+ bool optout, bool secure,
+ dns_rdataset_t *ardataset, isc_result_t *eresultp)
+ {
+@@ -5881,10 +5890,10 @@ ncache_adderesult(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
+ }
+ if (secure)
+ result = dns_ncache_addoptout(message, cache, node, covers,
+- now, maxttl, optout, ardataset);
++ now, minttl, maxttl, optout, ardataset);
+ else
+ result = dns_ncache_add(message, cache, node, covers, now,
+- maxttl, ardataset);
++ minttl, maxttl, ardataset);
+ if (result == DNS_R_UNCHANGED || result == ISC_R_SUCCESS) {
+ /*
+ * If the cache now contains a negative entry and we
+@@ -6059,7 +6068,8 @@ ncache_message(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
+ ttl = 0;
+
+ result = ncache_adderesult(fctx->rmessage, fctx->cache, node,
+- covers, now, ttl, false,
++ covers, now, ttl,
++ fctx->res->view->minncachettl, false,
+ false, ardataset, &eresult);
+ if (result != ISC_R_SUCCESS)
+ goto unlock;
+diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c
+index cd797a6..fdaf8ff 100644
+--- a/lib/isccfg/namedconf.c
++++ b/lib/isccfg/namedconf.c
+@@ -1780,6 +1780,8 @@ view_clauses[] = {
+ { "max-recursion-queries", &cfg_type_uint32, 0 },
+ { "max-udp-size", &cfg_type_uint32, 0 },
+ { "message-compression", &cfg_type_boolean, 0 },
++ { "min-cache-ttl", &cfg_type_uint32, 0 },
++ { "min-ncache-ttl", &cfg_type_uint32, 0 },
+ { "min-roots", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTIMP },
+ { "minimal-any", &cfg_type_boolean, 0 },
+ { "minimal-responses", &cfg_type_minimal, 0 },
diff --git a/debian/patches/0004-library_paths.diff b/debian/patches/0004-library_paths.diff
new file mode 100644
index 0000000..abc8d66
--- /dev/null
+++ b/debian/patches/0004-library_paths.diff
@@ -0,0 +1,136 @@
+From: Debian DNS Packaging <pkg-dns-devel@lists.alioth.debian.org>
+Date: Fri, 24 Nov 2017 16:26:54 +0000
+Subject: _library_paths
+
+ Makefile.in: be explicit about library paths
+
+ Debian policy requires that all dependent libs be in the .so, not just the
+ immediately depended ones.
+
+ Signed-off-by: LaMont Jones <lamont@debian.org>
+---
+ lib/dns/Makefile.in | 4 +++-
+ lib/irs/Makefile.in | 4 +++-
+ lib/isc/Makefile.in | 3 +++
+ lib/isccc/Makefile.in | 4 +++-
+ lib/isccfg/Makefile.in | 2 +-
+ 5 files changed, 13 insertions(+), 4 deletions(-)
+
+diff --git a/lib/dns/Makefile.in b/lib/dns/Makefile.in
+index 4a8549e..e0fcca4 100644
+--- a/lib/dns/Makefile.in
++++ b/lib/dns/Makefile.in
+@@ -37,7 +37,7 @@ ISCLIBS = ../../lib/isc/libisc.@A@
+
+ ISCDEPLIBS = ../../lib/isc/libisc.@A@
+
+-LIBS = @LIBS@
++LIBS = @LIBS@ -L../../lib/isc -lcrypto
+
+ # Alphabetically
+
+@@ -155,6 +155,7 @@ libdns.la: ${OBJS}
+ ${CC} ${ALL_CFLAGS} ${LDFLAGS} -o libdns.la -rpath ${libdir} \
+ -version-info ${LIBINTERFACE}:${LIBREVISION}:${LIBAGE} \
+ ${OBJS} ${ISCLIBS} @DNS_CRYPTO_LIBS@ ${LIBS}
++ ln -sf .libs/libdns.so .
+
+ include: gen
+ ${MAKE} include/dns/enumtype.h
+@@ -206,6 +207,7 @@ clean distclean::
+ newrr::
+ rm -f code.h include/dns/enumtype.h include/dns/enumclass.h
+ rm -f include/dns/rdatastruct.h
++ rm -f libdns.so
+
+ rdata.@O@: include
+
+diff --git a/lib/irs/Makefile.in b/lib/irs/Makefile.in
+index fc11447..4bb85b1 100644
+--- a/lib/irs/Makefile.in
++++ b/lib/irs/Makefile.in
+@@ -36,7 +36,7 @@ SRCS = context.c \
+ gai_strerror.c getaddrinfo.c getnameinfo.c \
+ resconf.c
+
+-LIBS = @LIBS@
++LIBS = @LIBS@ -L../../lib/isc -L../../lib/dns -L../../lib/isccfg -lcrypto -lisc -ldns -lisccfg
+
+ SUBDIRS = include
+ TESTDIRS = @UNITTESTS@
+@@ -61,6 +61,7 @@ libirs.la: ${OBJS} version.@O@
+ ${CC} ${ALL_CFLAGS} ${LDFLAGS} -o libirs.la -rpath ${libdir} \
+ -version-info ${LIBINTERFACE}:${LIBREVISION}:${LIBAGE} \
+ ${OBJS} version.@O@ ${LIBS}
++ ln -sf .libs/libirs.so .
+
+ timestamp: libirs.@A@
+ touch timestamp
+@@ -78,3 +79,4 @@ uninstall::
+
+ clean distclean::
+ rm -f libirs.@A@ libirs.la timestamp
++ rm -f libdns.so
+diff --git a/lib/isc/Makefile.in b/lib/isc/Makefile.in
+index ba53ef1..ba3f013 100644
+--- a/lib/isc/Makefile.in
++++ b/lib/isc/Makefile.in
+@@ -120,12 +120,14 @@ libisc.la: ${OBJS} ${SYMTBLOBJS}
+ ${CC} ${ALL_CFLAGS} ${LDFLAGS} -o libisc.la -rpath ${libdir} \
+ -version-info ${LIBINTERFACE}:${LIBREVISION}:${LIBAGE} \
+ ${OBJS} ${SYMTBLOBJS} ${LIBS}
++ ln -sf .libs/libisc.so .
+
+ libisc-nosymtbl.la: ${OBJS}
+ ${LIBTOOL_MODE_LINK} \
+ ${CC} ${ALL_CFLAGS} ${LDFLAGS} -o libisc-nosymtbl.la -rpath ${libdir} \
+ -version-info ${LIBINTERFACE}:${LIBREVISION}:${LIBAGE} \
+ ${OBJS} ${LIBS}
++ ln -sf .libs/libisc-nosymtbl.so .
+
+ timestamp: libisc.@A@ libisc-nosymtbl.@A@
+ touch timestamp
+@@ -144,3 +146,4 @@ uninstall::
+ clean distclean::
+ rm -f libisc.@A@ libisc-nosymtbl.@A@ libisc.la \
+ libisc-nosymtbl.la timestamp
++ rm -f libisc.so libisc-nosymtbl.so
+diff --git a/lib/isccc/Makefile.in b/lib/isccc/Makefile.in
+index ca88e98..8d875bc 100644
+--- a/lib/isccc/Makefile.in
++++ b/lib/isccc/Makefile.in
+@@ -31,7 +31,7 @@ ISCCCLIBS = ../../lib/isccc/libisccc.@A@
+ ISCDEPLIBS = ../../lib/isc/libisc.@A@
+ ISCCCDEPLIBS = libisccc.@A@
+
+-LIBS = @LIBS@
++LIBS = @LIBS@ -L../../lib/isc
+
+ SUBDIRS = include
+
+@@ -67,6 +67,7 @@ libisccc.la: ${OBJS}
+ ${CC} ${ALL_CFLAGS} ${LDFLAGS} -o libisccc.la -rpath ${libdir} \
+ -version-info ${LIBINTERFACE}:${LIBREVISION}:${LIBAGE} \
+ ${OBJS} ${ISCLIBS} ${LIBS}
++ ln -sf .libs/libisccc.so .
+
+ timestamp: libisccc.@A@
+ touch timestamp
+@@ -82,3 +83,4 @@ uninstall::
+
+ clean distclean::
+ rm -f libisccc.@A@ timestamp
++ rm -f libisccc.so
+diff --git a/lib/isccfg/Makefile.in b/lib/isccfg/Makefile.in
+index f459bd5..0c42b2a 100644
+--- a/lib/isccfg/Makefile.in
++++ b/lib/isccfg/Makefile.in
+@@ -29,7 +29,7 @@ ISCCFGLIBS = ../../lib/cfg/libisccfg.@A@
+ ISCDEPLIBS = ../../lib/isc/libisc.@A@
+ ISCCFGDEPLIBS = libisccfg.@A@
+
+-LIBS = @LIBS@
++LIBS = @LIBS@ -L../dns -L../isc -L../isccc
+
+ SUBDIRS = include
+ TESTDIRS = @UNITTESTS@
diff --git a/debian/patches/0005-resource_missing_include.diff b/debian/patches/0005-resource_missing_include.diff
new file mode 100644
index 0000000..cc1fe91
--- /dev/null
+++ b/debian/patches/0005-resource_missing_include.diff
@@ -0,0 +1,24 @@
+From: Debian DNS Packaging <pkg-dns-devel@lists.alioth.debian.org>
+Date: Fri, 24 Nov 2017 16:26:54 +0000
+Subject: _resource_missing_include
+
+ lib/isc/unix/resource.c was missing inttypes.h include.
+
+ Addresses-Ubuntu-Bug: 674199
+ Signed-off-by: LaMont Jones <lamont@debian.org>
+---
+ lib/isc/unix/resource.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/lib/isc/unix/resource.c b/lib/isc/unix/resource.c
+index 6d5c5aa..a4302c6 100644
+--- a/lib/isc/unix/resource.c
++++ b/lib/isc/unix/resource.c
+@@ -25,6 +25,7 @@
+ #include <isc/util.h>
+
+ #ifdef __linux__
++#include <inttypes.h>
+ #include <linux/fs.h> /* To get the large NR_OPEN. */
+ #endif
+
diff --git a/debian/patches/0006-prepare_native_pkcs11.diff b/debian/patches/0006-prepare_native_pkcs11.diff
new file mode 100644
index 0000000..9cd63b6
--- /dev/null
+++ b/debian/patches/0006-prepare_native_pkcs11.diff
@@ -0,0 +1,246 @@
+From: Debian DNS Packaging <pkg-dns-devel@lists.alioth.debian.org>
+Date: Fri, 24 Nov 2017 16:26:54 +0000
+Subject: _prepare_native_pkcs11
+
+---
+ bin/Makefile.in | 2 +-
+ bin/dnssec/Makefile.in | 2 +-
+ bin/named/Makefile.in | 2 +-
+ bin/pkcs11/Makefile.in | 6 +++---
+ configure.in | 51 +++++++++++++++++++++++++++++++++++---------------
+ lib/Makefile.in | 2 +-
+ make/includes.in | 10 ++++++++++
+ 7 files changed, 53 insertions(+), 22 deletions(-)
+
+diff --git a/bin/Makefile.in b/bin/Makefile.in
+index f0c504a..ef6bf5f 100644
+--- a/bin/Makefile.in
++++ b/bin/Makefile.in
+@@ -11,7 +11,7 @@ srcdir = @srcdir@
+ VPATH = @srcdir@
+ top_srcdir = @top_srcdir@
+
+-SUBDIRS = named rndc dig delv dnssec tools nsupdate check confgen \
++SUBDIRS = named named-pkcs11 rndc dig delv dnssec dnssec-pkcs11 tools nsupdate check confgen \
+ @NZD_TOOLS@ @PYTHON_TOOLS@ @PKCS11_TOOLS@ tests
+ TARGETS =
+
+diff --git a/bin/dnssec/Makefile.in b/bin/dnssec/Makefile.in
+index 2239ad1..1ded1fa 100644
+--- a/bin/dnssec/Makefile.in
++++ b/bin/dnssec/Makefile.in
+@@ -19,7 +19,7 @@ VERSION=@BIND9_VERSION@
+
+ CINCLUDES = ${DNS_INCLUDES} ${ISC_INCLUDES} @DST_OPENSSL_INC@
+
+-CDEFINES = -DVERSION=\"${VERSION}\" @USE_PKCS11@ @PKCS11_ENGINE@ \
++CDEFINES = -DVERSION=\"${VERSION}\" \
+ @CRYPTO@ -DPK11_LIB_LOCATION=\"@PKCS11_PROVIDER@\"
+ CWARNINGS =
+
+diff --git a/bin/named/Makefile.in b/bin/named/Makefile.in
+index 1c41397..5e6e84c 100644
+--- a/bin/named/Makefile.in
++++ b/bin/named/Makefile.in
+@@ -47,7 +47,7 @@ CINCLUDES = -I${srcdir}/include -I${srcdir}/unix/include -I. \
+ ${ISCCFG_INCLUDES} ${ISCCC_INCLUDES} ${ISC_INCLUDES} \
+ ${DLZDRIVER_INCLUDES} ${DBDRIVER_INCLUDES} @DST_OPENSSL_INC@
+
+-CDEFINES = @CONTRIB_DLZ@ @USE_PKCS11@ @PKCS11_ENGINE@ @CRYPTO@
++CDEFINES = @CONTRIB_DLZ@ @CRYPTO@
+
+ CWARNINGS =
+
+diff --git a/bin/pkcs11/Makefile.in b/bin/pkcs11/Makefile.in
+index ae90616..4bc1256 100644
+--- a/bin/pkcs11/Makefile.in
++++ b/bin/pkcs11/Makefile.in
+@@ -15,13 +15,13 @@ top_srcdir = @top_srcdir@
+
+ @BIND9_MAKE_INCLUDES@
+
+-CINCLUDES = ${ISC_INCLUDES}
++CINCLUDES = ${ISC_PKCS11_INCLUDES}
+
+ CDEFINES =
+
+-ISCLIBS = ../../lib/isc/libisc.@A@ @ISC_OPENSSL_LIBS@
++ISCLIBS = ../../lib/isc-pkcs11/libisc-pkcs11.@A@ @ISC_OPENSSL_LIBS@
+
+-ISCDEPLIBS = ../../lib/isc/libisc.@A@
++ISCDEPLIBS = ../../lib/isc-pkcs11/libisc-pkcs11.@A@
+
+ DEPLIBS = ${ISCDEPLIBS}
+
+diff --git a/configure.in b/configure.in
+index bf028fe..f8603b8 100644
+--- a/configure.in
++++ b/configure.in
+@@ -1109,12 +1109,14 @@ AC_SUBST(USE_GSSAPI)
+ AC_SUBST(DST_GSSAPI_INC)
+ AC_SUBST(DNS_GSSAPI_LIBS)
+ DNS_CRYPTO_LIBS="$DNS_GSSAPI_LIBS $DNS_CRYPTO_LIBS"
++DNS_CRYPTO_PK11_LIBS="$DNS_GSSAPI_LIBS $DNS_CRYPTO_PK11_LIBS"
+
+ #
+ # Applications linking with libdns also need to link with these libraries.
+ #
+
+ AC_SUBST(DNS_CRYPTO_LIBS)
++AC_SUBST(DNS_CRYPTO_PK11_LIBS)
+
+ #
+ # was --with-randomdev specified?
+@@ -1499,11 +1501,6 @@ fi
+ AC_MSG_CHECKING(for OpenSSL library)
+ OPENSSL_WARNING=
+ openssldirs="/usr /usr/local /usr/local/ssl /usr/pkg /usr/sfw"
+-if test "yes" = "$want_native_pkcs11"
+-then
+- use_openssl="native_pkcs11"
+- AC_MSG_RESULT(use of native PKCS11 instead)
+-fi
+
+ if test "auto" = "$use_openssl"
+ then
+@@ -1516,6 +1513,7 @@ then
+ fi
+ done
+ fi
++CRYPTO_PK11=""
+ OPENSSL_ECDSA=""
+ OPENSSL_GOST=""
+ OPENSSL_ED25519=""
+@@ -1537,11 +1535,10 @@ case "$with_gost" in
+ ;;
+ esac
+
+-case "$use_openssl" in
+- native_pkcs11)
+- AC_MSG_RESULT(disabled because of native PKCS11)
++if test "$want_native_pkcs11" = "yes"
++then
+ DST_OPENSSL_INC=""
+- CRYPTO="-DPKCS11CRYPTO"
++ CRYPTO_PK11="-DPKCS11CRYPTO"
+ OPENSSLECDSALINKOBJS=""
+ OPENSSLECDSALINKSRCS=""
+ OPENSSLEDDSALINKOBJS=""
+@@ -1550,7 +1547,9 @@ case "$use_openssl" in
+ OPENSSLGOSTLINKSRCS=""
+ OPENSSLLINKOBJS=""
+ OPENSSLLINKSRCS=""
+- ;;
++fi
++
++case "$use_openssl" in
+ no)
+ AC_MSG_RESULT(no)
+ DST_OPENSSL_INC=""
+@@ -1580,11 +1579,6 @@ case "$use_openssl" in
+ If you don't want OpenSSL, use --without-openssl])
+ ;;
+ *)
+- if test "yes" = "$want_native_pkcs11"
+- then
+- AC_MSG_RESULT()
+- AC_MSG_ERROR([OpenSSL and native PKCS11 cannot be used together.])
+- fi
+ if test "yes" = "$use_openssl"
+ then
+ # User did not specify a path - guess it
+@@ -2007,6 +2001,7 @@ AC_SUBST(OPENSSL_ED25519)
+ AC_SUBST(OPENSSL_GOST)
+
+ DNS_CRYPTO_LIBS="$DNS_CRYPTO_LIBS $DST_OPENSSL_LIBS"
++DNS_CRYPTO_PK11_LIBS="$DNS_CRYPTO_LIBS"
+
+ ISC_PLATFORM_WANTAES="#undef ISC_PLATFORM_WANTAES"
+ if test "yes" = "$with_aes"
+@@ -2326,6 +2321,7 @@ esac
+ AC_SUBST(PKCS11LINKOBJS)
+ AC_SUBST(PKCS11LINKSRCS)
+ AC_SUBST(CRYPTO)
++AC_SUBST(CRYPTO_PK11)
+ AC_SUBST(PKCS11_ECDSA)
+ AC_SUBST(PKCS11_GOST)
+ AC_SUBST(PKCS11_ED25519)
+@@ -5331,8 +5327,11 @@ AC_CONFIG_FILES([
+ bin/delv/Makefile
+ bin/dig/Makefile
+ bin/dnssec/Makefile
++ bin/dnssec-pkcs11/Makefile
+ bin/named/Makefile
+ bin/named/unix/Makefile
++ bin/named-pkcs11/Makefile
++ bin/named-pkcs11/unix/Makefile
+ bin/nsupdate/Makefile
+ bin/pkcs11/Makefile
+ bin/python/Makefile
+@@ -5406,6 +5405,10 @@ AC_CONFIG_FILES([
+ lib/dns/include/dns/Makefile
+ lib/dns/include/dst/Makefile
+ lib/dns/tests/Makefile
++ lib/dns-pkcs11/Makefile
++ lib/dns-pkcs11/include/Makefile
++ lib/dns-pkcs11/include/dns/Makefile
++ lib/dns-pkcs11/include/dst/Makefile
+ lib/irs/Makefile
+ lib/irs/include/Makefile
+ lib/irs/include/irs/Makefile
+@@ -5430,6 +5433,24 @@ AC_CONFIG_FILES([
+ lib/isc/unix/include/Makefile
+ lib/isc/unix/include/isc/Makefile
+ lib/isc/unix/include/pkcs11/Makefile
++ lib/isc-pkcs11/$arch/Makefile
++ lib/isc-pkcs11/$arch/include/Makefile
++ lib/isc-pkcs11/$arch/include/isc/Makefile
++ lib/isc-pkcs11/$thread_dir/Makefile
++ lib/isc-pkcs11/$thread_dir/include/Makefile
++ lib/isc-pkcs11/$thread_dir/include/isc/Makefile
++ lib/isc-pkcs11/Makefile
++ lib/isc-pkcs11/include/Makefile
++ lib/isc-pkcs11/include/isc/Makefile
++ lib/isc-pkcs11/include/isc/platform.h
++ lib/isc-pkcs11/include/pk11/Makefile
++ lib/isc-pkcs11/include/pkcs11/Makefile
++ lib/isc-pkcs11/tests/Makefile
++ lib/isc-pkcs11/nls/Makefile
++ lib/isc-pkcs11/unix/Makefile
++ lib/isc-pkcs11/unix/include/Makefile
++ lib/isc-pkcs11/unix/include/isc/Makefile
++ lib/isc-pkcs11/unix/include/pkcs11/Makefile
+ lib/isccc/Makefile
+ lib/isccc/include/Makefile
+ lib/isccc/include/isccc/Makefile
+diff --git a/lib/Makefile.in b/lib/Makefile.in
+index 81270a0..bcb5312 100644
+--- a/lib/Makefile.in
++++ b/lib/Makefile.in
+@@ -15,7 +15,7 @@ top_srcdir = @top_srcdir@
+ # Attempt to disable parallel processing.
+ .NOTPARALLEL:
+ .NO_PARALLEL:
+-SUBDIRS = isc isccc dns isccfg bind9 lwres irs samples
++SUBDIRS = isc isc-pkcs11 isccc dns dns-pkcs11 isccfg bind9 lwres irs samples
+ TARGETS =
+
+ @BIND9_MAKE_RULES@
+diff --git a/make/includes.in b/make/includes.in
+index fa86ad1..3cfbe9f 100644
+--- a/make/includes.in
++++ b/make/includes.in
+@@ -43,3 +43,13 @@ BIND9_INCLUDES = @BIND9_BIND9_BUILDINCLUDE@ \
+
+ TEST_INCLUDES = \
+ -I${top_srcdir}/lib/tests/include
++
++ISC_PKCS11_INCLUDES = @BIND9_ISC_BUILDINCLUDE@ \
++ -I${top_srcdir}/lib/isc-pkcs11 \
++ -I${top_srcdir}/lib/isc-pkcs11/include \
++ -I${top_srcdir}/lib/isc-pkcs11/unix/include \
++ -I${top_srcdir}/lib/isc-pkcs11/@ISC_THREAD_DIR@/include \
++ -I${top_srcdir}/lib/isc-pkcs11/@ISC_ARCH_DIR@/include
++
++DNS_PKCS11_INCLUDES = @BIND9_DNS_BUILDINCLUDE@ \
++ -I${top_srcdir}/lib/dns-pkcs11/include
diff --git a/debian/patches/0007-ctxstart_no_sighandling.diff b/debian/patches/0007-ctxstart_no_sighandling.diff
new file mode 100644
index 0000000..9304fea
--- /dev/null
+++ b/debian/patches/0007-ctxstart_no_sighandling.diff
@@ -0,0 +1,22 @@
+From: Debian DNS Packaging <pkg-dns-devel@lists.alioth.debian.org>
+Date: Fri, 24 Nov 2017 16:26:54 +0000
+Subject: _ctxstart_no_sighandling
+
+---
+ lib/isc/unix/app.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/lib/isc/unix/app.c b/lib/isc/unix/app.c
+index 7e5a0ee..8574141 100644
+--- a/lib/isc/unix/app.c
++++ b/lib/isc/unix/app.c
+@@ -262,6 +262,9 @@ isc__app_ctxstart(isc_appctx_t *ctx0) {
+ ctx->want_reload = false;
+ ctx->blocked = false;
+
++ if (!isc_bind9)
++ return (ISC_R_SUCCESS);
++
+ #ifndef HAVE_SIGWAIT
+ /*
+ * Install do-nothing handlers for SIGINT and SIGTERM.
diff --git a/debian/patches/0008-reproducible_build.diff b/debian/patches/0008-reproducible_build.diff
new file mode 100644
index 0000000..94d8b58
--- /dev/null
+++ b/debian/patches/0008-reproducible_build.diff
@@ -0,0 +1,68 @@
+From: Debian DNS Packaging <pkg-dns-devel@lists.alioth.debian.org>
+Date: Fri, 24 Nov 2017 16:26:54 +0000
+Subject: _reproducible_build
+
+---
+ lib/dns/gen.c | 35 +++++++++++++++++++++++++++++++++--
+ 1 file changed, 33 insertions(+), 2 deletions(-)
+
+diff --git a/lib/dns/gen.c b/lib/dns/gen.c
+index 2a3b94b..b26a509 100644
+--- a/lib/dns/gen.c
++++ b/lib/dns/gen.c
+@@ -32,6 +32,8 @@
+ #include <stdlib.h>
+ #include <string.h>
+ #include <time.h>
++#include <errno.h>
++#include <limits.h>
+
+ #ifdef WIN32
+ #include "gen-win32.h"
+@@ -528,6 +530,9 @@ main(int argc, char **argv) {
+ char *prefix = NULL;
+ char *suffix = NULL;
+ char *file = NULL;
++ char *source_date_epoch;
++ unsigned long long epoch;
++ char *endptr;
+ isc_dir_t dir;
+
+ for (i = 0; i < TYPENAMES; i++)
+@@ -618,8 +623,34 @@ main(int argc, char **argv) {
+ INSIST(n > 0 && (unsigned)n < sizeof(srcdir));
+ sd(0, "", buf, filetype);
+
+- if (time(&now) != -1) {
+- if ((tm = localtime(&now)) != NULL && tm->tm_year > 104) {
++ source_date_epoch = getenv("SOURCE_DATE_EPOCH");
++ if (source_date_epoch) {
++ errno = 0;
++ epoch = strtoull(source_date_epoch, &endptr, 10);
++ if ((errno == ERANGE && (epoch == ULLONG_MAX || epoch == 0))
++ || (errno != 0 && epoch == 0)) {
++ fprintf(stderr, "Environment variable $SOURCE_DATE_EPOCH: strtoull: %s\n", strerror(errno));
++ exit(EXIT_FAILURE);
++ }
++ if (endptr == source_date_epoch) {
++ fprintf(stderr, "Environment variable $SOURCE_DATE_EPOCH: No digits were found: %s\n", endptr);
++ exit(EXIT_FAILURE);
++ }
++ if (*endptr != '\0') {
++ fprintf(stderr, "Environment variable $SOURCE_DATE_EPOCH: Trailing garbage: %s\n", endptr);
++ exit(EXIT_FAILURE);
++ }
++ if (epoch > ULONG_MAX) {
++ fprintf(stderr, "Environment variable $SOURCE_DATE_EPOCH: value must be smaller than or equal to: %lu but was found to be: %llu \n", ULONG_MAX, epoch);
++ exit(EXIT_FAILURE);
++ }
++ now = epoch;
++ } else {
++ time(&now);
++ }
++
++ if (now != -1) {
++ if ((tm = gmtime(&now)) != NULL && tm->tm_year > 104) {
+ n = snprintf(year, sizeof(year), "-%d",
+ tm->tm_year + 1900);
+ INSIST(n > 0 && (unsigned)n < sizeof(year));
diff --git a/debian/patches/0009-Add_--install-layout=deb_to_setup.py_call.patch b/debian/patches/0009-Add_--install-layout=deb_to_setup.py_call.patch
new file mode 100644
index 0000000..d64e51d
--- /dev/null
+++ b/debian/patches/0009-Add_--install-layout=deb_to_setup.py_call.patch
@@ -0,0 +1,24 @@
+From: Debian DNS Packaging <pkg-dns-devel@lists.alioth.debian.org>
+Date: Fri, 24 Nov 2017 16:26:55 +0000
+Subject: Add_--install-layout=deb_to_setup.py_call
+
+---
+ bin/python/Makefile.in | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/bin/python/Makefile.in b/bin/python/Makefile.in
+index aa678d4..a58d72f 100644
+--- a/bin/python/Makefile.in
++++ b/bin/python/Makefile.in
+@@ -56,9 +56,9 @@ install:: ${TARGETS} installdirs
+ ${INSTALL_DATA} ${srcdir}/dnssec-keymgr.8 ${DESTDIR}${mandir}/man8
+ if test -n "${PYTHON}" ; then \
+ if test -n "${DESTDIR}" ; then \
+- ${PYTHON} ${srcdir}/setup.py install --root=${DESTDIR} --prefix=${prefix} @PYTHON_INSTALL_LIB@ ; \
++ ${PYTHON} ${srcdir}/setup.py install --root=${DESTDIR} --prefix=${prefix} --install-layout=deb @PYTHON_INSTALL_LIB@ ; \
+ else \
+- ${PYTHON} ${srcdir}/setup.py install --prefix=${prefix} @PYTHON_INSTALL_LIB@ ; \
++ ${PYTHON} ${srcdir}/setup.py install --prefix=${prefix} --install-layout=deb @PYTHON_INSTALL_LIB@ ; \
+ fi ; \
+ rm -rf build ; \
+ fi
diff --git a/debian/patches/0010-skip-rtld-deepbind-for-dyndb.diff b/debian/patches/0010-skip-rtld-deepbind-for-dyndb.diff
new file mode 100644
index 0000000..4455d46
--- /dev/null
+++ b/debian/patches/0010-skip-rtld-deepbind-for-dyndb.diff
@@ -0,0 +1,24 @@
+From: BIND 9 Package <bind9@package.debian.org>
+Date: Fri, 31 Aug 2018 08:42:51 +0000
+Subject: skip-rtld-deepbind-for-dyndb
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1410433
+https://bugs.launchpad.net/bugs/1769440
+---
+ lib/dns/dyndb.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+diff --git a/lib/dns/dyndb.c b/lib/dns/dyndb.c
+index 93ad795..11ba0f5 100644
+--- a/lib/dns/dyndb.c
++++ b/lib/dns/dyndb.c
+@@ -133,9 +133,6 @@ load_library(isc_mem_t *mctx, const char *filename, const char *instname,
+ instname, filename);
+
+ flags = RTLD_NOW|RTLD_LOCAL;
+-#ifdef RTLD_DEEPBIND
+- flags |= RTLD_DEEPBIND;
+-#endif
+
+ handle = dlopen(filename, flags);
+ if (handle == NULL)
diff --git a/debian/patches/0011-keymgr-dont-immediately-delete.diff b/debian/patches/0011-keymgr-dont-immediately-delete.diff
new file mode 100644
index 0000000..e0a9cb8
--- /dev/null
+++ b/debian/patches/0011-keymgr-dont-immediately-delete.diff
@@ -0,0 +1,236 @@
+From: Debian DNS Team <team+dns@tracker.debian.org>
+Date: Wed, 20 Nov 2019 22:17:10 +0100
+Subject: keymgr-dont-immediately-delete
+
+---
+ bin/python/isc/keyseries.py.in | 28 ++++++++++++++++++--
+ bin/tests/system/keymgr/19-old-keys/README | 7 +++++
+ bin/tests/system/keymgr/19-old-keys/expect | 12 +++++++++
+ bin/tests/system/keymgr/19-old-keys/extra.sh | 19 ++++++++++++++
+ bin/tests/system/keymgr/19-old-keys/policy.conf | 18 +++++++++++++
+ bin/tests/system/keymgr/clean.sh | 2 ++
+ bin/tests/system/keymgr/setup.sh | 10 ++++++++
+ bin/tests/system/keymgr/tests.sh | 34 +++++++++++++++----------
+ 8 files changed, 114 insertions(+), 16 deletions(-)
+ create mode 100644 bin/tests/system/keymgr/19-old-keys/README
+ create mode 100644 bin/tests/system/keymgr/19-old-keys/expect
+ create mode 100644 bin/tests/system/keymgr/19-old-keys/extra.sh
+ create mode 100644 bin/tests/system/keymgr/19-old-keys/policy.conf
+
+diff --git a/bin/python/isc/keyseries.py.in b/bin/python/isc/keyseries.py.in
+index e1241f0..74ccc64 100644
+--- a/bin/python/isc/keyseries.py.in
++++ b/bin/python/isc/keyseries.py.in
+@@ -77,15 +77,39 @@ class keyseries:
+ a = key.activate()
+ if not p or p > now:
+ key.setpublish(now)
++ p = now
+ if not a or a > now:
+ key.setactivate(now)
++ a = now
+
++ i = key.inactive()
+ if not rp:
+ key.setinactive(None, **kwargs)
+ key.setdelete(None, **kwargs)
++ elif not i or a + rp != i:
++ if not i and a + rp > now + prepub:
++ key.setinactive(a + rp, **kwargs)
++ key.setdelete(a + rp + postpub, **kwargs)
++ elif not i:
++ key.setinactive(now + prepub, **kwargs)
++ key.setdelete(now + prepub + postpub, **kwargs)
++ elif a + rp > i:
++ key.setinactive(a + rp, **kwargs)
++ key.setdelete(a + rp + postpub, **kwargs)
++ elif a + rp > now + prepub:
++ key.setinactive(a + rp, **kwargs)
++ key.setdelete(a + rp + postpub, **kwargs)
++ else:
++ key.setinactive(now + prepub, **kwargs)
++ key.setdelete(now + prepub + postpub, **kwargs)
+ else:
+- key.setinactive(a + rp, **kwargs)
+- key.setdelete(a + rp + postpub, **kwargs)
++ d = key.delete()
++ if not d or i + postpub > now:
++ key.setdelete(i + postpub, **kwargs)
++ elif not d:
++ key.setdelete(now + postpub, **kwargs)
++ elif d < i + postpub:
++ key.setdelete(i + postpub, **kwargs)
+
+ if policy.keyttl != key.ttl:
+ key.setttl(policy.keyttl)
+diff --git a/bin/tests/system/keymgr/19-old-keys/README b/bin/tests/system/keymgr/19-old-keys/README
+new file mode 100644
+index 0000000..424b70c
+--- /dev/null
++++ b/bin/tests/system/keymgr/19-old-keys/README
+@@ -0,0 +1,7 @@
++Copyright (C) Internet Systems Consortium, Inc. ("ISC")
++
++See COPYRIGHT in the source root or http://isc.org/copyright.html for terms.
++
++This directory has a key set which is valid, but which was published
++and activated more than one rollover period ago. dnssec-keymgr should
++not mark the keys as already being inactive and deleted.
+diff --git a/bin/tests/system/keymgr/19-old-keys/expect b/bin/tests/system/keymgr/19-old-keys/expect
+new file mode 100644
+index 0000000..f3e49b3
+--- /dev/null
++++ b/bin/tests/system/keymgr/19-old-keys/expect
+@@ -0,0 +1,12 @@
++kargs="-c policy.conf example.com"
++kmatch=""
++kret=0
++cargs="-d 1w -m 2w example.com"
++cmatch="4,Publish
++4,Activate
++2,Inactive
++2,Delete"
++cret=0
++warn=0
++error=0
++ok=2
+diff --git a/bin/tests/system/keymgr/19-old-keys/extra.sh b/bin/tests/system/keymgr/19-old-keys/extra.sh
+new file mode 100644
+index 0000000..8da6aa1
+--- /dev/null
++++ b/bin/tests/system/keymgr/19-old-keys/extra.sh
+@@ -0,0 +1,19 @@
++# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
++#
++# This Source Code Form is subject to the terms of the Mozilla Public
++# License, v. 2.0. If a copy of the MPL was not distributed with this
++# file, You can obtain one at http://mozilla.org/MPL/2.0/.
++#
++# See the COPYRIGHT file distributed with this work for additional
++# information regarding copyright ownership.
++
++now=`$PERL -e 'print time()."\n";'`
++for keyfile in K*.key; do
++ inactive=`$SETTIME -upI $keyfile | awk '{print $2}'`
++ if [ "$inactive" = UNSET ]; then
++ continue
++ elif [ "$inactive" -lt "$now" ]; then
++ echo_d "inactive date is in the past"
++ ret=1
++ fi
++done
+diff --git a/bin/tests/system/keymgr/19-old-keys/policy.conf b/bin/tests/system/keymgr/19-old-keys/policy.conf
+new file mode 100644
+index 0000000..91817ff
+--- /dev/null
++++ b/bin/tests/system/keymgr/19-old-keys/policy.conf
+@@ -0,0 +1,18 @@
++/*
++ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
++ *
++ * This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
++ *
++ * See the COPYRIGHT file distributed with this work for additional
++ * information regarding copyright ownership.
++ */
++
++policy default {
++ policy global;
++ algorithm nsec3rsasha1;
++ pre-publish zsk 2w;
++ roll-period zsk 6mo;
++ coverage 364d;
++};
+diff --git a/bin/tests/system/keymgr/clean.sh b/bin/tests/system/keymgr/clean.sh
+index dc9f0a0..3b9b1a2 100644
+--- a/bin/tests/system/keymgr/clean.sh
++++ b/bin/tests/system/keymgr/clean.sh
+@@ -11,5 +11,7 @@
+
+ rm -f */K*.key
+ rm -f */K*.private
++rm -f Kexample.com.*.key
++rm -f Kexample.com.*.private
+ rm -f coverage.* keymgr.*
+ rm -f policy.out
+diff --git a/bin/tests/system/keymgr/setup.sh b/bin/tests/system/keymgr/setup.sh
+index 24e6c7c..ea6e566 100644
+--- a/bin/tests/system/keymgr/setup.sh
++++ b/bin/tests/system/keymgr/setup.sh
+@@ -214,3 +214,13 @@ rm -f $dir/K*.private
+ ksk1=`$KEYGEN -K $dir -3fk example.com`
+ zsk1=`$KEYGEN -K $dir -3 example.com`
+ $SETTIME -K $dir -I now+2mo -D now+3mo $zsk1 > /dev/null
++
++# Test 19: Key has been published/active a long time
++dir=19-old-keys
++echo_i "set up $dir"
++rm -f $dir/K*.key
++rm -f $dir/K*.private
++ksk1=`$KEYGEN -K $dir -a rsasha1 -3fk example.com`
++zsk1=`$KEYGEN -K $dir -a rsasha1 -3 example.com`
++$SETTIME -K $dir -P now-2y -A now-2y $ksk1 > /dev/null
++$SETTIME -K $dir -P now-2y -A now-2y $zsk1 > /dev/null
+diff --git a/bin/tests/system/keymgr/tests.sh b/bin/tests/system/keymgr/tests.sh
+index 88b43d9..89fedd3 100644
+--- a/bin/tests/system/keymgr/tests.sh
++++ b/bin/tests/system/keymgr/tests.sh
+@@ -16,13 +16,19 @@ status=0
+ n=1
+
+ matchall () {
++ match_result=ok
+ file=$1
+- echo "$2" | while read matchline; do
+- grep "$matchline" $file > /dev/null 2>&1 || {
+- echo "FAIL"
+- return
++ while IFS="," read expect matchline; do
++ [ -z "$matchline" ] && continue
++ matches=`grep "$matchline" $file | wc -l`
++ [ "$matches" -ne "$expect" ] && {
++ echo "'$matchline': expected $expect found $matches"
++ return 1
+ }
+- done
++ done << EOF
++ $2
++EOF
++ return 0
+ }
+
+ echo_i "checking for DNSSEC key coverage issues"
+@@ -51,11 +57,8 @@ for dir in [0-9][0-9]-*; do
+ ret=1
+ fi
+
+- found=`matchall keymgr.$n "$kmatch"`
+- if [ "$found" = "FAIL" ]; then
+- echo "no match on '$kmatch'"
+- ret=1
+- fi
++ # check for matches in keymgr output
++ matchall keymgr.$n "$kmatch" || ret=1
+
+ # now check coverage
+ $COVERAGE -K $dir $cargs > coverage.$n 2>&1
+@@ -87,10 +90,13 @@ for dir in [0-9][0-9]-*; do
+ ret=1
+ fi
+
+- found=`matchall coverage.$n "$cmatch"`
+- if [ "$found" = "FAIL" ]; then
+- echo "no match on '$cmatch'"
+- ret=1
++ # check for matches in coverage output
++ matchall coverage.$n "$cmatch" || ret=1
++
++ if [ -f $dir/extra.sh ]; then
++ cd $dir
++ . ./extra.sh
++ cd ..
+ fi
+
+ n=`expr $n + 1`
diff --git a/debian/patches/0012-CVE-2018-5743-Limiting-simultaneous-TCP-clients-is-i.patch b/debian/patches/0012-CVE-2018-5743-Limiting-simultaneous-TCP-clients-is-i.patch
new file mode 100644
index 0000000..b451238
--- /dev/null
+++ b/debian/patches/0012-CVE-2018-5743-Limiting-simultaneous-TCP-clients-is-i.patch
@@ -0,0 +1,912 @@
+From: =?utf-8?q?Witold_Kr=C4=99cicki?= <wpk@isc.org>
+Date: Thu, 3 Jan 2019 14:17:43 +0100
+Subject: [CVE-2018-5743]: Limiting simultaneous TCP clients is ineffective
+
+---
+ bin/named/client.c | 427 ++++++++++++++++++++++++++-------
+ bin/named/include/named/client.h | 23 +-
+ bin/named/include/named/interfacemgr.h | 13 +-
+ bin/named/interfacemgr.c | 9 +-
+ doc/arm/Bv9ARM-book.xml | 3 +-
+ lib/isc/include/isc/quota.h | 7 +
+ lib/isc/quota.c | 33 ++-
+ lib/isc/win32/libisc.def.in | 1 +
+ 8 files changed, 396 insertions(+), 120 deletions(-)
+
+diff --git a/bin/named/client.c b/bin/named/client.c
+index 4d26eff..020603d 100644
+--- a/bin/named/client.c
++++ b/bin/named/client.c
+@@ -246,10 +246,11 @@ static void ns_client_dumpmessage(ns_client_t *client, const char *reason);
+ static isc_result_t get_client(ns_clientmgr_t *manager, ns_interface_t *ifp,
+ dns_dispatch_t *disp, bool tcp);
+ static isc_result_t get_worker(ns_clientmgr_t *manager, ns_interface_t *ifp,
+- isc_socket_t *sock);
++ isc_socket_t *sock, ns_client_t *oldclient);
+ static inline bool
+-allowed(isc_netaddr_t *addr, dns_name_t *signer, isc_netaddr_t *ecs_addr,
+- uint8_t ecs_addrlen, uint8_t *ecs_scope, dns_acl_t *acl);
++allowed(isc_netaddr_t *addr, dns_name_t *signer,
++ isc_netaddr_t *ecs_addr, uint8_t ecs_addrlen,
++ uint8_t *ecs_scope, dns_acl_t *acl);
+ static void compute_cookie(ns_client_t *client, uint32_t when,
+ uint32_t nonce, const unsigned char *secret,
+ isc_buffer_t *buf);
+@@ -298,6 +299,119 @@ ns_client_settimeout(ns_client_t *client, unsigned int seconds) {
+ }
+ }
+
++/*%
++ * Allocate a reference-counted object that will maintain a single pointer to
++ * the (also reference-counted) TCP client quota, shared between all the
++ * clients processing queries on a single TCP connection, so that all
++ * clients sharing the one socket will together consume only one slot in
++ * the 'tcp-clients' quota.
++ */
++static isc_result_t
++tcpconn_init(ns_client_t *client, bool force) {
++ isc_result_t result;
++ isc_quota_t *quota = NULL;
++ ns_tcpconn_t *tconn = NULL;
++
++ REQUIRE(client->tcpconn == NULL);
++
++ /*
++ * Try to attach to the quota first, so we won't pointlessly
++ * allocate memory for a tcpconn object if we can't get one.
++ */
++ if (force) {
++ result = isc_quota_force(&ns_g_server->tcpquota, &quota);
++ } else {
++ result = isc_quota_attach(&ns_g_server->tcpquota, &quota);
++ }
++ if (result != ISC_R_SUCCESS) {
++ return (result);
++ }
++
++ /*
++ * A global memory context is used for the allocation as different
++ * client structures may have different memory contexts assigned and a
++ * reference counter allocated here might need to be freed by a
++ * different client. The performance impact caused by memory context
++ * contention here is expected to be negligible, given that this code
++ * is only executed for TCP connections.
++ */
++ tconn = isc_mem_allocate(ns_g_mctx, sizeof(*tconn));
++
++ isc_refcount_init(&tconn->refs, 1);
++ tconn->tcpquota = quota;
++ quota = NULL;
++ tconn->pipelined = false;
++
++ client->tcpconn = tconn;
++
++ return (ISC_R_SUCCESS);
++}
++
++/*%
++ * Increase the count of client structures sharing the TCP connection
++ * that 'source' is associated with; add a pointer to the same tcpconn
++ * to 'target', thus associating it with the same TCP connection.
++ */
++static void
++tcpconn_attach(ns_client_t *source, ns_client_t *target) {
++ int refs;
++
++ REQUIRE(source->tcpconn != NULL);
++ REQUIRE(target->tcpconn == NULL);
++ REQUIRE(source->tcpconn->pipelined);
++
++ isc_refcount_increment(&source->tcpconn->refs, &refs);
++ INSIST(refs > 1);
++ target->tcpconn = source->tcpconn;
++}
++
++/*%
++ * Decrease the count of client structures sharing the TCP connection that
++ * 'client' is associated with. If this is the last client using this TCP
++ * connection, we detach from the TCP quota and free the tcpconn
++ * object. Either way, client->tcpconn is set to NULL.
++ */
++static void
++tcpconn_detach(ns_client_t *client) {
++ ns_tcpconn_t *tconn = NULL;
++ int refs;
++
++ REQUIRE(client->tcpconn != NULL);
++
++ tconn = client->tcpconn;
++ client->tcpconn = NULL;
++
++ isc_refcount_decrement(&tconn->refs, &refs);
++ if (refs == 0) {
++ isc_quota_detach(&tconn->tcpquota);
++ isc_mem_free(ns_g_mctx, tconn);
++ }
++}
++
++/*%
++ * Mark a client as active and increment the interface's 'ntcpactive'
++ * counter, as a signal that there is at least one client servicing
++ * TCP queries for the interface. If we reach the TCP client quota at
++ * some point, this will be used to determine whether a quota overrun
++ * should be permitted.
++ *
++ * Marking the client active with the 'tcpactive' flag ensures proper
++ * accounting, by preventing us from incrementing or decrementing
++ * 'ntcpactive' more than once per client.
++ */
++static void
++mark_tcp_active(ns_client_t *client, bool active) {
++ if (active && !client->tcpactive) {
++ isc_atomic_xadd(&client->interface->ntcpactive, 1);
++ client->tcpactive = active;
++ } else if (!active && client->tcpactive) {
++ uint32_t old =
++ isc_atomic_xadd(&client->interface->ntcpactive, -1);
++ INSIST(old > 0);
++ client->tcpactive = active;
++ }
++}
++
+ /*%
+ * Check for a deactivation or shutdown request and take appropriate
+ * action. Returns true if either is in progress; in this case
+@@ -387,7 +501,8 @@ exit_check(ns_client_t *client) {
+ INSIST(client->recursionquota == NULL);
+
+ if (NS_CLIENTSTATE_READING == client->newstate) {
+- if (!client->pipelined) {
++ INSIST(client->tcpconn != NULL);
++ if (!client->tcpconn->pipelined) {
+ client_read(client);
+ client->newstate = NS_CLIENTSTATE_MAX;
+ return (true); /* We're done. */
+@@ -405,10 +520,13 @@ exit_check(ns_client_t *client) {
+ */
+ INSIST(client->recursionquota == NULL);
+ INSIST(client->newstate <= NS_CLIENTSTATE_READY);
+- if (client->nreads > 0)
++
++ if (client->nreads > 0) {
+ dns_tcpmsg_cancelread(&client->tcpmsg);
+- if (client->nreads != 0) {
+- /* Still waiting for read cancel completion. */
++ }
++
++ /* Still waiting for read cancel completion. */
++ if (client->nreads > 0) {
+ return (true);
+ }
+
+@@ -416,14 +534,49 @@ exit_check(ns_client_t *client) {
+ dns_tcpmsg_invalidate(&client->tcpmsg);
+ client->tcpmsg_valid = false;
+ }
++
++ /*
++ * Soon the client will be ready to accept a new TCP
++ * connection or UDP request, but we may have enough
++ * clients doing that already. Check whether this client
++ * needs to remain active and allow it go inactive if
++ * not.
++ *
++ * UDP clients always go inactive at this point, but a TCP
++ * client may need to stay active and return to READY
++ * state if no other clients are available to listen
++ * for TCP requests on this interface.
++ *
++ * Regardless, if we're going to FREED state, that means
++ * the system is shutting down and we don't need to
++ * retain clients.
++ */
++ if (client->mortal && TCP_CLIENT(client) &&
++ client->newstate != NS_CLIENTSTATE_FREED &&
++ !ns_g_clienttest &&
++ isc_atomic_xadd(&client->interface->ntcpaccepting, 0) == 0)
++ {
++ /* Nobody else is accepting */
++ client->mortal = false;
++ client->newstate = NS_CLIENTSTATE_READY;
++ }
++
++ /*
++ * Detach from TCP connection and TCP client quota,
++ * if appropriate. If this is the last reference to
++ * the TCP connection in our pipeline group, the
++ * TCP quota slot will be released.
++ */
++ if (client->tcpconn) {
++ tcpconn_detach(client);
++ }
++
+ if (client->tcpsocket != NULL) {
+ CTRACE("closetcp");
+ isc_socket_detach(&client->tcpsocket);
++ mark_tcp_active(client, false);
+ }
+
+- if (client->tcpquota != NULL)
+- isc_quota_detach(&client->tcpquota);
+-
+ if (client->timerset) {
+ (void)isc_timer_reset(client->timer,
+ isc_timertype_inactive,
+@@ -431,45 +584,26 @@ exit_check(ns_client_t *client) {
+ client->timerset = false;
+ }
+
+- client->pipelined = false;
+-
+ client->peeraddr_valid = false;
+
+ client->state = NS_CLIENTSTATE_READY;
+- INSIST(client->recursionquota == NULL);
+-
+- /*
+- * Now the client is ready to accept a new TCP connection
+- * or UDP request, but we may have enough clients doing
+- * that already. Check whether this client needs to remain
+- * active and force it to go inactive if not.
+- *
+- * UDP clients go inactive at this point, but TCP clients
+- * may remain active if we have fewer active TCP client
+- * objects than desired due to an earlier quota exhaustion.
+- */
+- if (client->mortal && TCP_CLIENT(client) && !ns_g_clienttest) {
+- LOCK(&client->interface->lock);
+- if (client->interface->ntcpcurrent <
+- client->interface->ntcptarget)
+- client->mortal = false;
+- UNLOCK(&client->interface->lock);
+- }
+
+ /*
+ * We don't need the client; send it to the inactive
+ * queue for recycling.
+ */
+ if (client->mortal) {
+- if (client->newstate > NS_CLIENTSTATE_INACTIVE)
++ if (client->newstate > NS_CLIENTSTATE_INACTIVE) {
+ client->newstate = NS_CLIENTSTATE_INACTIVE;
++ }
+ }
+
+ if (NS_CLIENTSTATE_READY == client->newstate) {
+ if (TCP_CLIENT(client)) {
+ client_accept(client);
+- } else
++ } else {
+ client_udprecv(client);
++ }
+ client->newstate = NS_CLIENTSTATE_MAX;
+ return (true);
+ }
+@@ -481,41 +615,50 @@ exit_check(ns_client_t *client) {
+ /*
+ * We are trying to enter the inactive state.
+ */
+- if (client->naccepts > 0)
++ if (client->naccepts > 0) {
+ isc_socket_cancel(client->tcplistener, client->task,
+ ISC_SOCKCANCEL_ACCEPT);
++ }
+
+ /* Still waiting for accept cancel completion. */
+- if (! (client->naccepts == 0))
++ if (client->naccepts > 0) {
+ return (true);
++ }
+
+ /* Accept cancel is complete. */
+- if (client->nrecvs > 0)
++ if (client->nrecvs > 0) {
+ isc_socket_cancel(client->udpsocket, client->task,
+ ISC_SOCKCANCEL_RECV);
++ }
+
+ /* Still waiting for recv cancel completion. */
+- if (! (client->nrecvs == 0))
++ if (client->nrecvs > 0) {
+ return (true);
++ }
+
+ /* Still waiting for control event to be delivered */
+- if (client->nctls > 0)
++ if (client->nctls > 0) {
+ return (true);
+-
+- /* Deactivate the client. */
+- if (client->interface)
+- ns_interface_detach(&client->interface);
++ }
+
+ INSIST(client->naccepts == 0);
+ INSIST(client->recursionquota == NULL);
+- if (client->tcplistener != NULL)
++ if (client->tcplistener != NULL) {
+ isc_socket_detach(&client->tcplistener);
+-
+- if (client->udpsocket != NULL)
++ mark_tcp_active(client, false);
++ }
++ if (client->udpsocket != NULL) {
+ isc_socket_detach(&client->udpsocket);
++ }
++
++ /* Deactivate the client. */
++ if (client->interface != NULL) {
++ ns_interface_detach(&client->interface);
++ }
+
+- if (client->dispatch != NULL)
++ if (client->dispatch != NULL) {
+ dns_dispatch_detach(&client->dispatch);
++ }
+
+ client->attributes = 0;
+ client->mortal = false;
+@@ -540,10 +683,13 @@ exit_check(ns_client_t *client) {
+ client->newstate = NS_CLIENTSTATE_MAX;
+ if (!ns_g_clienttest && manager != NULL &&
+ !manager->exiting)
++ {
+ ISC_QUEUE_PUSH(manager->inactive, client,
+ ilink);
+- if (client->needshutdown)
++ }
++ if (client->needshutdown) {
+ isc_task_shutdown(client->task);
++ }
+ return (true);
+ }
+ }
+@@ -653,7 +799,7 @@ client_start(isc_task_t *task, isc_event_t *event) {
+ return;
+
+ if (TCP_CLIENT(client)) {
+- if (client->pipelined) {
++ if (client->tcpconn != NULL) {
+ client_read(client);
+ } else {
+ client_accept(client);
+@@ -663,7 +809,6 @@ client_start(isc_task_t *task, isc_event_t *event) {
+ }
+ }
+
+-
+ /*%
+ * The client's task has received a shutdown event.
+ */
+@@ -2304,6 +2449,7 @@ client_request(isc_task_t *task, isc_event_t *event) {
+ client->nrecvs--;
+ } else {
+ INSIST(TCP_CLIENT(client));
++ INSIST(client->tcpconn != NULL);
+ REQUIRE(event->ev_type == DNS_EVENT_TCPMSG);
+ REQUIRE(event->ev_sender == &client->tcpmsg);
+ buffer = &client->tcpmsg.buffer;
+@@ -2487,18 +2633,27 @@ client_request(isc_task_t *task, isc_event_t *event) {
+ /*
+ * Pipeline TCP query processing.
+ */
+- if (client->message->opcode != dns_opcode_query)
+- client->pipelined = false;
+- if (TCP_CLIENT(client) && client->pipelined) {
+- result = isc_quota_reserve(&ns_g_server->tcpquota);
+- if (result == ISC_R_SUCCESS)
+- result = ns_client_replace(client);
++ if (TCP_CLIENT(client) &&
++ client->message->opcode != dns_opcode_query)
++ {
++ client->tcpconn->pipelined = false;
++ }
++ if (TCP_CLIENT(client) && client->tcpconn->pipelined) {
++ /*
++ * We're pipelining. Replace the client; the
++ * replacement can read the TCP socket looking
++ * for new messages and this one can process the
++ * current message asynchronously.
++ *
++ * There will now be at least three clients using this
++ * TCP socket - one accepting new connections,
++ * one reading an existing connection to get new
++ * messages, and one answering the message already
++ * received.
++ */
++ result = ns_client_replace(client);
+ if (result != ISC_R_SUCCESS) {
+- ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+- NS_LOGMODULE_CLIENT, ISC_LOG_WARNING,
+- "no more TCP clients(read): %s",
+- isc_result_totext(result));
+- client->pipelined = false;
++ client->tcpconn->pipelined = false;
+ }
+ }
+
+@@ -3054,8 +3209,7 @@ client_create(ns_clientmgr_t *manager, ns_client_t **clientp) {
+ client->signer = NULL;
+ dns_name_init(&client->signername, NULL);
+ client->mortal = false;
+- client->pipelined = false;
+- client->tcpquota = NULL;
++ client->tcpconn = NULL;
+ client->recursionquota = NULL;
+ client->interface = NULL;
+ client->peeraddr_valid = false;
+@@ -3065,6 +3219,7 @@ client_create(ns_clientmgr_t *manager, ns_client_t **clientp) {
+ client->filter_aaaa = dns_aaaa_ok;
+ #endif
+ client->needshutdown = ns_g_clienttest;
++ client->tcpactive = false;
+
+ ISC_EVENT_INIT(&client->ctlevent, sizeof(client->ctlevent), 0, NULL,
+ NS_EVENT_CLIENTCONTROL, client_start, client, client,
+@@ -3159,9 +3314,10 @@ client_read(ns_client_t *client) {
+
+ static void
+ client_newconn(isc_task_t *task, isc_event_t *event) {
++ isc_result_t result;
+ ns_client_t *client = event->ev_arg;
+ isc_socket_newconnev_t *nevent = (isc_socket_newconnev_t *)event;
+- isc_result_t result;
++ uint32_t old;
+
+ REQUIRE(event->ev_type == ISC_SOCKEVENT_NEWCONN);
+ REQUIRE(NS_CLIENT_VALID(client));
+@@ -3171,13 +3327,18 @@ client_newconn(isc_task_t *task, isc_event_t *event) {
+
+ INSIST(client->state == NS_CLIENTSTATE_READY);
+
++ /*
++ * The accept() was successful and we're now establishing a new
++ * connection. We need to make note of it in the client and
++ * interface objects so client objects can do the right thing
++ * when going inactive in exit_check() (see comments in
++ * client_accept() for details).
++ */
+ INSIST(client->naccepts == 1);
+ client->naccepts--;
+
+- LOCK(&client->interface->lock);
+- INSIST(client->interface->ntcpcurrent > 0);
+- client->interface->ntcpcurrent--;
+- UNLOCK(&client->interface->lock);
++ old = isc_atomic_xadd(&client->interface->ntcpaccepting, -1);
++ INSIST(old > 0);
+
+ /*
+ * We must take ownership of the new socket before the exit
+@@ -3210,6 +3371,7 @@ client_newconn(isc_task_t *task, isc_event_t *event) {
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+ "accept failed: %s",
+ isc_result_totext(nevent->result));
++ tcpconn_detach(client);
+ }
+
+ if (exit_check(client))
+@@ -3247,20 +3409,13 @@ client_newconn(isc_task_t *task, isc_event_t *event) {
+ * telnetting to port 53 (once per CPU) will
+ * deny service to legitimate TCP clients.
+ */
+- client->pipelined = false;
+- result = isc_quota_attach(&ns_g_server->tcpquota,
+- &client->tcpquota);
+- if (result == ISC_R_SUCCESS)
+- result = ns_client_replace(client);
+- if (result != ISC_R_SUCCESS) {
+- ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+- NS_LOGMODULE_CLIENT, ISC_LOG_WARNING,
+- "no more TCP clients(accept): %s",
+- isc_result_totext(result));
+- } else if (ns_g_server->keepresporder == NULL ||
+- !allowed(&netaddr, NULL, NULL, 0, NULL,
+- ns_g_server->keepresporder)) {
+- client->pipelined = true;
++ result = ns_client_replace(client);
++ if (result == ISC_R_SUCCESS &&
++ (ns_g_server->keepresporder == NULL ||
++ !allowed(&netaddr, NULL, NULL, 0, NULL,
++ ns_g_server->keepresporder)))
++ {
++ client->tcpconn->pipelined = true;
+ }
+
+ client_read(client);
+@@ -3276,12 +3431,66 @@ client_accept(ns_client_t *client) {
+
+ CTRACE("accept");
+
++ /*
++ * Set up a new TCP connection. This means try to attach to the
++ * TCP client quota (tcp-clients), but fail if we're over quota.
++ */
++ result = tcpconn_init(client, false);
++ if (result != ISC_R_SUCCESS) {
++ bool exit;
++
++ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
++ NS_LOGMODULE_CLIENT, ISC_LOG_WARNING,
++ "TCP client quota reached: %s",
++ isc_result_totext(result));
++
++ /*
++ * We have exceeded the system-wide TCP client quota. But,
++ * we can't just block this accept in all cases, because if
++ * we did, a heavy TCP load on other interfaces might cause
++ * this interface to be starved, with no clients able to
++ * accept new connections.
++ *
++ * So, we check here to see if any other clients are
++ * already servicing TCP queries on this interface (whether
++ * accepting, reading, or processing). If we find that at
++ * least one client other than this one is active, then
++ * it's okay *not* to call accept - we can let this
++ * client go inactive and another will take over when it's
++ * done.
++ *
++ * If there aren't enough active clients on the interface,
++ * then we can be a little bit flexible about the quota.
++ * We'll allow *one* extra client through to ensure we're
++ * listening on every interface; we do this by setting the
++ * 'force' option to tcpconn_init().
++ *
++ * (Note: In practice this means that the real TCP client
++ * quota is tcp-clients plus the number of listening
++ * interfaces plus 1.)
++ */
++ exit = (isc_atomic_xadd(&client->interface->ntcpactive, 0) >
++ (client->tcpactive ? 1 : 0));
++ if (exit) {
++ client->newstate = NS_CLIENTSTATE_INACTIVE;
++ (void)exit_check(client);
++ return;
++ }
++
++ result = tcpconn_init(client, true);
++ RUNTIME_CHECK(result == ISC_R_SUCCESS);
++ }
++
++ /*
++ * If this client was set up using get_client() or get_worker(),
++ * then TCP is already marked active. However, if it was restarted
++ * from exit_check(), it might not be, so we take care of it now.
++ */
++ mark_tcp_active(client, true);
++
+ result = isc_socket_accept(client->tcplistener, client->task,
+ client_newconn, client);
+ if (result != ISC_R_SUCCESS) {
+- UNEXPECTED_ERROR(__FILE__, __LINE__,
+- "isc_socket_accept() failed: %s",
+- isc_result_totext(result));
+ /*
+ * XXXRTH What should we do? We're trying to accept but
+ * it didn't work. If we just give up, then TCP
+@@ -3289,13 +3498,37 @@ client_accept(ns_client_t *client) {
+ *
+ * For now, we just go idle.
+ */
++ UNEXPECTED_ERROR(__FILE__, __LINE__,
++ "isc_socket_accept() failed: %s",
++ isc_result_totext(result));
++
++ tcpconn_detach(client);
++ mark_tcp_active(client, false);
+ return;
+ }
++
++ /*
++ * The client's 'naccepts' counter indicates that this client has
++ * called accept() and is waiting for a new connection. It should
++ * never exceed 1.
++ */
+ INSIST(client->naccepts == 0);
+ client->naccepts++;
+- LOCK(&client->interface->lock);
+- client->interface->ntcpcurrent++;
+- UNLOCK(&client->interface->lock);
++
++ /*
++ * The interface's 'ntcpaccepting' counter is incremented when
++ * any client calls accept(), and decremented in client_newconn()
++ * once the connection is established.
++ *
++ * When the client object is shutting down after handling a TCP
++ * request (see exit_check()), if this value is at least one, that
++ * means another client has called accept() and is waiting to
++ * establish the next connection. That means the client may be
++ * be free to become inactive; otherwise it may need to start
++ * listening for connections itself to prevent the interface
++ * going dead.
++ */
++ isc_atomic_xadd(&client->interface->ntcpaccepting, 1);
+ }
+
+ static void
+@@ -3366,15 +3599,17 @@ ns_client_replace(ns_client_t *client) {
+ REQUIRE(client->manager != NULL);
+
+ tcp = TCP_CLIENT(client);
+- if (tcp && client->pipelined) {
++ if (tcp && client->tcpconn != NULL && client->tcpconn->pipelined) {
+ result = get_worker(client->manager, client->interface,
+- client->tcpsocket);
++ client->tcpsocket, client);
+ } else {
+ result = get_client(client->manager, client->interface,
+ client->dispatch, tcp);
++
+ }
+- if (result != ISC_R_SUCCESS)
++ if (result != ISC_R_SUCCESS) {
+ return (result);
++ }
+
+ /*
+ * The responsibility for listening for new requests is hereby
+@@ -3560,9 +3795,12 @@ get_client(ns_clientmgr_t *manager, ns_interface_t *ifp,
+ client->dscp = ifp->dscp;
+
+ if (tcp) {
++ mark_tcp_active(client, true);
++
+ client->attributes |= NS_CLIENTATTR_TCP;
+ isc_socket_attach(ifp->tcpsocket,
+ &client->tcplistener);
++
+ } else {
+ isc_socket_t *sock;
+
+@@ -3580,7 +3818,8 @@ get_client(ns_clientmgr_t *manager, ns_interface_t *ifp,
+ }
+
+ static isc_result_t
+-get_worker(ns_clientmgr_t *manager, ns_interface_t *ifp, isc_socket_t *sock)
++get_worker(ns_clientmgr_t *manager, ns_interface_t *ifp, isc_socket_t *sock,
++ ns_client_t *oldclient)
+ {
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_event_t *ev;
+@@ -3588,6 +3827,7 @@ get_worker(ns_clientmgr_t *manager, ns_interface_t *ifp, isc_socket_t *sock)
+ MTRACE("get worker");
+
+ REQUIRE(manager != NULL);
++ REQUIRE(oldclient != NULL);
+
+ if (manager->exiting)
+ return (ISC_R_SHUTTINGDOWN);
+@@ -3620,14 +3860,15 @@ get_worker(ns_clientmgr_t *manager, ns_interface_t *ifp, isc_socket_t *sock)
+ ns_interface_attach(ifp, &client->interface);
+ client->newstate = client->state = NS_CLIENTSTATE_WORKING;
+ INSIST(client->recursionquota == NULL);
+- client->tcpquota = &ns_g_server->tcpquota;
+
+ client->dscp = ifp->dscp;
+
+ client->attributes |= NS_CLIENTATTR_TCP;
+- client->pipelined = true;
+ client->mortal = true;
+
++ tcpconn_attach(oldclient, client);
++ mark_tcp_active(client, true);
++
+ isc_socket_attach(ifp->tcpsocket, &client->tcplistener);
+ isc_socket_attach(sock, &client->tcpsocket);
+ isc_socket_setname(client->tcpsocket, "worker-tcp", NULL);
+diff --git a/bin/named/include/named/client.h b/bin/named/include/named/client.h
+index b23a7b1..969ee4c 100644
+--- a/bin/named/include/named/client.h
++++ b/bin/named/include/named/client.h
+@@ -9,8 +9,6 @@
+ * information regarding copyright ownership.
+ */
+
+-/* $Id: client.h,v 1.96 2012/01/31 23:47:31 tbox Exp $ */
+-
+ #ifndef NAMED_CLIENT_H
+ #define NAMED_CLIENT_H 1
+
+@@ -80,6 +78,13 @@
+ *** Types
+ ***/
+
++/*% reference-counted TCP connection object */
++typedef struct ns_tcpconn {
++ isc_refcount_t refs;
++ isc_quota_t *tcpquota;
++ bool pipelined;
++} ns_tcpconn_t;
++
+ /*% nameserver client structure */
+ struct ns_client {
+ unsigned int magic;
+@@ -94,7 +99,8 @@ struct ns_client {
+ int nupdates;
+ int nctls;
+ int references;
+- bool needshutdown; /*
++ bool tcpactive;
++ bool needshutdown; /*
+ * Used by clienttest to get
+ * the client to go from
+ * inactive to free state
+@@ -130,10 +136,9 @@ struct ns_client {
+ isc_stdtime_t now;
+ isc_time_t tnow;
+ dns_name_t signername; /*%< [T]SIG key name */
+- dns_name_t * signer; /*%< NULL if not valid sig */
+- bool mortal; /*%< Die after handling request */
+- bool pipelined; /*%< TCP queries not in sequence */
+- isc_quota_t *tcpquota;
++ dns_name_t *signer; /*%< NULL if not valid sig */
++ bool mortal; /*%< Die after handling request */
++ ns_tcpconn_t *tcpconn;
+ isc_quota_t *recursionquota;
+ ns_interface_t *interface;
+
+@@ -143,8 +148,8 @@ struct ns_client {
+ isc_sockaddr_t destsockaddr;
+
+ isc_netaddr_t ecs_addr; /*%< EDNS client subnet */
+- uint8_t ecs_addrlen;
+- uint8_t ecs_scope;
++ uint8_t ecs_addrlen;
++ uint8_t ecs_scope;
+
+ struct in6_pktinfo pktinfo;
+ isc_dscp_t dscp;
+diff --git a/bin/named/include/named/interfacemgr.h b/bin/named/include/named/interfacemgr.h
+index 7d1883e..3535ef2 100644
+--- a/bin/named/include/named/interfacemgr.h
++++ b/bin/named/include/named/interfacemgr.h
+@@ -9,8 +9,6 @@
+ * information regarding copyright ownership.
+ */
+
+-/* $Id: interfacemgr.h,v 1.35 2011/07/28 23:47:58 tbox Exp $ */
+-
+ #ifndef NAMED_INTERFACEMGR_H
+ #define NAMED_INTERFACEMGR_H 1
+
+@@ -77,9 +75,14 @@ struct ns_interface {
+ /*%< UDP dispatchers. */
+ isc_socket_t * tcpsocket; /*%< TCP socket. */
+ isc_dscp_t dscp; /*%< "listen-on" DSCP value */
+- int ntcptarget; /*%< Desired number of concurrent
+- TCP accepts */
+- int ntcpcurrent; /*%< Current ditto, locked */
++ int32_t ntcpaccepting; /*%< Number of clients
++ ready to accept new
++ TCP connections on this
++ interface */
++ int32_t ntcpactive; /*%< Number of clients
++ servicing TCP queries
++ (whether accepting or
++ connected) */
+ int nudpdispatch; /*%< Number of UDP dispatches */
+ ns_clientmgr_t * clientmgr; /*%< Client manager. */
+ ISC_LINK(ns_interface_t) link;
+diff --git a/bin/named/interfacemgr.c b/bin/named/interfacemgr.c
+index 419927b..d9f6df5 100644
+--- a/bin/named/interfacemgr.c
++++ b/bin/named/interfacemgr.c
+@@ -386,8 +386,9 @@ ns_interface_create(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr,
+ * connections will be handled in parallel even though there is
+ * only one client initially.
+ */
+- ifp->ntcptarget = 1;
+- ifp->ntcpcurrent = 0;
++ ifp->ntcpaccepting = 0;
++ ifp->ntcpactive = 0;
++
+ ifp->nudpdispatch = 0;
+
+ ifp->dscp = -1;
+@@ -522,9 +523,7 @@ ns_interface_accepttcp(ns_interface_t *ifp) {
+ */
+ (void)isc_socket_filter(ifp->tcpsocket, "dataready");
+
+- result = ns_clientmgr_createclients(ifp->clientmgr,
+- ifp->ntcptarget, ifp,
+- true);
++ result = ns_clientmgr_createclients(ifp->clientmgr, 1, ifp, true);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "TCP ns_clientmgr_createclients(): %s",
+diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml
+index 719b074..4b36bd0 100644
+--- a/doc/arm/Bv9ARM-book.xml
++++ b/doc/arm/Bv9ARM-book.xml
+@@ -8487,7 +8487,8 @@ avoid-v6-udp-ports { 40000; range 50000 60000; };
+ <para>
+ The number of file descriptors reserved for TCP, stdio,
+ etc. This needs to be big enough to cover the number of
+- interfaces <command>named</command> listens on, <command>tcp-clients</command> as well as
++ interfaces <command>named</command> listens on plus
++ <command>tcp-clients</command>, as well as
+ to provide room for outgoing TCP queries and incoming zone
+ transfers. The default is <literal>512</literal>.
+ The minimum value is <literal>128</literal> and the
+diff --git a/lib/isc/include/isc/quota.h b/lib/isc/include/isc/quota.h
+index b9bf598..36c5830 100644
+--- a/lib/isc/include/isc/quota.h
++++ b/lib/isc/include/isc/quota.h
+@@ -100,6 +100,13 @@ isc_quota_attach(isc_quota_t *quota, isc_quota_t **p);
+ * quota if successful (ISC_R_SUCCESS or ISC_R_SOFTQUOTA).
+ */
+
++isc_result_t
++isc_quota_force(isc_quota_t *quota, isc_quota_t **p);
++/*%<
++ * Like isc_quota_attach, but will attach '*p' to the quota
++ * even if the hard quota has been exceeded.
++ */
++
+ void
+ isc_quota_detach(isc_quota_t **p);
+ /*%<
+diff --git a/lib/isc/quota.c b/lib/isc/quota.c
+index 3ddff0d..556a61f 100644
+--- a/lib/isc/quota.c
++++ b/lib/isc/quota.c
+@@ -74,20 +74,39 @@ isc_quota_release(isc_quota_t *quota) {
+ UNLOCK(&quota->lock);
+ }
+
+-isc_result_t
+-isc_quota_attach(isc_quota_t *quota, isc_quota_t **p)
+-{
++static isc_result_t
++doattach(isc_quota_t *quota, isc_quota_t **p, bool force) {
+ isc_result_t result;
+- INSIST(p != NULL && *p == NULL);
++ REQUIRE(p != NULL && *p == NULL);
++
+ result = isc_quota_reserve(quota);
+- if (result == ISC_R_SUCCESS || result == ISC_R_SOFTQUOTA)
++ if (result == ISC_R_SUCCESS || result == ISC_R_SOFTQUOTA) {
++ *p = quota;
++ } else if (result == ISC_R_QUOTA && force) {
++ /* attach anyway */
++ LOCK(&quota->lock);
++ quota->used++;
++ UNLOCK(&quota->lock);
++
+ *p = quota;
++ result = ISC_R_SUCCESS;
++ }
++
+ return (result);
+ }
+
++isc_result_t
++isc_quota_attach(isc_quota_t *quota, isc_quota_t **p) {
++ return (doattach(quota, p, false));
++}
++
++isc_result_t
++isc_quota_force(isc_quota_t *quota, isc_quota_t **p) {
++ return (doattach(quota, p, true));
++}
++
+ void
+-isc_quota_detach(isc_quota_t **p)
+-{
++isc_quota_detach(isc_quota_t **p) {
+ INSIST(p != NULL && *p != NULL);
+ isc_quota_release(*p);
+ *p = NULL;
+diff --git a/lib/isc/win32/libisc.def.in b/lib/isc/win32/libisc.def.in
+index a82face..7b9f23d 100644
+--- a/lib/isc/win32/libisc.def.in
++++ b/lib/isc/win32/libisc.def.in
+@@ -519,6 +519,7 @@ isc_portset_removerange
+ isc_quota_attach
+ isc_quota_destroy
+ isc_quota_detach
++isc_quota_force
+ isc_quota_init
+ isc_quota_max
+ isc_quota_release
diff --git a/debian/patches/0013-Replace-atomic-operations-in-bin-named-client.c-with.patch b/debian/patches/0013-Replace-atomic-operations-in-bin-named-client.c-with.patch
new file mode 100644
index 0000000..4ce3abe
--- /dev/null
+++ b/debian/patches/0013-Replace-atomic-operations-in-bin-named-client.c-with.patch
@@ -0,0 +1,128 @@
+From: =?utf-8?b?T25kxZllaiBTdXLDvQ==?= <ondrej@sury.org>
+Date: Wed, 17 Apr 2019 15:22:27 +0200
+Subject: Replace atomic operations in bin/named/client.c with isc_refcount
+ reference counting
+
+---
+ bin/named/client.c | 18 +++++++-----------
+ bin/named/include/named/interfacemgr.h | 5 +++--
+ bin/named/interfacemgr.c | 7 +++++--
+ 3 files changed, 15 insertions(+), 15 deletions(-)
+
+diff --git a/bin/named/client.c b/bin/named/client.c
+index 020603d..8155c6b 100644
+--- a/bin/named/client.c
++++ b/bin/named/client.c
+@@ -402,12 +402,10 @@ tcpconn_detach(ns_client_t *client) {
+ static void
+ mark_tcp_active(ns_client_t *client, bool active) {
+ if (active && !client->tcpactive) {
+- isc_atomic_xadd(&client->interface->ntcpactive, 1);
++ isc_refcount_increment0(&client->interface->ntcpactive, NULL);
+ client->tcpactive = active;
+ } else if (!active && client->tcpactive) {
+- uint32_t old =
+- isc_atomic_xadd(&client->interface->ntcpactive, -1);
+- INSIST(old > 0);
++ isc_refcount_decrement(&client->interface->ntcpactive, NULL);
+ client->tcpactive = active;
+ }
+ }
+@@ -554,7 +552,7 @@ exit_check(ns_client_t *client) {
+ if (client->mortal && TCP_CLIENT(client) &&
+ client->newstate != NS_CLIENTSTATE_FREED &&
+ !ns_g_clienttest &&
+- isc_atomic_xadd(&client->interface->ntcpaccepting, 0) == 0)
++ isc_refcount_current(&client->interface->ntcpaccepting) == 0)
+ {
+ /* Nobody else is accepting */
+ client->mortal = false;
+@@ -3317,7 +3315,6 @@ client_newconn(isc_task_t *task, isc_event_t *event) {
+ isc_result_t result;
+ ns_client_t *client = event->ev_arg;
+ isc_socket_newconnev_t *nevent = (isc_socket_newconnev_t *)event;
+- uint32_t old;
+
+ REQUIRE(event->ev_type == ISC_SOCKEVENT_NEWCONN);
+ REQUIRE(NS_CLIENT_VALID(client));
+@@ -3337,8 +3334,7 @@ client_newconn(isc_task_t *task, isc_event_t *event) {
+ INSIST(client->naccepts == 1);
+ client->naccepts--;
+
+- old = isc_atomic_xadd(&client->interface->ntcpaccepting, -1);
+- INSIST(old > 0);
++ isc_refcount_decrement(&client->interface->ntcpaccepting, NULL);
+
+ /*
+ * We must take ownership of the new socket before the exit
+@@ -3469,8 +3465,8 @@ client_accept(ns_client_t *client) {
+ * quota is tcp-clients plus the number of listening
+ * interfaces plus 1.)
+ */
+- exit = (isc_atomic_xadd(&client->interface->ntcpactive, 0) >
+- (client->tcpactive ? 1 : 0));
++ exit = (isc_refcount_current(&client->interface->ntcpactive) >
++ (client->tcpactive ? 1U : 0U));
+ if (exit) {
+ client->newstate = NS_CLIENTSTATE_INACTIVE;
+ (void)exit_check(client);
+@@ -3528,7 +3524,7 @@ client_accept(ns_client_t *client) {
+ * listening for connections itself to prevent the interface
+ * going dead.
+ */
+- isc_atomic_xadd(&client->interface->ntcpaccepting, 1);
++ isc_refcount_increment0(&client->interface->ntcpaccepting, NULL);
+ }
+
+ static void
+diff --git a/bin/named/include/named/interfacemgr.h b/bin/named/include/named/interfacemgr.h
+index 3535ef2..6e10f21 100644
+--- a/bin/named/include/named/interfacemgr.h
++++ b/bin/named/include/named/interfacemgr.h
+@@ -45,6 +45,7 @@
+ #include <isc/magic.h>
+ #include <isc/mem.h>
+ #include <isc/socket.h>
++#include <isc/refcount.h>
+
+ #include <dns/result.h>
+
+@@ -75,11 +76,11 @@ struct ns_interface {
+ /*%< UDP dispatchers. */
+ isc_socket_t * tcpsocket; /*%< TCP socket. */
+ isc_dscp_t dscp; /*%< "listen-on" DSCP value */
+- int32_t ntcpaccepting; /*%< Number of clients
++ isc_refcount_t ntcpaccepting; /*%< Number of clients
+ ready to accept new
+ TCP connections on this
+ interface */
+- int32_t ntcpactive; /*%< Number of clients
++ isc_refcount_t ntcpactive; /*%< Number of clients
+ servicing TCP queries
+ (whether accepting or
+ connected) */
+diff --git a/bin/named/interfacemgr.c b/bin/named/interfacemgr.c
+index d9f6df5..135533b 100644
+--- a/bin/named/interfacemgr.c
++++ b/bin/named/interfacemgr.c
+@@ -386,8 +386,8 @@ ns_interface_create(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr,
+ * connections will be handled in parallel even though there is
+ * only one client initially.
+ */
+- ifp->ntcpaccepting = 0;
+- ifp->ntcpactive = 0;
++ isc_refcount_init(&ifp->ntcpaccepting, 0);
++ isc_refcount_init(&ifp->ntcpactive, 0);
+
+ ifp->nudpdispatch = 0;
+
+@@ -618,6 +618,9 @@ ns_interface_destroy(ns_interface_t *ifp) {
+
+ ns_interfacemgr_detach(&ifp->mgr);
+
++ isc_refcount_destroy(&ifp->ntcpactive);
++ isc_refcount_destroy(&ifp->ntcpaccepting);
++
+ ifp->magic = 0;
+ isc_mem_put(mctx, ifp, sizeof(*ifp));
+ }
diff --git a/debian/patches/0014-Disable-broken-Ed448-support.patch b/debian/patches/0014-Disable-broken-Ed448-support.patch
new file mode 100644
index 0000000..29c8289
--- /dev/null
+++ b/debian/patches/0014-Disable-broken-Ed448-support.patch
@@ -0,0 +1,508 @@
+From: =?utf-8?b?T25kxZllaiBTdXLDvQ==?= <ondrej@debian.org>
+Date: Fri, 26 Apr 2019 07:58:26 +0000
+Subject: Disable broken Ed448 support
+
+---
+ config.h.in | 3 -
+ configure | 201 ++++++++++++++++++++++-------------------------------------
+ configure.in | 33 ----------
+ 3 files changed, 75 insertions(+), 162 deletions(-)
+
+diff --git a/config.h.in b/config.h.in
+index b6f1a28..8268259 100644
+--- a/config.h.in
++++ b/config.h.in
+@@ -387,9 +387,6 @@ int sigwait(const unsigned int *set, int *sig);
+ /* Define if your OpenSSL version supports Ed25519. */
+ #undef HAVE_OPENSSL_ED25519
+
+-/* Define if your OpenSSL version supports Ed448. */
+-#undef HAVE_OPENSSL_ED448
+-
+ /* Define if your OpenSSL version supports EVP AES */
+ #undef HAVE_OPENSSL_EVP_AES
+
+diff --git a/configure b/configure
+index 80b8eca..160e996 100755
+--- a/configure
++++ b/configure
+@@ -827,6 +827,7 @@ PKCS11_TEST
+ PKCS11_ED25519
+ PKCS11_GOST
+ PKCS11_ECDSA
++CRYPTO_PK11
+ CRYPTO
+ PKCS11LINKSRCS
+ PKCS11LINKOBJS
+@@ -865,6 +866,7 @@ THREADOPTOBJS
+ ISC_PLATFORM_USETHREADS
+ ALWAYS_DEFINES
+ CHECK_DSA
++DNS_CRYPTO_PK11_LIBS
+ DNS_CRYPTO_LIBS
+ DNS_GSSAPI_LIBS
+ DST_GSSAPI_INC
+@@ -968,6 +970,7 @@ infodir
+ docdir
+ oldincludedir
+ includedir
++runstatedir
+ localstatedir
+ sharedstatedir
+ sysconfdir
+@@ -1129,6 +1132,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}'
+@@ -1381,6 +1385,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=* \
+@@ -1518,7 +1531,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.
+@@ -1671,6 +1684,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]
+@@ -5037,7 +5051,7 @@ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+-netbsd*)
++netbsd* | netbsdelf*-gnu)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
+ else
+@@ -5943,11 +5957,8 @@ _LT_EOF
+ test $ac_status = 0; }; then
+ # Now try to grab the symbols.
+ nlist=conftest.nm
+- if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist\""; } >&5
+- (eval $NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) 2>&5
+- ac_status=$?
+- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+- test $ac_status = 0; } && test -s "$nlist"; then
++ $ECHO "$as_me:$LINENO: $NM conftest.$ac_objext | $lt_cv_sys_global_symbol_pipe > $nlist" >&5
++ if eval "$NM" conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist 2>&5 && test -s "$nlist"; then
+ # Try sorting and uniquifying the output.
+ if sort "$nlist" | uniq > "$nlist"T; then
+ mv -f "$nlist"T "$nlist"
+@@ -8772,6 +8783,9 @@ $as_echo_n "checking whether the $compiler linker ($LD) supports shared librarie
+ openbsd* | bitrig*)
+ with_gnu_ld=no
+ ;;
++ linux* | k*bsd*-gnu | gnu*)
++ link_all_deplibs=no
++ ;;
+ esac
+
+ ld_shlibs=yes
+@@ -9026,7 +9040,7 @@ _LT_EOF
+ fi
+ ;;
+
+- netbsd*)
++ netbsd* | netbsdelf*-gnu)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
+ wlarc=
+@@ -9696,6 +9710,7 @@ $as_echo "$lt_cv_irix_exported_symbol" >&6; }
+ if test yes = "$lt_cv_irix_exported_symbol"; then
+ archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib'
+ fi
++ link_all_deplibs=no
+ else
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib'
+@@ -9717,7 +9732,7 @@ $as_echo "$lt_cv_irix_exported_symbol" >&6; }
+ esac
+ ;;
+
+- netbsd*)
++ netbsd* | netbsdelf*-gnu)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out
+ else
+@@ -10832,6 +10847,18 @@ fi
+ dynamic_linker='GNU/Linux ld.so'
+ ;;
+
++netbsdelf*-gnu)
++ version_type=linux
++ need_lib_prefix=no
++ need_version=no
++ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
++ soname_spec='${libname}${release}${shared_ext}$major'
++ shlibpath_var=LD_LIBRARY_PATH
++ shlibpath_overrides_runpath=no
++ hardcode_into_libs=yes
++ dynamic_linker='NetBSD ld.elf_so'
++ ;;
++
+ netbsd*)
+ version_type=sunos
+ need_lib_prefix=no
+@@ -13427,7 +13454,7 @@ case "$host" in
+ # as it breaks how the two halves (Basic and Advanced) of the IPv6
+ # Socket API were designed to be used but we have to live with it.
+ # Define _GNU_SOURCE to pull in the IPv6 Advanced Socket API.
+- *-linux* | *-kfreebsd*-gnu*)
++ *-linux* | *-kfreebsd*-gnu* | *-gnu*)
+ STD_CDEFINES="$STD_CDEFINES -D_GNU_SOURCE"
+ CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE"
+ ;;
+@@ -15227,6 +15254,7 @@ esac
+
+
+ DNS_CRYPTO_LIBS="$DNS_GSSAPI_LIBS $DNS_CRYPTO_LIBS"
++DNS_CRYPTO_PK11_LIBS="$DNS_GSSAPI_LIBS $DNS_CRYPTO_PK11_LIBS"
+
+ #
+ # Applications linking with libdns also need to link with these libraries.
+@@ -15234,6 +15262,7 @@ DNS_CRYPTO_LIBS="$DNS_GSSAPI_LIBS $DNS_CRYPTO_LIBS"
+
+
+
++
+ #
+ # was --with-randomdev specified?
+ #
+@@ -16330,7 +16359,7 @@ fi
+ # LinuxThreads requires some changes to the way we
+ # deal with signals.
+ #
+- *-linux*)
++ *-linux*|*-kfreebsd*-gnu)
+ $as_echo "#define HAVE_LINUXTHREADS 1" >>confdefs.h
+
+ ;;
+@@ -16585,12 +16614,6 @@ fi
+ $as_echo_n "checking for OpenSSL library... " >&6; }
+ OPENSSL_WARNING=
+ openssldirs="/usr /usr/local /usr/local/ssl /usr/pkg /usr/sfw"
+-if test "yes" = "$want_native_pkcs11"
+-then
+- use_openssl="native_pkcs11"
+- { $as_echo "$as_me:${as_lineno-$LINENO}: result: use of native PKCS11 instead" >&5
+-$as_echo "use of native PKCS11 instead" >&6; }
+-fi
+
+ if test "auto" = "$use_openssl"
+ then
+@@ -16603,6 +16626,7 @@ then
+ fi
+ done
+ fi
++CRYPTO_PK11=""
+ OPENSSL_ECDSA=""
+ OPENSSL_GOST=""
+ OPENSSL_ED25519=""
+@@ -16625,12 +16649,10 @@ $as_echo "#define PREFER_GOSTASN1 1" >>confdefs.h
+ ;;
+ esac
+
+-case "$use_openssl" in
+- native_pkcs11)
+- { $as_echo "$as_me:${as_lineno-$LINENO}: result: disabled because of native PKCS11" >&5
+-$as_echo "disabled because of native PKCS11" >&6; }
++if test "$want_native_pkcs11" = "yes"
++then
+ DST_OPENSSL_INC=""
+- CRYPTO="-DPKCS11CRYPTO"
++ CRYPTO_PK11="-DPKCS11CRYPTO"
+ OPENSSLECDSALINKOBJS=""
+ OPENSSLECDSALINKSRCS=""
+ OPENSSLEDDSALINKOBJS=""
+@@ -16639,7 +16661,9 @@ $as_echo "disabled because of native PKCS11" >&6; }
+ OPENSSLGOSTLINKSRCS=""
+ OPENSSLLINKOBJS=""
+ OPENSSLLINKSRCS=""
+- ;;
++fi
++
++case "$use_openssl" in
+ no)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+ $as_echo "no" >&6; }
+@@ -16669,12 +16693,6 @@ $as_echo "no" >&6; }
+ If you don't want OpenSSL, use --without-openssl" "$LINENO" 5
+ ;;
+ *)
+- if test "yes" = "$want_native_pkcs11"
+- then
+- { $as_echo "$as_me:${as_lineno-$LINENO}: result: " >&5
+-$as_echo "" >&6; }
+- as_fn_error $? "OpenSSL and native PKCS11 cannot be used together." "$LINENO" 5
+- fi
+ if test "yes" = "$use_openssl"
+ then
+ # User did not specify a path - guess it
+@@ -17126,60 +17144,10 @@ fi
+
+ $as_echo "#define HAVE_OPENSSL_ED25519 1" >>confdefs.h
+
+- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for OpenSSL Ed448 support" >&5
+-$as_echo_n "checking for OpenSSL Ed448 support... " >&6; }
+- if test "$cross_compiling" = yes; then :
+- { $as_echo "$as_me:${as_lineno-$LINENO}: result: using --with-eddsa" >&5
+-$as_echo "using --with-eddsa" >&6; }
+-else
+- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+-/* end confdefs.h. */
+-
+-#include <openssl/evp.h>
+-#include <openssl/objects.h>
+-int main() {
+- EVP_PKEY_CTX *ctx;
+-
+- ctx = EVP_PKEY_CTX_new_id(NID_ED448, NULL);
+- if (ctx == NULL)
+- return (2);
+- return (0);
+-}
+-
+-_ACEOF
+-if ac_fn_c_try_run "$LINENO"; then :
+- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+-$as_echo "yes" >&6; }
+- have_ed448="yes"
+-else
+- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+-$as_echo "no" >&6; }
+- have_ed448="no"
+-fi
+-rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+- conftest.$ac_objext conftest.beam conftest.$ac_ext
+-fi
+-
+- case $with_eddsa in
+- all)
+- have_ed448=yes ;;
+- *)
+- ;;
+- esac
+- case $have_ed448 in
+- yes)
+-
+-$as_echo "#define HAVE_OPENSSL_ED448 1" >>confdefs.h
+-
+- ;;
+- *)
+- ;;
+- esac
+ ;;
+ *)
+ ;;
+ esac
+-
+ have_aes="no"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for OpenSSL AES support" >&5
+ $as_echo_n "checking for OpenSSL AES support... " >&6; }
+@@ -17278,6 +17246,7 @@ esac
+
+
+ DNS_CRYPTO_LIBS="$DNS_CRYPTO_LIBS $DST_OPENSSL_LIBS"
++DNS_CRYPTO_PK11_LIBS="$DNS_CRYPTO_LIBS"
+
+ ISC_PLATFORM_WANTAES="#undef ISC_PLATFORM_WANTAES"
+ if test "yes" = "$with_aes"
+@@ -17667,6 +17636,7 @@ esac
+
+
+
++
+ # for PKCS11 benchmarks
+
+ have_clock_gt=no
+@@ -18526,51 +18496,6 @@ _ACEOF
+
+ LIBS="-lsocket $LIBS"
+
+-fi
+-
+- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for inet_addr in -lnsl" >&5
+-$as_echo_n "checking for inet_addr in -lnsl... " >&6; }
+-if ${ac_cv_lib_nsl_inet_addr+:} false; then :
+- $as_echo_n "(cached) " >&6
+-else
+- ac_check_lib_save_LIBS=$LIBS
+-LIBS="-lnsl $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 inet_addr ();
+-int
+-main ()
+-{
+-return inet_addr ();
+- ;
+- return 0;
+-}
+-_ACEOF
+-if ac_fn_c_try_link "$LINENO"; then :
+- ac_cv_lib_nsl_inet_addr=yes
+-else
+- ac_cv_lib_nsl_inet_addr=no
+-fi
+-rm -f core conftest.err conftest.$ac_objext \
+- conftest$ac_exeext conftest.$ac_ext
+-LIBS=$ac_check_lib_save_LIBS
+-fi
+-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_nsl_inet_addr" >&5
+-$as_echo "$ac_cv_lib_nsl_inet_addr" >&6; }
+-if test "x$ac_cv_lib_nsl_inet_addr" = xyes; then :
+- cat >>confdefs.h <<_ACEOF
+-#define HAVE_LIBNSL 1
+-_ACEOF
+-
+- LIBS="-lnsl $LIBS"
+-
+ fi
+
+ ;;
+@@ -24555,7 +24480,7 @@ ac_config_commands="$ac_config_commands chmod"
+ # elsewhere if there's a good reason for doing so.
+ #
+
+-ac_config_files="$ac_config_files make/Makefile make/mkdep Makefile bin/Makefile bin/check/Makefile bin/confgen/Makefile bin/confgen/unix/Makefile bin/delv/Makefile bin/dig/Makefile bin/dnssec/Makefile bin/named/Makefile bin/named/unix/Makefile bin/nsupdate/Makefile bin/pkcs11/Makefile bin/python/Makefile bin/python/isc/Makefile bin/python/isc/utils.py bin/python/isc/tests/Makefile bin/python/dnssec-checkds.py bin/python/dnssec-coverage.py bin/python/dnssec-keymgr.py bin/python/isc/__init__.py bin/python/isc/checkds.py bin/python/isc/coverage.py bin/python/isc/dnskey.py bin/python/isc/eventlist.py bin/python/isc/keydict.py bin/python/isc/keyevent.py bin/python/isc/keymgr.py bin/python/isc/keyseries.py bin/python/isc/keyzone.py bin/python/isc/policy.py bin/python/isc/rndc.py bin/python/isc/tests/dnskey_test.py bin/python/isc/tests/policy_test.py bin/rndc/Makefile bin/tests/Makefile bin/tests/headerdep_test.sh bin/tests/optional/Makefile bin/tests/pkcs11/Makefile bin/tests/pkcs11/benchmarks/Makefile bin/tests/system/Makefile bin/tests/system/conf.sh bin/tests/system/dlz/prereq.sh bin/tests/system/dlzexternal/Makefile bin/tests/system/dlzexternal/ns1/dlzs.conf bin/tests/system/dyndb/Makefile bin/tests/system/dyndb/driver/Makefile bin/tests/system/inline/checkdsa.sh bin/tests/system/lwresd/Makefile bin/tests/system/pipelined/Makefile bin/tests/system/rndc/Makefile bin/tests/system/rsabigexponent/Makefile bin/tests/system/tkey/Makefile bin/tests/virtual-time/Makefile bin/tests/virtual-time/conf.sh bin/tools/Makefile contrib/scripts/check-secure-delegation.pl contrib/scripts/zone-edit.sh doc/Makefile doc/arm/Makefile doc/arm/noteversion.xml doc/arm/pkgversion.xml doc/arm/releaseinfo.xml doc/doxygen/Doxyfile doc/doxygen/Makefile doc/doxygen/doxygen-input-filter doc/misc/Makefile doc/tex/Makefile doc/tex/armstyle.sty doc/xsl/Makefile doc/xsl/isc-docbook-chunk.xsl doc/xsl/isc-docbook-html.xsl doc/xsl/isc-manpage.xsl doc/xsl/isc-notes-html.xsl isc-config.sh lib/Makefile lib/bind9/Makefile lib/bind9/include/Makefile lib/bind9/include/bind9/Makefile lib/dns/Makefile lib/dns/include/Makefile lib/dns/include/dns/Makefile lib/dns/include/dst/Makefile lib/dns/tests/Makefile lib/irs/Makefile lib/irs/include/Makefile lib/irs/include/irs/Makefile lib/irs/include/irs/netdb.h lib/irs/include/irs/platform.h lib/irs/tests/Makefile lib/isc/$arch/Makefile lib/isc/$arch/include/Makefile lib/isc/$arch/include/isc/Makefile lib/isc/$thread_dir/Makefile lib/isc/$thread_dir/include/Makefile lib/isc/$thread_dir/include/isc/Makefile lib/isc/Makefile lib/isc/include/Makefile lib/isc/include/isc/Makefile lib/isc/include/isc/platform.h lib/isc/include/pk11/Makefile lib/isc/include/pkcs11/Makefile lib/isc/tests/Makefile lib/isc/nls/Makefile lib/isc/unix/Makefile lib/isc/unix/include/Makefile lib/isc/unix/include/isc/Makefile lib/isc/unix/include/pkcs11/Makefile lib/isccc/Makefile lib/isccc/include/Makefile lib/isccc/include/isccc/Makefile lib/isccfg/Makefile lib/isccfg/include/Makefile lib/isccfg/include/isccfg/Makefile lib/isccfg/tests/Makefile lib/lwres/Makefile lib/lwres/include/Makefile lib/lwres/include/lwres/Makefile lib/lwres/include/lwres/netdb.h lib/lwres/include/lwres/platform.h lib/lwres/man/Makefile lib/lwres/tests/Makefile lib/lwres/unix/Makefile lib/lwres/unix/include/Makefile lib/lwres/unix/include/lwres/Makefile lib/samples/Makefile lib/samples/Makefile-postinstall unit/Makefile unit/unittest.sh"
++ac_config_files="$ac_config_files make/Makefile make/mkdep Makefile bin/Makefile bin/check/Makefile bin/confgen/Makefile bin/confgen/unix/Makefile bin/delv/Makefile bin/dig/Makefile bin/dnssec/Makefile bin/dnssec-pkcs11/Makefile bin/named/Makefile bin/named/unix/Makefile bin/named-pkcs11/Makefile bin/named-pkcs11/unix/Makefile bin/nsupdate/Makefile bin/pkcs11/Makefile bin/python/Makefile bin/python/isc/Makefile bin/python/isc/utils.py bin/python/isc/tests/Makefile bin/python/dnssec-checkds.py bin/python/dnssec-coverage.py bin/python/dnssec-keymgr.py bin/python/isc/__init__.py bin/python/isc/checkds.py bin/python/isc/coverage.py bin/python/isc/dnskey.py bin/python/isc/eventlist.py bin/python/isc/keydict.py bin/python/isc/keyevent.py bin/python/isc/keymgr.py bin/python/isc/keyseries.py bin/python/isc/keyzone.py bin/python/isc/policy.py bin/python/isc/rndc.py bin/python/isc/tests/dnskey_test.py bin/python/isc/tests/policy_test.py bin/rndc/Makefile bin/tests/Makefile bin/tests/headerdep_test.sh bin/tests/optional/Makefile bin/tests/pkcs11/Makefile bin/tests/pkcs11/benchmarks/Makefile bin/tests/system/Makefile bin/tests/system/conf.sh bin/tests/system/dlz/prereq.sh bin/tests/system/dlzexternal/Makefile bin/tests/system/dlzexternal/ns1/dlzs.conf bin/tests/system/dyndb/Makefile bin/tests/system/dyndb/driver/Makefile bin/tests/system/inline/checkdsa.sh bin/tests/system/lwresd/Makefile bin/tests/system/pipelined/Makefile bin/tests/system/rndc/Makefile bin/tests/system/rsabigexponent/Makefile bin/tests/system/tkey/Makefile bin/tests/virtual-time/Makefile bin/tests/virtual-time/conf.sh bin/tools/Makefile contrib/scripts/check-secure-delegation.pl contrib/scripts/zone-edit.sh doc/Makefile doc/arm/Makefile doc/arm/noteversion.xml doc/arm/pkgversion.xml doc/arm/releaseinfo.xml doc/doxygen/Doxyfile doc/doxygen/Makefile doc/doxygen/doxygen-input-filter doc/misc/Makefile doc/tex/Makefile doc/tex/armstyle.sty doc/xsl/Makefile doc/xsl/isc-docbook-chunk.xsl doc/xsl/isc-docbook-html.xsl doc/xsl/isc-manpage.xsl doc/xsl/isc-notes-html.xsl isc-config.sh lib/Makefile lib/bind9/Makefile lib/bind9/include/Makefile lib/bind9/include/bind9/Makefile lib/dns/Makefile lib/dns/include/Makefile lib/dns/include/dns/Makefile lib/dns/include/dst/Makefile lib/dns/tests/Makefile lib/dns-pkcs11/Makefile lib/dns-pkcs11/include/Makefile lib/dns-pkcs11/include/dns/Makefile lib/dns-pkcs11/include/dst/Makefile lib/irs/Makefile lib/irs/include/Makefile lib/irs/include/irs/Makefile lib/irs/include/irs/netdb.h lib/irs/include/irs/platform.h lib/irs/tests/Makefile lib/isc/$arch/Makefile lib/isc/$arch/include/Makefile lib/isc/$arch/include/isc/Makefile lib/isc/$thread_dir/Makefile lib/isc/$thread_dir/include/Makefile lib/isc/$thread_dir/include/isc/Makefile lib/isc/Makefile lib/isc/include/Makefile lib/isc/include/isc/Makefile lib/isc/include/isc/platform.h lib/isc/include/pk11/Makefile lib/isc/include/pkcs11/Makefile lib/isc/tests/Makefile lib/isc/nls/Makefile lib/isc/unix/Makefile lib/isc/unix/include/Makefile lib/isc/unix/include/isc/Makefile lib/isc/unix/include/pkcs11/Makefile lib/isc-pkcs11/$arch/Makefile lib/isc-pkcs11/$arch/include/Makefile lib/isc-pkcs11/$arch/include/isc/Makefile lib/isc-pkcs11/$thread_dir/Makefile lib/isc-pkcs11/$thread_dir/include/Makefile lib/isc-pkcs11/$thread_dir/include/isc/Makefile lib/isc-pkcs11/Makefile lib/isc-pkcs11/include/Makefile lib/isc-pkcs11/include/isc/Makefile lib/isc-pkcs11/include/isc/platform.h lib/isc-pkcs11/include/pk11/Makefile lib/isc-pkcs11/include/pkcs11/Makefile lib/isc-pkcs11/tests/Makefile lib/isc-pkcs11/nls/Makefile lib/isc-pkcs11/unix/Makefile lib/isc-pkcs11/unix/include/Makefile lib/isc-pkcs11/unix/include/isc/Makefile lib/isc-pkcs11/unix/include/pkcs11/Makefile lib/isccc/Makefile lib/isccc/include/Makefile lib/isccc/include/isccc/Makefile lib/isccfg/Makefile lib/isccfg/include/Makefile lib/isccfg/include/isccfg/Makefile lib/isccfg/tests/Makefile lib/lwres/Makefile lib/lwres/include/Makefile lib/lwres/include/lwres/Makefile lib/lwres/include/lwres/netdb.h lib/lwres/include/lwres/platform.h lib/lwres/man/Makefile lib/lwres/tests/Makefile lib/lwres/unix/Makefile lib/lwres/unix/include/Makefile lib/lwres/unix/include/lwres/Makefile lib/samples/Makefile lib/samples/Makefile-postinstall unit/Makefile unit/unittest.sh"
+
+
+ #
+@@ -25567,8 +25492,11 @@ do
+ "bin/delv/Makefile") CONFIG_FILES="$CONFIG_FILES bin/delv/Makefile" ;;
+ "bin/dig/Makefile") CONFIG_FILES="$CONFIG_FILES bin/dig/Makefile" ;;
+ "bin/dnssec/Makefile") CONFIG_FILES="$CONFIG_FILES bin/dnssec/Makefile" ;;
++ "bin/dnssec-pkcs11/Makefile") CONFIG_FILES="$CONFIG_FILES bin/dnssec-pkcs11/Makefile" ;;
+ "bin/named/Makefile") CONFIG_FILES="$CONFIG_FILES bin/named/Makefile" ;;
+ "bin/named/unix/Makefile") CONFIG_FILES="$CONFIG_FILES bin/named/unix/Makefile" ;;
++ "bin/named-pkcs11/Makefile") CONFIG_FILES="$CONFIG_FILES bin/named-pkcs11/Makefile" ;;
++ "bin/named-pkcs11/unix/Makefile") CONFIG_FILES="$CONFIG_FILES bin/named-pkcs11/unix/Makefile" ;;
+ "bin/nsupdate/Makefile") CONFIG_FILES="$CONFIG_FILES bin/nsupdate/Makefile" ;;
+ "bin/pkcs11/Makefile") CONFIG_FILES="$CONFIG_FILES bin/pkcs11/Makefile" ;;
+ "bin/python/Makefile") CONFIG_FILES="$CONFIG_FILES bin/python/Makefile" ;;
+@@ -25642,6 +25570,10 @@ do
+ "lib/dns/include/dns/Makefile") CONFIG_FILES="$CONFIG_FILES lib/dns/include/dns/Makefile" ;;
+ "lib/dns/include/dst/Makefile") CONFIG_FILES="$CONFIG_FILES lib/dns/include/dst/Makefile" ;;
+ "lib/dns/tests/Makefile") CONFIG_FILES="$CONFIG_FILES lib/dns/tests/Makefile" ;;
++ "lib/dns-pkcs11/Makefile") CONFIG_FILES="$CONFIG_FILES lib/dns-pkcs11/Makefile" ;;
++ "lib/dns-pkcs11/include/Makefile") CONFIG_FILES="$CONFIG_FILES lib/dns-pkcs11/include/Makefile" ;;
++ "lib/dns-pkcs11/include/dns/Makefile") CONFIG_FILES="$CONFIG_FILES lib/dns-pkcs11/include/dns/Makefile" ;;
++ "lib/dns-pkcs11/include/dst/Makefile") CONFIG_FILES="$CONFIG_FILES lib/dns-pkcs11/include/dst/Makefile" ;;
+ "lib/irs/Makefile") CONFIG_FILES="$CONFIG_FILES lib/irs/Makefile" ;;
+ "lib/irs/include/Makefile") CONFIG_FILES="$CONFIG_FILES lib/irs/include/Makefile" ;;
+ "lib/irs/include/irs/Makefile") CONFIG_FILES="$CONFIG_FILES lib/irs/include/irs/Makefile" ;;
+@@ -25666,6 +25598,24 @@ do
+ "lib/isc/unix/include/Makefile") CONFIG_FILES="$CONFIG_FILES lib/isc/unix/include/Makefile" ;;
+ "lib/isc/unix/include/isc/Makefile") CONFIG_FILES="$CONFIG_FILES lib/isc/unix/include/isc/Makefile" ;;
+ "lib/isc/unix/include/pkcs11/Makefile") CONFIG_FILES="$CONFIG_FILES lib/isc/unix/include/pkcs11/Makefile" ;;
++ "lib/isc-pkcs11/$arch/Makefile") CONFIG_FILES="$CONFIG_FILES lib/isc-pkcs11/$arch/Makefile" ;;
++ "lib/isc-pkcs11/$arch/include/Makefile") CONFIG_FILES="$CONFIG_FILES lib/isc-pkcs11/$arch/include/Makefile" ;;
++ "lib/isc-pkcs11/$arch/include/isc/Makefile") CONFIG_FILES="$CONFIG_FILES lib/isc-pkcs11/$arch/include/isc/Makefile" ;;
++ "lib/isc-pkcs11/$thread_dir/Makefile") CONFIG_FILES="$CONFIG_FILES lib/isc-pkcs11/$thread_dir/Makefile" ;;
++ "lib/isc-pkcs11/$thread_dir/include/Makefile") CONFIG_FILES="$CONFIG_FILES lib/isc-pkcs11/$thread_dir/include/Makefile" ;;
++ "lib/isc-pkcs11/$thread_dir/include/isc/Makefile") CONFIG_FILES="$CONFIG_FILES lib/isc-pkcs11/$thread_dir/include/isc/Makefile" ;;
++ "lib/isc-pkcs11/Makefile") CONFIG_FILES="$CONFIG_FILES lib/isc-pkcs11/Makefile" ;;
++ "lib/isc-pkcs11/include/Makefile") CONFIG_FILES="$CONFIG_FILES lib/isc-pkcs11/include/Makefile" ;;
++ "lib/isc-pkcs11/include/isc/Makefile") CONFIG_FILES="$CONFIG_FILES lib/isc-pkcs11/include/isc/Makefile" ;;
++ "lib/isc-pkcs11/include/isc/platform.h") CONFIG_FILES="$CONFIG_FILES lib/isc-pkcs11/include/isc/platform.h" ;;
++ "lib/isc-pkcs11/include/pk11/Makefile") CONFIG_FILES="$CONFIG_FILES lib/isc-pkcs11/include/pk11/Makefile" ;;
++ "lib/isc-pkcs11/include/pkcs11/Makefile") CONFIG_FILES="$CONFIG_FILES lib/isc-pkcs11/include/pkcs11/Makefile" ;;
++ "lib/isc-pkcs11/tests/Makefile") CONFIG_FILES="$CONFIG_FILES lib/isc-pkcs11/tests/Makefile" ;;
++ "lib/isc-pkcs11/nls/Makefile") CONFIG_FILES="$CONFIG_FILES lib/isc-pkcs11/nls/Makefile" ;;
++ "lib/isc-pkcs11/unix/Makefile") CONFIG_FILES="$CONFIG_FILES lib/isc-pkcs11/unix/Makefile" ;;
++ "lib/isc-pkcs11/unix/include/Makefile") CONFIG_FILES="$CONFIG_FILES lib/isc-pkcs11/unix/include/Makefile" ;;
++ "lib/isc-pkcs11/unix/include/isc/Makefile") CONFIG_FILES="$CONFIG_FILES lib/isc-pkcs11/unix/include/isc/Makefile" ;;
++ "lib/isc-pkcs11/unix/include/pkcs11/Makefile") CONFIG_FILES="$CONFIG_FILES lib/isc-pkcs11/unix/include/pkcs11/Makefile" ;;
+ "lib/isccc/Makefile") CONFIG_FILES="$CONFIG_FILES lib/isccc/Makefile" ;;
+ "lib/isccc/include/Makefile") CONFIG_FILES="$CONFIG_FILES lib/isccc/include/Makefile" ;;
+ "lib/isccc/include/isccc/Makefile") CONFIG_FILES="$CONFIG_FILES lib/isccc/include/isccc/Makefile" ;;
+@@ -26296,7 +26246,6 @@ $as_echo "$as_me: executing $ac_file commands" >&6;}
+ cat <<_LT_EOF >> "$cfgfile"
+ #! $SHELL
+ # Generated automatically by $as_me ($PACKAGE) $VERSION
+-# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+ # NOTE: Changes made to this file will be lost: look at ltmain.sh.
+
+ # Provide generalized library-building support services.
+diff --git a/configure.in b/configure.in
+index f8603b8..324ac97 100644
+--- a/configure.in
++++ b/configure.in
+@@ -1889,43 +1889,10 @@ int main() {
+ OPENSSLEDDSALINKSRCS='${OPENSSLEDDSALINKSRCS}'
+ AC_DEFINE(HAVE_OPENSSL_ED25519, 1,
+ [Define if your OpenSSL version supports Ed25519.])
+- AC_MSG_CHECKING(for OpenSSL Ed448 support)
+- AC_TRY_RUN([
+-#include <openssl/evp.h>
+-#include <openssl/objects.h>
+-int main() {
+- EVP_PKEY_CTX *ctx;
+-
+- ctx = EVP_PKEY_CTX_new_id(NID_ED448, NULL);
+- if (ctx == NULL)
+- return (2);
+- return (0);
+-}
+-],
+- [AC_MSG_RESULT(yes)
+- have_ed448="yes"],
+- [AC_MSG_RESULT(no)
+- have_ed448="no"],
+- [AC_MSG_RESULT(using --with-eddsa)])
+- case $with_eddsa in
+- all)
+- have_ed448=yes ;;
+- *)
+- ;;
+- esac
+- case $have_ed448 in
+- yes)
+- AC_DEFINE(HAVE_OPENSSL_ED448, 1,
+- [Define if your OpenSSL version supports Ed448.])
+- ;;
+- *)
+- ;;
+- esac
+ ;;
+ *)
+ ;;
+ esac
+-
+ have_aes="no"
+ AC_MSG_CHECKING(for OpenSSL AES support)
+ AC_TRY_RUN([
diff --git a/debian/patches/0015-move-item_out-test-inside-lock-in-dns_dispatch_getne.patch b/debian/patches/0015-move-item_out-test-inside-lock-in-dns_dispatch_getne.patch
new file mode 100644
index 0000000..5b665a9
--- /dev/null
+++ b/debian/patches/0015-move-item_out-test-inside-lock-in-dns_dispatch_getne.patch
@@ -0,0 +1,54 @@
+From: Mark Andrews <marka@isc.org>
+Date: Tue, 19 Mar 2019 14:14:21 +1100
+Subject: move item_out test inside lock in dns_dispatch_getnext()
+
+Origin: https://gitlab.isc.org/isc-projects/bind9/commit/3a9c7bb80d4a609b86427406d9dd783199920b5b
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2019-6471
+Bug-Debian: https://bugs.debian.org/930746
+
+(cherry picked from commit 60c42f849d520564ed42e5ed0ba46b4b69c07712)
+---
+ lib/dns/dispatch.c | 12 ++++++++----
+ 1 file changed, 8 insertions(+), 4 deletions(-)
+
+diff --git a/lib/dns/dispatch.c b/lib/dns/dispatch.c
+index d1e4c16..b5a8f74 100644
+--- a/lib/dns/dispatch.c
++++ b/lib/dns/dispatch.c
+@@ -134,7 +134,7 @@ struct dns_dispentry {
+ isc_task_t *task;
+ isc_taskaction_t action;
+ void *arg;
+- bool item_out;
++ bool item_out;
+ dispsocket_t *dispsocket;
+ ISC_LIST(dns_dispatchevent_t) items;
+ ISC_LINK(dns_dispentry_t) link;
+@@ -3422,13 +3422,14 @@ dns_dispatch_getnext(dns_dispentry_t *resp, dns_dispatchevent_t **sockevent) {
+ disp = resp->disp;
+ REQUIRE(VALID_DISPATCH(disp));
+
+- REQUIRE(resp->item_out == true);
+- resp->item_out = false;
+-
+ ev = *sockevent;
+ *sockevent = NULL;
+
+ LOCK(&disp->lock);
++
++ REQUIRE(resp->item_out == true);
++ resp->item_out = false;
++
+ if (ev->buffer.base != NULL)
+ free_buffer(disp, ev->buffer.base, ev->buffer.length);
+ free_devent(disp, ev);
+@@ -3573,6 +3574,9 @@ dns_dispatch_removeresponse(dns_dispentry_t **resp,
+ isc_task_send(disp->task[0], &disp->ctlevent);
+ }
+
++/*
++ * disp must be locked.
++ */
+ static void
+ do_cancel(dns_dispatch_t *disp) {
+ dns_dispatchevent_t *ev;
diff --git a/debian/patches/0016-Set-a-limit-on-number-of-simultaneous-pipelined-TCP-.patch b/debian/patches/0016-Set-a-limit-on-number-of-simultaneous-pipelined-TCP-.patch
new file mode 100644
index 0000000..a57296a
--- /dev/null
+++ b/debian/patches/0016-Set-a-limit-on-number-of-simultaneous-pipelined-TCP-.patch
@@ -0,0 +1,117 @@
+From: =?utf-8?q?Witold_Kr=C4=99cicki?= <wpk@isc.org>
+Date: Wed, 16 Oct 2019 13:18:48 +0200
+Subject: Set a limit on number of simultaneous pipelined TCP queries
+
+There was no limit on concurrently served queries served over one pipelined TCP
+connection, thus it was possible to send thousands queries over a single TCP
+connection, possibly exhausting the server resources.
+
+(cherry picked from commit efaa67749de825073cd7f19778386d0815c4ce29)
+---
+ bin/named/client.c | 57 ++++++++++++++++++++++++++--------------
+ bin/named/include/named/client.h | 5 +++-
+ 2 files changed, 42 insertions(+), 20 deletions(-)
+
+diff --git a/bin/named/client.c b/bin/named/client.c
+index 8155c6b..30877c5 100644
+--- a/bin/named/client.c
++++ b/bin/named/client.c
+@@ -101,7 +101,15 @@
+ #define SEND_BUFFER_SIZE 4096
+ #define RECV_BUFFER_SIZE 4096
+
++#define TCP_CLIENTS_PER_CONN 23
++/*%<
++ * Number of simultaneous ns_clients_t (queries in flight) for one
++ * TCP connection. The number was arbitrarily picked and might be
++ * changed in the future.
++ */
++
+ #ifdef ISC_PLATFORM_USETHREADS
++
+ #define NMCTXS 100
+ /*%<
+ * Number of 'mctx pools' for clients. (Should this be configurable?)
+@@ -337,7 +345,7 @@ tcpconn_init(ns_client_t *client, bool force) {
+ */
+ tconn = isc_mem_allocate(ns_g_mctx, sizeof(*tconn));
+
+- isc_refcount_init(&tconn->refs, 1);
++ isc_refcount_init(&tconn->refs, 1); /* Current client */
+ tconn->tcpquota = quota;
+ quota = NULL;
+ tconn->pipelined = false;
+@@ -2631,28 +2639,39 @@ client_request(isc_task_t *task, isc_event_t *event) {
+ /*
+ * Pipeline TCP query processing.
+ */
+- if (TCP_CLIENT(client) &&
+- client->message->opcode != dns_opcode_query)
+- {
+- client->tcpconn->pipelined = false;
+- }
+- if (TCP_CLIENT(client) && client->tcpconn->pipelined) {
++ if (TCP_CLIENT(client)) {
++ if (client->message->opcode != dns_opcode_query) {
++ client->tcpconn->pipelined = false;
++ }
++
+ /*
+- * We're pipelining. Replace the client; the
+- * replacement can read the TCP socket looking
+- * for new messages and this one can process the
+- * current message asynchronously.
+- *
+- * There will now be at least three clients using this
+- * TCP socket - one accepting new connections,
+- * one reading an existing connection to get new
+- * messages, and one answering the message already
+- * received.
++ * Limit the maximum number of simultaenous pipelined
++ * queries on TCP connection to TCP_CLIENTS_PER_CONN.
+ */
+- result = ns_client_replace(client);
+- if (result != ISC_R_SUCCESS) {
++ if ((isc_refcount_current(&client->tcpconn->refs)
++ > TCP_CLIENTS_PER_CONN))
++ {
+ client->tcpconn->pipelined = false;
+ }
++
++ if (client->tcpconn->pipelined) {
++ /*
++ * We're pipelining. Replace the client; the
++ * replacement can read the TCP socket looking
++ * for new messages and this one can process the
++ * current message asynchronously.
++ *
++ * There will now be at least three clients using this
++ * TCP socket - one accepting new connections,
++ * one reading an existing connection to get new
++ * messages, and one answering the message already
++ * received.
++ */
++ result = ns_client_replace(client);
++ if (result != ISC_R_SUCCESS) {
++ client->tcpconn->pipelined = false;
++ }
++ }
+ }
+
+ dns_opcodestats_increment(ns_g_server->opcodestats,
+diff --git a/bin/named/include/named/client.h b/bin/named/include/named/client.h
+index 969ee4c..5ae10ae 100644
+--- a/bin/named/include/named/client.h
++++ b/bin/named/include/named/client.h
+@@ -80,7 +80,10 @@
+
+ /*% reference-counted TCP connection object */
+ typedef struct ns_tcpconn {
+- isc_refcount_t refs;
++ isc_refcount_t refs; /* Number of clients using
++ * this connection. Conn can
++ * be freed if goes to 0
++ */
+ isc_quota_t *tcpquota;
+ bool pipelined;
+ } ns_tcpconn_t;
diff --git a/debian/patches/0017-libns-Rename-ns_tcpconn-refs-member-to-clients.patch b/debian/patches/0017-libns-Rename-ns_tcpconn-refs-member-to-clients.patch
new file mode 100644
index 0000000..4045792
--- /dev/null
+++ b/debian/patches/0017-libns-Rename-ns_tcpconn-refs-member-to-clients.patch
@@ -0,0 +1,82 @@
+From: =?utf-8?q?Witold_Kr=C4=99cicki?= <wpk@isc.org>
+Date: Wed, 16 Oct 2019 13:18:48 +0200
+Subject: libns: Rename ns_tcpconn refs member to clients
+
+(cherry picked from commit b6d6b50c997b3a00fdde9e0d32c4594ffe94f369)
+---
+ bin/named/client.c | 16 ++++++++--------
+ bin/named/include/named/client.h | 2 +-
+ 2 files changed, 9 insertions(+), 9 deletions(-)
+
+diff --git a/bin/named/client.c b/bin/named/client.c
+index 30877c5..5dc309a 100644
+--- a/bin/named/client.c
++++ b/bin/named/client.c
+@@ -345,7 +345,7 @@ tcpconn_init(ns_client_t *client, bool force) {
+ */
+ tconn = isc_mem_allocate(ns_g_mctx, sizeof(*tconn));
+
+- isc_refcount_init(&tconn->refs, 1); /* Current client */
++ isc_refcount_init(&tconn->clients, 1); /* Current client */
+ tconn->tcpquota = quota;
+ quota = NULL;
+ tconn->pipelined = false;
+@@ -362,14 +362,14 @@ tcpconn_init(ns_client_t *client, bool force) {
+ */
+ static void
+ tcpconn_attach(ns_client_t *source, ns_client_t *target) {
+- int refs;
++ int old_clients;
+
+ REQUIRE(source->tcpconn != NULL);
+ REQUIRE(target->tcpconn == NULL);
+ REQUIRE(source->tcpconn->pipelined);
+
+- isc_refcount_increment(&source->tcpconn->refs, &refs);
+- INSIST(refs > 1);
++ isc_refcount_increment(&source->tcpconn->clients, &old_clients);
++ INSIST(old_clients > 1);
+ target->tcpconn = source->tcpconn;
+ }
+
+@@ -382,15 +382,15 @@ tcpconn_attach(ns_client_t *source, ns_client_t *target) {
+ static void
+ tcpconn_detach(ns_client_t *client) {
+ ns_tcpconn_t *tconn = NULL;
+- int refs;
++ int old_clients;
+
+ REQUIRE(client->tcpconn != NULL);
+
+ tconn = client->tcpconn;
+ client->tcpconn = NULL;
+
+- isc_refcount_decrement(&tconn->refs, &refs);
+- if (refs == 0) {
++ isc_refcount_decrement(&tconn->clients, &old_clients);
++ if (old_clients == 0) {
+ isc_quota_detach(&tconn->tcpquota);
+ isc_mem_free(ns_g_mctx, tconn);
+ }
+@@ -2648,7 +2648,7 @@ client_request(isc_task_t *task, isc_event_t *event) {
+ * Limit the maximum number of simultaenous pipelined
+ * queries on TCP connection to TCP_CLIENTS_PER_CONN.
+ */
+- if ((isc_refcount_current(&client->tcpconn->refs)
++ if ((isc_refcount_current(&client->tcpconn->clients)
+ > TCP_CLIENTS_PER_CONN))
+ {
+ client->tcpconn->pipelined = false;
+diff --git a/bin/named/include/named/client.h b/bin/named/include/named/client.h
+index 5ae10ae..01b6141 100644
+--- a/bin/named/include/named/client.h
++++ b/bin/named/include/named/client.h
+@@ -80,7 +80,7 @@
+
+ /*% reference-counted TCP connection object */
+ typedef struct ns_tcpconn {
+- isc_refcount_t refs; /* Number of clients using
++ isc_refcount_t clients; /* Number of clients using
+ * this connection. Conn can
+ * be freed if goes to 0
+ */
diff --git a/debian/patches/0018-CVE-2020-8616.patch b/debian/patches/0018-CVE-2020-8616.patch
new file mode 100644
index 0000000..1342140
--- /dev/null
+++ b/debian/patches/0018-CVE-2020-8616.patch
@@ -0,0 +1,212 @@
+From: =?utf-8?b?T25kxZllaiBTdXLDvQ==?= <ondrej@debian.org>
+Date: Thu, 14 May 2020 12:24:03 +0200
+Subject: CVE-2020-8616
+
+---
+ lib/dns/adb.c | 33 ++++++++++++++++------------
+ lib/dns/include/dns/adb.h | 4 ++++
+ lib/dns/resolver.c | 55 +++++++++++++++++++++++++++++++----------------
+ 3 files changed, 60 insertions(+), 32 deletions(-)
+
+diff --git a/lib/dns/adb.c b/lib/dns/adb.c
+index 956f368..55fa416 100644
+--- a/lib/dns/adb.c
++++ b/lib/dns/adb.c
+@@ -404,14 +404,13 @@ static void log_quota(dns_adbentry_t *entry, const char *fmt, ...)
+ */
+ #define FIND_WANTEVENT(fn) (((fn)->options & DNS_ADBFIND_WANTEVENT) != 0)
+ #define FIND_WANTEMPTYEVENT(fn) (((fn)->options & DNS_ADBFIND_EMPTYEVENT) != 0)
+-#define FIND_AVOIDFETCHES(fn) (((fn)->options & DNS_ADBFIND_AVOIDFETCHES) \
+- != 0)
+-#define FIND_STARTATZONE(fn) (((fn)->options & DNS_ADBFIND_STARTATZONE) \
+- != 0)
+-#define FIND_HINTOK(fn) (((fn)->options & DNS_ADBFIND_HINTOK) != 0)
+-#define FIND_GLUEOK(fn) (((fn)->options & DNS_ADBFIND_GLUEOK) != 0)
+-#define FIND_HAS_ADDRS(fn) (!ISC_LIST_EMPTY((fn)->list))
+-#define FIND_RETURNLAME(fn) (((fn)->options & DNS_ADBFIND_RETURNLAME) != 0)
++#define FIND_AVOIDFETCHES(fn) (((fn)->options & DNS_ADBFIND_AVOIDFETCHES) != 0)
++#define FIND_STARTATZONE(fn) (((fn)->options & DNS_ADBFIND_STARTATZONE) != 0)
++#define FIND_HINTOK(fn) (((fn)->options & DNS_ADBFIND_HINTOK) != 0)
++#define FIND_GLUEOK(fn) (((fn)->options & DNS_ADBFIND_GLUEOK) != 0)
++#define FIND_HAS_ADDRS(fn) (!ISC_LIST_EMPTY((fn)->list))
++#define FIND_RETURNLAME(fn) (((fn)->options & DNS_ADBFIND_RETURNLAME) != 0)
++#define FIND_NOFETCH(fn) (((fn)->options & DNS_ADBFIND_NOFETCH) != 0)
+
+ /*
+ * These are currently used on simple unsigned ints, so they are
+@@ -3155,21 +3154,26 @@ dns_adb_createfind2(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action,
+ * Listen to negative cache hints, and don't start
+ * another query.
+ */
+- if (NCACHE_RESULT(result) || AUTH_NX(result))
++ if (NCACHE_RESULT(result) || AUTH_NX(result)) {
+ goto fetch;
++ }
+
+- if (!NAME_FETCH_V6(adbname))
++ if (!NAME_FETCH_V6(adbname)) {
+ wanted_fetches |= DNS_ADBFIND_INET6;
++ }
+ }
+
+ fetch:
+ if ((WANT_INET(wanted_addresses) && NAME_HAS_V4(adbname)) ||
+ (WANT_INET6(wanted_addresses) && NAME_HAS_V6(adbname)))
++ {
+ have_address = true;
+- else
++ } else {
+ have_address = false;
+- if (wanted_fetches != 0 &&
+- ! (FIND_AVOIDFETCHES(find) && have_address)) {
++ }
++ if (wanted_fetches != 0 && !(FIND_AVOIDFETCHES(find) && have_address) &&
++ !FIND_NOFETCH(find))
++ {
+ /*
+ * We're missing at least one address family. Either the
+ * caller hasn't instructed us to avoid fetches, or we don't
+@@ -3177,8 +3181,9 @@ dns_adb_createfind2(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action,
+ * be acceptable so we have to launch fetches.
+ */
+
+- if (FIND_STARTATZONE(find))
++ if (FIND_STARTATZONE(find)) {
+ start_at_zone = true;
++ }
+
+ /*
+ * Start V4.
+diff --git a/lib/dns/include/dns/adb.h b/lib/dns/include/dns/adb.h
+index ca35bac..3e27c9e 100644
+--- a/lib/dns/include/dns/adb.h
++++ b/lib/dns/include/dns/adb.h
+@@ -207,6 +207,10 @@ struct dns_adbfind {
+ * lame for this query.
+ */
+ #define DNS_ADBFIND_OVERQUOTA 0x00000400
++/*%
++ * Don't perform a fetch even if there are no address records available.
++ */
++#define DNS_ADBFIND_NOFETCH 0x00000800
+
+ /*%
+ * The answers to queries come back as a list of these.
+diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c
+index 1d76504..1e4fcab 100644
+--- a/lib/dns/resolver.c
++++ b/lib/dns/resolver.c
+@@ -177,6 +177,14 @@
+ #define DEFAULT_MAX_QUERIES 75
+ #endif
+
++/*
++ * After NS_FAIL_LIMIT attempts to fetch a name server address,
++ * if the number of addresses in the NS RRset exceeds NS_RR_LIMIT,
++ * stop trying to fetch, in order to avoid wasting resources.
++ */
++#define NS_FAIL_LIMIT 4
++#define NS_RR_LIMIT 5
++
+ /* Number of hash buckets for zone counters */
+ #ifndef RES_DOMAIN_BUCKETS
+ #define RES_DOMAIN_BUCKETS 523
+@@ -3089,8 +3097,7 @@ sort_finds(dns_adbfindlist_t *findlist, unsigned int bias) {
+ static void
+ findname(fetchctx_t *fctx, dns_name_t *name, in_port_t port,
+ unsigned int options, unsigned int flags, isc_stdtime_t now,
+- bool *overquota, bool *need_alternate)
+-{
++ bool *overquota, bool *need_alternate, unsigned int *no_addresses) {
+ dns_adbaddrinfo_t *ai;
+ dns_adbfind_t *find;
+ dns_resolver_t *res;
+@@ -3178,7 +3185,12 @@ findname(fetchctx_t *fctx, dns_name_t *name, in_port_t port,
+ find->result_v6 != DNS_R_NXDOMAIN) ||
+ (res->dispatches6 == NULL &&
+ find->result_v4 != DNS_R_NXDOMAIN)))
++ {
+ *need_alternate = true;
++ }
++ if (no_addresses != NULL) {
++ (*no_addresses)++;
++ }
+ } else {
+ if ((find->options & DNS_ADBFIND_OVERQUOTA) != 0) {
+ if (overquota != NULL)
+@@ -3229,6 +3241,7 @@ fctx_getaddresses(fetchctx_t *fctx, bool badcache) {
+ dns_rdata_ns_t ns;
+ bool need_alternate = false;
+ bool all_spilled = true;
++ unsigned int no_addresses = 0;
+
+ FCTXTRACE5("getaddresses", "fctx->depth=", fctx->depth);
+
+@@ -3384,20 +3397,28 @@ fctx_getaddresses(fetchctx_t *fctx, bool badcache) {
+ * Extract the name from the NS record.
+ */
+ result = dns_rdata_tostruct(&rdata, &ns, NULL);
+- if (result != ISC_R_SUCCESS)
++ if (result != ISC_R_SUCCESS) {
+ continue;
++ }
+
+- findname(fctx, &ns.name, 0, stdoptions, 0, now,
+- &overquota, &need_alternate);
++ if (no_addresses > NS_FAIL_LIMIT &&
++ dns_rdataset_count(&fctx->nameservers) > NS_RR_LIMIT)
++ {
++ stdoptions |= DNS_ADBFIND_NOFETCH;
++ }
++ findname(fctx, &ns.name, 0, stdoptions, 0, now, &overquota,
++ &need_alternate, &no_addresses);
+
+- if (!overquota)
++ if (!overquota) {
+ all_spilled = false;
++ }
+
+ dns_rdata_reset(&rdata);
+ dns_rdata_freestruct(&ns);
+ }
+- if (result != ISC_R_NOMORE)
++ if (result != ISC_R_NOMORE) {
+ return (result);
++ }
+
+ /*
+ * Do we need to use 6 to 4?
+@@ -3412,7 +3433,7 @@ fctx_getaddresses(fetchctx_t *fctx, bool badcache) {
+ if (!a->isaddress) {
+ findname(fctx, &a->_u._n.name, a->_u._n.port,
+ stdoptions, FCTX_ADDRINFO_FORWARDER,
+- now, NULL, NULL);
++ now, NULL, NULL, NULL);
+ continue;
+ }
+ if (isc_sockaddr_pf(&a->_u.addr) != family)
+@@ -3774,16 +3795,14 @@ fctx_try(fetchctx_t *fctx, bool retrying, bool badcache) {
+ }
+ }
+
+- if (dns_name_countlabels(&fctx->domain) > 2) {
+- result = isc_counter_increment(fctx->qc);
+- if (result != ISC_R_SUCCESS) {
+- isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
+- DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(3),
+- "exceeded max queries resolving '%s'",
+- fctx->info);
+- fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
+- return;
+- }
++ result = isc_counter_increment(fctx->qc);
++ if (result != ISC_R_SUCCESS) {
++ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
++ DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(3),
++ "exceeded max queries resolving '%s'",
++ fctx->info);
++ fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
++ return;
+ }
+
+ bucketnum = fctx->bucketnum;
diff --git a/debian/patches/0019-CVE-2020-8617.patch b/debian/patches/0019-CVE-2020-8617.patch
new file mode 100644
index 0000000..63e84d3
--- /dev/null
+++ b/debian/patches/0019-CVE-2020-8617.patch
@@ -0,0 +1,33 @@
+From: =?utf-8?b?T25kxZllaiBTdXLDvQ==?= <ondrej@debian.org>
+Date: Thu, 14 May 2020 12:24:23 +0200
+Subject: CVE-2020-8617
+
+---
+ lib/dns/tsig.c | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/lib/dns/tsig.c b/lib/dns/tsig.c
+index a94ec69..ff40b99 100644
+--- a/lib/dns/tsig.c
++++ b/lib/dns/tsig.c
+@@ -1422,8 +1422,9 @@ dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg,
+ goto cleanup_context;
+ }
+ msg->verified_sig = 1;
+- } else if (tsig.error != dns_tsigerror_badsig &&
+- tsig.error != dns_tsigerror_badkey) {
++ } else if (!response || (tsig.error != dns_tsigerror_badsig &&
++ tsig.error != dns_tsigerror_badkey))
++ {
+ tsig_log(msg->tsigkey, 2, "signature was empty");
+ return (DNS_R_TSIGVERIFYFAILURE);
+ }
+@@ -1489,7 +1490,7 @@ dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg,
+ }
+ }
+
+- if (tsig.error != dns_rcode_noerror) {
++ if (response && tsig.error != dns_rcode_noerror) {
+ msg->tsigstatus = tsig.error;
+ if (tsig.error == dns_tsigerror_badtime)
+ ret = DNS_R_CLOCKSKEW;
diff --git a/debian/patches/0020-Remove-INSIST-from-from-new_reference.patch b/debian/patches/0020-Remove-INSIST-from-from-new_reference.patch
new file mode 100644
index 0000000..5880fd5
--- /dev/null
+++ b/debian/patches/0020-Remove-INSIST-from-from-new_reference.patch
@@ -0,0 +1,568 @@
+From: Mark Andrews <marka@isc.org>
+Date: Tue, 2 Jun 2020 12:38:40 +1000
+Subject: Remove INSIST from from new_reference
+
+RBTDB node can now appear on the deadnodes lists following the changes
+to decrement_reference in 176b23b6cd98e5b58f832902fdbe964ee5f762d0 to
+defer checking of node->down when the tree write lock is not held. The
+node should be unlinked instead.
+
+(cherry picked from commit b8c4efb10fc8ef1489120a8169fea42adf97025e)
+---
+ lib/dns/rbtdb.c | 238 +++++++++++++++++++++++++++++++++-----------------------
+ 1 file changed, 142 insertions(+), 96 deletions(-)
+
+diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c
+index 0861139..792c443 100644
+--- a/lib/dns/rbtdb.c
++++ b/lib/dns/rbtdb.c
+@@ -2069,11 +2069,16 @@ delete_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node) {
+ * Caller must be holding the node lock.
+ */
+ static inline void
+-new_reference(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node) {
++new_reference(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
++ isc_rwlocktype_t locktype) {
+ unsigned int lockrefs, noderefs;
+ isc_refcount_t *lockref;
+
+- INSIST(!ISC_LINK_LINKED(node, deadlink));
++ if (locktype == isc_rwlocktype_write && ISC_LINK_LINKED(node, deadlink))
++ {
++ ISC_LIST_UNLINK(rbtdb->deadnodes[node->locknum], node,
++ deadlink);
++ }
+ dns_rbtnode_refincrement0(node, &noderefs);
+ if (noderefs == 1) { /* this is the first reference to the node */
+ lockref = &rbtdb->node_locks[node->locknum].references;
+@@ -2119,7 +2124,7 @@ cleanup_dead_nodes(dns_rbtdb_t *rbtdb, int bucketnum) {
+ prune_tree, node,
+ sizeof(isc_event_t));
+ if (ev != NULL) {
+- new_reference(rbtdb, node);
++ new_reference(rbtdb, node, isc_rwlocktype_write);
+ db = NULL;
+ attach((dns_db_t *)rbtdb, &db);
+ ev->ev_sender = db;
+@@ -2183,7 +2188,7 @@ reactivate_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
+ cleanup_dead_nodes(rbtdb, node->locknum);
+ }
+
+- new_reference(rbtdb, node);
++ new_reference(rbtdb, node, locktype);
+
+ NODE_WEAKUNLOCK(nodelock, locktype);
+ NODE_STRONGUNLOCK(nodelock);
+@@ -2329,7 +2334,7 @@ decrement_reference(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
+ prune_tree, node,
+ sizeof(isc_event_t));
+ if (ev != NULL) {
+- new_reference(rbtdb, node);
++ new_reference(rbtdb, node, isc_rwlocktype_write);
+ db = NULL;
+ attach((dns_db_t *)rbtdb, &db);
+ ev->ev_sender = db;
+@@ -2361,8 +2366,10 @@ decrement_reference(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
+ }
+ } else {
+ INSIST(node->data == NULL);
+- INSIST(!ISC_LINK_LINKED(node, deadlink));
+- ISC_LIST_APPEND(rbtdb->deadnodes[bucket], node, deadlink);
++ if (!ISC_LINK_LINKED(node, deadlink)) {
++ ISC_LIST_APPEND(rbtdb->deadnodes[bucket], node,
++ deadlink);
++ }
+ }
+
+ restore_locks:
+@@ -2427,17 +2434,16 @@ prune_tree(isc_task_t *task, isc_event_t *event) {
+
+ /*
+ * We need to gain a reference to the node before
+- * decrementing it in the next iteration. In addition,
+- * if the node is in the dead-nodes list, extract it
+- * from the list beforehand as we do in
+- * reactivate_node().
++ * decrementing it in the next iteration.
+ */
+- if (ISC_LINK_LINKED(parent, deadlink))
++ if (ISC_LINK_LINKED(parent, deadlink)) {
+ ISC_LIST_UNLINK(rbtdb->deadnodes[locknum],
+ parent, deadlink);
+- new_reference(rbtdb, parent);
+- } else
++ }
++ new_reference(rbtdb, parent, isc_rwlocktype_write);
++ } else {
+ parent = NULL;
++ }
+
+ node = parent;
+ } while (node != NULL);
+@@ -3219,7 +3225,7 @@ zone_zonecut_callback(dns_rbtnode_t *node, dns_name_t *name, void *arg) {
+ * We increment the reference count on node to ensure that
+ * search->zonecut_rdataset will still be valid later.
+ */
+- new_reference(search->rbtdb, node);
++ new_reference(search->rbtdb, node, isc_rwlocktype_read);
+ search->zonecut = node;
+ search->zonecut_rdataset = found;
+ search->need_cleanup = true;
+@@ -3270,11 +3276,10 @@ zone_zonecut_callback(dns_rbtnode_t *node, dns_name_t *name, void *arg) {
+ }
+
+ static inline void
+-bind_rdataset(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
+- rdatasetheader_t *header, isc_stdtime_t now,
+- dns_rdataset_t *rdataset)
+-{
+- unsigned char *raw; /* RDATASLAB */
++bind_rdataset(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, rdatasetheader_t *header,
++ isc_stdtime_t now, isc_rwlocktype_t locktype,
++ dns_rdataset_t *rdataset) {
++ unsigned char *raw; /* RDATASLAB */
+
+ /*
+ * Caller must be holding the node reader lock.
+@@ -3287,7 +3292,7 @@ bind_rdataset(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
+ if (rdataset == NULL)
+ return;
+
+- new_reference(rbtdb, node);
++ new_reference(rbtdb, node, locktype);
+
+ INSIST(rdataset->methods == NULL); /* We must be disassociated. */
+
+@@ -3382,11 +3387,13 @@ setup_delegation(rbtdb_search_t *search, dns_dbnode_t **nodep,
+ NODE_LOCK(&(search->rbtdb->node_locks[node->locknum].lock),
+ isc_rwlocktype_read);
+ bind_rdataset(search->rbtdb, node, search->zonecut_rdataset,
+- search->now, rdataset);
++ search->now, isc_rwlocktype_read, rdataset);
+ if (sigrdataset != NULL && search->zonecut_sigrdataset != NULL)
++ {
+ bind_rdataset(search->rbtdb, node,
+- search->zonecut_sigrdataset,
+- search->now, sigrdataset);
++ search->zonecut_sigrdataset, search->now,
++ isc_rwlocktype_read, sigrdataset);
++ }
+ NODE_UNLOCK(&(search->rbtdb->node_locks[node->locknum].lock),
+ isc_rwlocktype_read);
+ }
+@@ -4045,19 +4052,22 @@ find_closest_nsec(rbtdb_search_t *search, dns_dbnode_t **nodep,
+ foundname, NULL);
+ if (result == ISC_R_SUCCESS) {
+ if (nodep != NULL) {
+- new_reference(search->rbtdb,
+- node);
++ new_reference(
++ search->rbtdb, node,
++ isc_rwlocktype_read);
+ *nodep = node;
+ }
+ bind_rdataset(search->rbtdb, node,
+ found, search->now,
++ isc_rwlocktype_read,
+ rdataset);
+- if (foundsig != NULL)
+- bind_rdataset(search->rbtdb,
+- node,
+- foundsig,
+- search->now,
+- sigrdataset);
++ if (foundsig != NULL) {
++ bind_rdataset(
++ search->rbtdb, node,
++ foundsig, search->now,
++ isc_rwlocktype_read,
++ sigrdataset);
++ }
+ }
+ } else if (found == NULL && foundsig == NULL) {
+ /*
+@@ -4331,7 +4341,8 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
+ * ensure that search->zonecut_rdataset will
+ * still be valid later.
+ */
+- new_reference(search.rbtdb, node);
++ new_reference(search.rbtdb, node,
++ isc_rwlocktype_read);
+ search.zonecut = node;
+ search.zonecut_rdataset = header;
+ search.zonecut_sigrdataset = NULL;
+@@ -4504,18 +4515,19 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
+ goto node_exit;
+ }
+ if (nodep != NULL) {
+- new_reference(search.rbtdb, node);
++ new_reference(search.rbtdb, node, isc_rwlocktype_read);
+ *nodep = node;
+ }
+ if ((search.rbtversion->secure == dns_db_secure &&
+ !search.rbtversion->havensec3) ||
+ (search.options & DNS_DBFIND_FORCENSEC) != 0)
+ {
+- bind_rdataset(search.rbtdb, node, nsecheader,
+- 0, rdataset);
+- if (nsecsig != NULL)
+- bind_rdataset(search.rbtdb, node,
+- nsecsig, 0, sigrdataset);
++ bind_rdataset(search.rbtdb, node, nsecheader, 0,
++ isc_rwlocktype_read, rdataset);
++ if (nsecsig != NULL) {
++ bind_rdataset(search.rbtdb, node, nsecsig, 0,
++ isc_rwlocktype_read, sigrdataset);
++ }
+ }
+ if (wild)
+ foundname->attributes |= DNS_NAMEATTR_WILDCARD;
+@@ -4581,18 +4593,21 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
+ }
+
+ if (nodep != NULL) {
+- if (!at_zonecut)
+- new_reference(search.rbtdb, node);
+- else
++ if (!at_zonecut) {
++ new_reference(search.rbtdb, node, isc_rwlocktype_read);
++ } else {
+ search.need_cleanup = false;
++ }
+ *nodep = node;
+ }
+
+ if (type != dns_rdatatype_any) {
+- bind_rdataset(search.rbtdb, node, found, 0, rdataset);
+- if (foundsig != NULL)
++ bind_rdataset(search.rbtdb, node, found, 0, isc_rwlocktype_read,
++ rdataset);
++ if (foundsig != NULL) {
+ bind_rdataset(search.rbtdb, node, foundsig, 0,
+- sigrdataset);
++ isc_rwlocktype_read, sigrdataset);
++ }
+ }
+
+ if (wild)
+@@ -4762,8 +4777,7 @@ cache_zonecut_callback(dns_rbtnode_t *node, dns_name_t *name, void *arg) {
+ * We increment the reference count on node to ensure that
+ * search->zonecut_rdataset will still be valid later.
+ */
+- new_reference(search->rbtdb, node);
+- INSIST(!ISC_LINK_LINKED(node, deadlink));
++ new_reference(search->rbtdb, node, locktype);
+ search->zonecut = node;
+ search->zonecut_rdataset = dname_header;
+ search->zonecut_sigrdataset = sigdname_header;
+@@ -4869,14 +4883,16 @@ find_deepest_zonecut(rbtdb_search_t *search, dns_rbtnode_t *node,
+ }
+ result = DNS_R_DELEGATION;
+ if (nodep != NULL) {
+- new_reference(search->rbtdb, node);
++ new_reference(search->rbtdb, node, locktype);
+ *nodep = node;
+ }
+ bind_rdataset(search->rbtdb, node, found, search->now,
+- rdataset);
+- if (foundsig != NULL)
++ locktype, rdataset);
++ if (foundsig != NULL) {
+ bind_rdataset(search->rbtdb, node, foundsig,
+- search->now, sigrdataset);
++ search->now, locktype,
++ sigrdataset);
++ }
+ if (need_headerupdate(found, search->now) ||
+ (foundsig != NULL &&
+ need_headerupdate(foundsig, search->now))) {
+@@ -4968,14 +4984,16 @@ find_coveringnsec(rbtdb_search_t *search, dns_dbnode_t **nodep,
+ if (found != NULL) {
+ result = dns_name_concatenate(name, origin,
+ foundname, NULL);
+- if (result != ISC_R_SUCCESS)
++ if (result != ISC_R_SUCCESS) {
+ goto unlock_node;
+- bind_rdataset(search->rbtdb, node, found,
+- now, rdataset);
+- if (foundsig != NULL)
++ }
++ bind_rdataset(search->rbtdb, node, found, now, locktype,
++ rdataset);
++ if (foundsig != NULL) {
+ bind_rdataset(search->rbtdb, node, foundsig,
+- now, sigrdataset);
+- new_reference(search->rbtdb, node);
++ now, locktype, sigrdataset);
++ }
++ new_reference(search->rbtdb, node, locktype);
+ *nodep = node;
+ result = DNS_R_COVERINGNSEC;
+ } else if (!empty_node) {
+@@ -5230,19 +5248,21 @@ cache_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
+ */
+ if (nsheader != NULL) {
+ if (nodep != NULL) {
+- new_reference(search.rbtdb, node);
+- INSIST(!ISC_LINK_LINKED(node, deadlink));
++ new_reference(search.rbtdb, node, locktype);
+ *nodep = node;
+ }
+ bind_rdataset(search.rbtdb, node, nsheader, search.now,
+- rdataset);
+- if (need_headerupdate(nsheader, search.now))
++ locktype, rdataset);
++ if (need_headerupdate(nsheader, search.now)) {
+ update = nsheader;
++ }
+ if (nssig != NULL) {
+ bind_rdataset(search.rbtdb, node, nssig,
+- search.now, sigrdataset);
+- if (need_headerupdate(nssig, search.now))
++ search.now, locktype,
++ sigrdataset);
++ if (need_headerupdate(nssig, search.now)) {
+ updatesig = nssig;
++ }
+ }
+ result = DNS_R_DELEGATION;
+ goto node_exit;
+@@ -5260,8 +5280,7 @@ cache_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
+ */
+
+ if (nodep != NULL) {
+- new_reference(search.rbtdb, node);
+- INSIST(!ISC_LINK_LINKED(node, deadlink));
++ new_reference(search.rbtdb, node, locktype);
+ *nodep = node;
+ }
+
+@@ -5290,16 +5309,19 @@ cache_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
+ }
+
+ if (type != dns_rdatatype_any || result == DNS_R_NCACHENXDOMAIN ||
+- result == DNS_R_NCACHENXRRSET) {
+- bind_rdataset(search.rbtdb, node, found, search.now,
++ result == DNS_R_NCACHENXRRSET)
++ {
++ bind_rdataset(search.rbtdb, node, found, search.now, locktype,
+ rdataset);
+- if (need_headerupdate(found, search.now))
++ if (need_headerupdate(found, search.now)) {
+ update = found;
++ }
+ if (!NEGATIVE(found) && foundsig != NULL) {
+ bind_rdataset(search.rbtdb, node, foundsig, search.now,
+- sigrdataset);
+- if (need_headerupdate(foundsig, search.now))
++ locktype, sigrdataset);
++ if (need_headerupdate(foundsig, search.now)) {
+ updatesig = foundsig;
++ }
+ }
+ }
+
+@@ -5445,15 +5467,16 @@ cache_findzonecut(dns_db_t *db, dns_name_t *name, unsigned int options,
+ }
+
+ if (nodep != NULL) {
+- new_reference(search.rbtdb, node);
+- INSIST(!ISC_LINK_LINKED(node, deadlink));
++ new_reference(search.rbtdb, node, locktype);
+ *nodep = node;
+ }
+
+- bind_rdataset(search.rbtdb, node, found, search.now, rdataset);
+- if (foundsig != NULL)
++ bind_rdataset(search.rbtdb, node, found, search.now, locktype,
++ rdataset);
++ if (foundsig != NULL) {
+ bind_rdataset(search.rbtdb, node, foundsig, search.now,
+- sigrdataset);
++ locktype, sigrdataset);
++ }
+
+ if (need_headerupdate(found, search.now) ||
+ (foundsig != NULL && need_headerupdate(foundsig, search.now))) {
+@@ -5804,10 +5827,12 @@ zone_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ }
+ }
+ if (found != NULL) {
+- bind_rdataset(rbtdb, rbtnode, found, now, rdataset);
+- if (foundsig != NULL)
++ bind_rdataset(rbtdb, rbtnode, found, now, isc_rwlocktype_read,
++ rdataset);
++ if (foundsig != NULL) {
+ bind_rdataset(rbtdb, rbtnode, foundsig, now,
+- sigrdataset);
++ isc_rwlocktype_read, sigrdataset);
++ }
+ }
+
+ NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+@@ -5892,10 +5917,11 @@ cache_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ }
+ }
+ if (found != NULL) {
+- bind_rdataset(rbtdb, rbtnode, found, now, rdataset);
+- if (!NEGATIVE(found) && foundsig != NULL)
+- bind_rdataset(rbtdb, rbtnode, foundsig, now,
++ bind_rdataset(rbtdb, rbtnode, found, now, locktype, rdataset);
++ if (!NEGATIVE(found) && foundsig != NULL) {
++ bind_rdataset(rbtdb, rbtnode, foundsig, now, locktype,
+ sigrdataset);
++ }
+ }
+
+ NODE_UNLOCK(lock, locktype);
+@@ -6061,6 +6087,9 @@ resign_insert(dns_rbtdb_t *rbtdb, int idx, rdatasetheader_t *newheader) {
+ return (result);
+ }
+
++/*
++ * node write lock must be held.
++ */
+ static void
+ resign_delete(dns_rbtdb_t *rbtdb, rbtdb_version_t *version,
+ rdatasetheader_t *header)
+@@ -6073,7 +6102,8 @@ resign_delete(dns_rbtdb_t *rbtdb, rbtdb_version_t *version,
+ header->heap_index);
+ header->heap_index = 0;
+ if (version != NULL) {
+- new_reference(rbtdb, header->node);
++ new_reference(rbtdb, header->node,
++ isc_rwlocktype_write);
+ ISC_LIST_APPEND(version->resigned_list, header, link);
+ }
+ }
+@@ -6095,6 +6125,9 @@ update_recordsandbytes(bool add, rbtdb_version_t *rbtversion,
+ }
+ }
+
++/*
++ * write lock on rbtnode must be held.
++ */
+ static isc_result_t
+ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
+ rdatasetheader_t *newheader, unsigned int options, bool loading,
+@@ -6218,10 +6251,13 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
+ free_rdataset(rbtdb,
+ rbtdb->common.mctx,
+ newheader);
+- if (addedrdataset != NULL)
+- bind_rdataset(rbtdb, rbtnode,
+- topheader, now,
+- addedrdataset);
++ if (addedrdataset != NULL) {
++ bind_rdataset(
++ rbtdb, rbtnode,
++ topheader, now,
++ isc_rwlocktype_write,
++ addedrdataset);
++ }
+ return (DNS_R_UNCHANGED);
+ }
+ /*
+@@ -6275,6 +6311,7 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
+ free_rdataset(rbtdb, rbtdb->common.mctx, newheader);
+ if (addedrdataset != NULL)
+ bind_rdataset(rbtdb, rbtnode, header, now,
++ isc_rwlocktype_write,
+ addedrdataset);
+ return (DNS_R_UNCHANGED);
+ }
+@@ -6374,6 +6411,7 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
+ free_rdataset(rbtdb, rbtdb->common.mctx, newheader);
+ if (addedrdataset != NULL)
+ bind_rdataset(rbtdb, rbtnode, header, now,
++ isc_rwlocktype_write,
+ addedrdataset);
+ return (ISC_R_SUCCESS);
+ }
+@@ -6420,6 +6458,7 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
+ free_rdataset(rbtdb, rbtdb->common.mctx, newheader);
+ if (addedrdataset != NULL)
+ bind_rdataset(rbtdb, rbtnode, header, now,
++ isc_rwlocktype_write,
+ addedrdataset);
+ return (ISC_R_SUCCESS);
+ }
+@@ -6617,8 +6656,10 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
+ cname_and_other_data(rbtnode, rbtversion->serial))
+ return (DNS_R_CNAMEANDOTHER);
+
+- if (addedrdataset != NULL)
+- bind_rdataset(rbtdb, rbtnode, newheader, now, addedrdataset);
++ if (addedrdataset != NULL) {
++ bind_rdataset(rbtdb, rbtnode, newheader, now,
++ isc_rwlocktype_write, addedrdataset);
++ }
+
+ return (ISC_R_SUCCESS);
+ }
+@@ -7141,12 +7182,17 @@ subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ result = DNS_R_UNCHANGED;
+ }
+
+- if (result == ISC_R_SUCCESS && newrdataset != NULL)
+- bind_rdataset(rbtdb, rbtnode, newheader, 0, newrdataset);
++ if (result == ISC_R_SUCCESS && newrdataset != NULL) {
++ bind_rdataset(rbtdb, rbtnode, newheader, 0,
++ isc_rwlocktype_write, newrdataset);
++ }
+
+ if (result == DNS_R_NXRRSET && newrdataset != NULL &&
+ (options & DNS_DBSUB_WANTOLD) != 0)
+- bind_rdataset(rbtdb, rbtnode, header, 0, newrdataset);
++ {
++ bind_rdataset(rbtdb, rbtnode, header, 0, isc_rwlocktype_write,
++ newrdataset);
++ }
+
+ unlock:
+ NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+@@ -8057,7 +8103,7 @@ getoriginnode(dns_db_t *db, dns_dbnode_t **nodep) {
+ onode = (dns_rbtnode_t *)rbtdb->origin_node;
+ if (onode != NULL) {
+ NODE_STRONGLOCK(&rbtdb->node_locks[onode->locknum].lock);
+- new_reference(rbtdb, onode);
++ new_reference(rbtdb, onode, isc_rwlocktype_none);
+ NODE_STRONGUNLOCK(&rbtdb->node_locks[onode->locknum].lock);
+
+ *nodep = rbtdb->origin_node;
+@@ -8222,7 +8268,7 @@ getsigningtime(dns_db_t *db, dns_rdataset_t *rdataset,
+ if (header == NULL)
+ goto unlock;
+
+- bind_rdataset(rbtdb, header->node, header, 0, rdataset);
++ bind_rdataset(rbtdb, header->node, header, 0, isc_rwlocktype_read, rdataset);
+
+ if (foundname != NULL)
+ dns_rbt_fullnamefromnode(header->node, foundname);
+@@ -9214,7 +9260,7 @@ rdatasetiter_current(dns_rdatasetiter_t *iterator, dns_rdataset_t *rdataset) {
+ isc_rwlocktype_read);
+
+ bind_rdataset(rbtdb, rbtnode, header, rbtiterator->common.now,
+- rdataset);
++ isc_rwlocktype_read, rdataset);
+
+ NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_read);
+@@ -9650,7 +9696,7 @@ dbiterator_current(dns_dbiterator_t *iterator, dns_dbnode_t **nodep,
+ result = ISC_R_SUCCESS;
+
+ NODE_STRONGLOCK(&rbtdb->node_locks[node->locknum].lock);
+- new_reference(rbtdb, node);
++ new_reference(rbtdb, node, isc_rwlocktype_none);
+ NODE_STRONGUNLOCK(&rbtdb->node_locks[node->locknum].lock);
+
+ *nodep = rbtdbiter->node;
+@@ -10405,7 +10451,7 @@ expire_header(dns_rbtdb_t *rbtdb, rdatasetheader_t *header,
+ * We first need to gain a new reference to the node to meet a
+ * requirement of decrement_reference().
+ */
+- new_reference(rbtdb, header->node);
++ new_reference(rbtdb, header->node, isc_rwlocktype_write);
+ decrement_reference(rbtdb, header->node, 0,
+ isc_rwlocktype_write,
+ tree_locked ? isc_rwlocktype_write :
diff --git a/debian/patches/0021-Always-keep-a-copy-of-the-message.patch b/debian/patches/0021-Always-keep-a-copy-of-the-message.patch
new file mode 100644
index 0000000..4edc752
--- /dev/null
+++ b/debian/patches/0021-Always-keep-a-copy-of-the-message.patch
@@ -0,0 +1,55 @@
+From: Mark Andrews <marka@isc.org>
+Date: Wed, 15 Jul 2020 16:07:51 +1000
+Subject: Always keep a copy of the message
+
+Origin: https://gitlab.isc.org/isc-projects/bind9/commit/6ed167ad0a647dff20c8cb08c944a7967df2d415
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2020-8622
+
+this allows it to be available even when dns_message_parse()
+returns a error.
+---
+ lib/dns/message.c | 24 +++++++++++++-----------
+ 1 file changed, 13 insertions(+), 11 deletions(-)
+
+diff --git a/lib/dns/message.c b/lib/dns/message.c
+index ac637a2..39ed80f 100644
+--- a/lib/dns/message.c
++++ b/lib/dns/message.c
+@@ -1679,6 +1679,19 @@ dns_message_parse(dns_message_t *msg, isc_buffer_t *source,
+ msg->header_ok = 0;
+ msg->question_ok = 0;
+
++ if ((options & DNS_MESSAGEPARSE_CLONEBUFFER) == 0) {
++ isc_buffer_usedregion(&origsource, &msg->saved);
++ } else {
++ msg->saved.length = isc_buffer_usedlength(&origsource);
++ msg->saved.base = isc_mem_get(msg->mctx, msg->saved.length);
++ if (msg->saved.base == NULL) {
++ return (ISC_R_NOMEMORY);
++ }
++ memmove(msg->saved.base, isc_buffer_base(&origsource),
++ msg->saved.length);
++ msg->free_saved = 1;
++ }
++
+ isc_buffer_remainingregion(source, &r);
+ if (r.length < DNS_MESSAGE_HEADERLEN)
+ return (ISC_R_UNEXPECTEDEND);
+@@ -1754,17 +1767,6 @@ dns_message_parse(dns_message_t *msg, isc_buffer_t *source,
+ }
+
+ truncated:
+- if ((options & DNS_MESSAGEPARSE_CLONEBUFFER) == 0)
+- isc_buffer_usedregion(&origsource, &msg->saved);
+- else {
+- msg->saved.length = isc_buffer_usedlength(&origsource);
+- msg->saved.base = isc_mem_get(msg->mctx, msg->saved.length);
+- if (msg->saved.base == NULL)
+- return (ISC_R_NOMEMORY);
+- memmove(msg->saved.base, isc_buffer_base(&origsource),
+- msg->saved.length);
+- msg->free_saved = 1;
+- }
+
+ if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
+ return (DNS_R_RECOVERABLE);
diff --git a/debian/patches/0022-Fix-crash-in-pk11_numbits-when-native-pkcs11-is-used.patch b/debian/patches/0022-Fix-crash-in-pk11_numbits-when-native-pkcs11-is-used.patch
new file mode 100644
index 0000000..b321616
--- /dev/null
+++ b/debian/patches/0022-Fix-crash-in-pk11_numbits-when-native-pkcs11-is-used.patch
@@ -0,0 +1,400 @@
+From: =?utf-8?b?T25kxZllaiBTdXLDvQ==?= <ondrej@isc.org>
+Date: Tue, 21 Jul 2020 14:42:47 +0200
+Subject: Fix crash in pk11_numbits() when native-pkcs11 is used
+
+origin: https://gitlab.isc.org/isc-projects/bind9/commit/8d807cc21655eaa6e6a08afafeec3682c0f3f2ab
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2020-8623
+
+When pk11_numbits() is passed a user provided input that contains all
+zeroes (via crafted DNS message), it would crash with assertion
+failure. Fix that by properly handling such input.
+
+[Salvatore Bonaccorso: Backport to 99.11.5.P4 which does not contain
+9d15323e2484 ("Add small tweaks to the code to fix compilation when ISC
+assertions are disabled")]
+---
+ lib/dns/pkcs11dh_link.c | 15 ++++++--
+ lib/dns/pkcs11dsa_link.c | 8 ++++-
+ lib/dns/pkcs11rsa_link.c | 79 +++++++++++++++++++++++++++++++----------
+ lib/isc/include/pk11/internal.h | 3 +-
+ lib/isc/pk11.c | 60 ++++++++++++++++++++-----------
+ 5 files changed, 121 insertions(+), 44 deletions(-)
+
+diff --git a/lib/dns/pkcs11dh_link.c b/lib/dns/pkcs11dh_link.c
+index e2b60ea..4cd8e32 100644
+--- a/lib/dns/pkcs11dh_link.c
++++ b/lib/dns/pkcs11dh_link.c
+@@ -748,6 +748,7 @@ pkcs11dh_fromdns(dst_key_t *key, isc_buffer_t *data) {
+ CK_BYTE *prime = NULL, *base = NULL, *pub = NULL;
+ CK_ATTRIBUTE *attr;
+ int special = 0;
++ unsigned int bits;
+ isc_result_t result;
+
+ isc_buffer_remainingregion(data, &r);
+@@ -852,7 +853,11 @@ pkcs11dh_fromdns(dst_key_t *key, isc_buffer_t *data) {
+ pub = r.base;
+ isc_region_consume(&r, publen);
+
+- key->key_size = pk11_numbits(prime, plen_);
++ result = pk11_numbits(prime, plen_, &bits);
++ if (result != ISC_R_SUCCESS) {
++ goto cleanup;
++ }
++ key->key_size = bits;
+
+ dh->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 3);
+ if (dh->repr == NULL)
+@@ -1012,6 +1017,7 @@ pkcs11dh_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
+ dst_private_t priv;
+ isc_result_t ret;
+ int i;
++ unsigned int bits;
+ pk11_object_t *dh = NULL;
+ CK_ATTRIBUTE *attr;
+ isc_mem_t *mctx;
+@@ -1082,7 +1088,12 @@ pkcs11dh_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
+
+ attr = pk11_attribute_bytype(dh, CKA_PRIME);
+ INSIST(attr != NULL);
+- key->key_size = pk11_numbits(attr->pValue, attr->ulValueLen);
++
++ ret = pk11_numbits(attr->pValue, attr->ulValueLen, &bits);
++ if (ret != ISC_R_SUCCESS) {
++ goto err;
++ }
++ key->key_size = bits;
+
+ return (ISC_R_SUCCESS);
+
+diff --git a/lib/dns/pkcs11dsa_link.c b/lib/dns/pkcs11dsa_link.c
+index 12d707a..24d4c14 100644
+--- a/lib/dns/pkcs11dsa_link.c
++++ b/lib/dns/pkcs11dsa_link.c
+@@ -983,6 +983,7 @@ pkcs11dsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
+ dst_private_t priv;
+ isc_result_t ret;
+ int i;
++ unsigned int bits;
+ pk11_object_t *dsa = NULL;
+ CK_ATTRIBUTE *attr;
+ isc_mem_t *mctx = key->mctx;
+@@ -1072,7 +1073,12 @@ pkcs11dsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
+
+ attr = pk11_attribute_bytype(dsa, CKA_PRIME);
+ INSIST(attr != NULL);
+- key->key_size = pk11_numbits(attr->pValue, attr->ulValueLen);
++
++ ret = pk11_numbits(attr->pValue, attr->ulValueLen, &bits);
++ if (ret != ISC_R_SUCCESS) {
++ goto err;
++ }
++ key->key_size = bits;
+
+ return (ISC_R_SUCCESS);
+
+diff --git a/lib/dns/pkcs11rsa_link.c b/lib/dns/pkcs11rsa_link.c
+index eb782c8..57ee41d 100644
+--- a/lib/dns/pkcs11rsa_link.c
++++ b/lib/dns/pkcs11rsa_link.c
+@@ -330,6 +330,7 @@ pkcs11rsa_createctx_verify(dst_key_t *key, unsigned int maxbits,
+ key->key_alg == DST_ALG_RSASHA256 ||
+ key->key_alg == DST_ALG_RSASHA512);
+ #endif
++ REQUIRE(maxbits <= RSA_MAX_PUBEXP_BITS);
+
+ /*
+ * Reject incorrect RSA key lengths.
+@@ -373,6 +374,7 @@ pkcs11rsa_createctx_verify(dst_key_t *key, unsigned int maxbits,
+ for (attr = pk11_attribute_first(rsa);
+ attr != NULL;
+ attr = pk11_attribute_next(rsa, attr))
++ {
+ switch (attr->type) {
+ case CKA_MODULUS:
+ INSIST(keyTemplate[5].type == attr->type);
+@@ -393,12 +395,16 @@ pkcs11rsa_createctx_verify(dst_key_t *key, unsigned int maxbits,
+ memmove(keyTemplate[6].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[6].ulValueLen = attr->ulValueLen;
+- if (pk11_numbits(attr->pValue,
+- attr->ulValueLen) > maxbits &&
+- maxbits != 0)
++ unsigned int bits;
++ ret = pk11_numbits(attr->pValue, attr->ulValueLen,
++ &bits);
++ if (ret != ISC_R_SUCCESS ||
++ (bits > maxbits && maxbits != 0)) {
+ DST_RET(DST_R_VERIFYFAILURE);
++ }
+ break;
+ }
++ }
+ pk11_ctx->object = CK_INVALID_HANDLE;
+ pk11_ctx->ontoken = false;
+ PK11_RET(pkcs_C_CreateObject,
+@@ -1063,6 +1069,7 @@ pkcs11rsa_verify(dst_context_t *dctx, const isc_region_t *sig) {
+ keyTemplate[5].ulValueLen = attr->ulValueLen;
+ break;
+ case CKA_PUBLIC_EXPONENT:
++ unsigned int bits;
+ INSIST(keyTemplate[6].type == attr->type);
+ keyTemplate[6].pValue = isc_mem_get(dctx->mctx,
+ attr->ulValueLen);
+@@ -1071,10 +1078,12 @@ pkcs11rsa_verify(dst_context_t *dctx, const isc_region_t *sig) {
+ memmove(keyTemplate[6].pValue, attr->pValue,
+ attr->ulValueLen);
+ keyTemplate[6].ulValueLen = attr->ulValueLen;
+- if (pk11_numbits(attr->pValue,
+- attr->ulValueLen)
+- > RSA_MAX_PUBEXP_BITS)
++ ret = pk11_numbits(attr->pValue, attr->ulValueLen,
++ &bits);
++ if (ret != ISC_R_SUCCESS || bits > RSA_MAX_PUBEXP_BITS)
++ {
+ DST_RET(DST_R_VERIFYFAILURE);
++ }
+ break;
+ }
+ pk11_ctx->object = CK_INVALID_HANDLE;
+@@ -1451,6 +1460,8 @@ pkcs11rsa_fromdns(dst_key_t *key, isc_buffer_t *data) {
+ CK_BYTE *exponent = NULL, *modulus = NULL;
+ CK_ATTRIBUTE *attr;
+ unsigned int length;
++ unsigned int bits;
++ isc_result_t ret = ISC_R_SUCCESS;
+
+ isc_buffer_remainingregion(data, &r);
+ if (r.length == 0)
+@@ -1468,9 +1479,7 @@ pkcs11rsa_fromdns(dst_key_t *key, isc_buffer_t *data) {
+
+ if (e_bytes == 0) {
+ if (r.length < 2) {
+- isc_safe_memwipe(rsa, sizeof(*rsa));
+- isc_mem_put(key->mctx, rsa, sizeof(*rsa));
+- return (DST_R_INVALIDPUBLICKEY);
++ DST_RET(DST_R_INVALIDPUBLICKEY);
+ }
+ e_bytes = (*r.base) << 8;
+ isc_region_consume(&r, 1);
+@@ -1479,16 +1488,18 @@ pkcs11rsa_fromdns(dst_key_t *key, isc_buffer_t *data) {
+ }
+
+ if (r.length < e_bytes) {
+- isc_safe_memwipe(rsa, sizeof(*rsa));
+- isc_mem_put(key->mctx, rsa, sizeof(*rsa));
+- return (DST_R_INVALIDPUBLICKEY);
++ DST_RET(DST_R_INVALIDPUBLICKEY);
+ }
+ exponent = r.base;
+ isc_region_consume(&r, e_bytes);
+ modulus = r.base;
+ mod_bytes = r.length;
+
+- key->key_size = pk11_numbits(modulus, mod_bytes);
++ ret = pk11_numbits(modulus, mod_bytes, &bits);
++ if (ret != ISC_R_SUCCESS) {
++ goto err;
++ }
++ key->key_size = bits;
+
+ isc_buffer_forward(data, length);
+
+@@ -1538,9 +1549,12 @@ pkcs11rsa_fromdns(dst_key_t *key, isc_buffer_t *data) {
+ rsa->repr,
+ rsa->attrcnt * sizeof(*attr));
+ }
++ ret = ISC_R_NOMEMORY;
++
++ err:
+ isc_safe_memwipe(rsa, sizeof(*rsa));
+ isc_mem_put(key->mctx, rsa, sizeof(*rsa));
+- return (ISC_R_NOMEMORY);
++ return (ret);
+ }
+
+ static isc_result_t
+@@ -1719,6 +1733,7 @@ pkcs11rsa_fetch(dst_key_t *key, const char *engine, const char *label,
+ pk11_object_t *pubrsa;
+ pk11_context_t *pk11_ctx = NULL;
+ isc_result_t ret;
++ unsigned int bits;
+
+ if (label == NULL)
+ return (DST_R_NOENGINE);
+@@ -1803,7 +1818,11 @@ pkcs11rsa_fetch(dst_key_t *key, const char *engine, const char *label,
+
+ attr = pk11_attribute_bytype(rsa, CKA_MODULUS);
+ INSIST(attr != NULL);
+- key->key_size = pk11_numbits(attr->pValue, attr->ulValueLen);
++ ret = pk11_numbits(attr->pValue, attr->ulValueLen, &bits);
++ if (ret != ISC_R_SUCCESS) {
++ goto err;
++ }
++ key->key_size = bits;
+
+ return (ISC_R_SUCCESS);
+
+@@ -1889,6 +1908,7 @@ pkcs11rsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
+ CK_ATTRIBUTE *attr;
+ isc_mem_t *mctx = key->mctx;
+ const char *engine = NULL, *label = NULL;
++ unsigned int bits;
+
+ /* read private key file */
+ ret = dst__privstruct_parse(key, DST_ALG_RSA, lexer, mctx, &priv);
+@@ -2032,12 +2052,22 @@ pkcs11rsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
+
+ attr = pk11_attribute_bytype(rsa, CKA_MODULUS);
+ INSIST(attr != NULL);
+- key->key_size = pk11_numbits(attr->pValue, attr->ulValueLen);
++ ret = pk11_numbits(attr->pValue, attr->ulValueLen, &bits);
++ if (ret != ISC_R_SUCCESS) {
++ goto err;
++ }
++ key->key_size = bits;
+
+ attr = pk11_attribute_bytype(rsa, CKA_PUBLIC_EXPONENT);
+ INSIST(attr != NULL);
+- if (pk11_numbits(attr->pValue, attr->ulValueLen) > RSA_MAX_PUBEXP_BITS)
++
++ ret = pk11_numbits(attr->pValue, attr->ulValueLen, &bits);
++ if (ret != ISC_R_SUCCESS) {
++ goto err;
++ }
++ if (bits > RSA_MAX_PUBEXP_BITS) {
+ DST_RET(ISC_R_RANGE);
++ }
+
+ dst__privstruct_free(&priv, mctx);
+ isc_safe_memwipe(&priv, sizeof(priv));
+@@ -2072,6 +2102,7 @@ pkcs11rsa_fromlabel(dst_key_t *key, const char *engine, const char *label,
+ pk11_context_t *pk11_ctx = NULL;
+ isc_result_t ret;
+ unsigned int i;
++ unsigned int bits;
+
+ UNUSED(pin);
+
+@@ -2166,12 +2197,22 @@ pkcs11rsa_fromlabel(dst_key_t *key, const char *engine, const char *label,
+
+ attr = pk11_attribute_bytype(rsa, CKA_PUBLIC_EXPONENT);
+ INSIST(attr != NULL);
+- if (pk11_numbits(attr->pValue, attr->ulValueLen) > RSA_MAX_PUBEXP_BITS)
++
++ ret = pk11_numbits(attr->pValue, attr->ulValueLen, &bits);
++ if (ret != ISC_R_SUCCESS) {
++ goto err;
++ }
++ if (bits > RSA_MAX_PUBEXP_BITS) {
+ DST_RET(ISC_R_RANGE);
++ }
+
+ attr = pk11_attribute_bytype(rsa, CKA_MODULUS);
+ INSIST(attr != NULL);
+- key->key_size = pk11_numbits(attr->pValue, attr->ulValueLen);
++ ret = pk11_numbits(attr->pValue, attr->ulValueLen, &bits);
++ if (ret != ISC_R_SUCCESS) {
++ goto err;
++ }
++ key->key_size = bits;
+
+ pk11_return_session(pk11_ctx);
+ isc_safe_memwipe(pk11_ctx, sizeof(*pk11_ctx));
+diff --git a/lib/isc/include/pk11/internal.h b/lib/isc/include/pk11/internal.h
+index aa8907a..7cc8ec8 100644
+--- a/lib/isc/include/pk11/internal.h
++++ b/lib/isc/include/pk11/internal.h
+@@ -25,7 +25,8 @@ void pk11_mem_put(void *ptr, size_t size);
+
+ CK_SLOT_ID pk11_get_best_token(pk11_optype_t optype);
+
+-unsigned int pk11_numbits(CK_BYTE_PTR data, unsigned int bytecnt);
++isc_result_t
++pk11_numbits(CK_BYTE_PTR data, unsigned int bytecnt, unsigned int *bits);
+
+ CK_ATTRIBUTE *pk11_attribute_first(const pk11_object_t *obj);
+
+diff --git a/lib/isc/pk11.c b/lib/isc/pk11.c
+index c5d2310..b2ab6be 100644
+--- a/lib/isc/pk11.c
++++ b/lib/isc/pk11.c
+@@ -962,13 +962,15 @@ pk11_get_best_token(pk11_optype_t optype) {
+ return (token->slotid);
+ }
+
+-unsigned int
+-pk11_numbits(CK_BYTE_PTR data, unsigned int bytecnt) {
++isc_result_t
++pk11_numbits(CK_BYTE_PTR data, unsigned int bytecnt, unsigned int *bits) {
+ unsigned int bitcnt, i;
+ CK_BYTE top;
+
+- if (bytecnt == 0)
+- return (0);
++ if (bytecnt == 0) {
++ *bits = 0;
++ return (ISC_R_SUCCESS);
++ }
+ bitcnt = bytecnt * 8;
+ for (i = 0; i < bytecnt; i++) {
+ top = data[i];
+@@ -976,25 +978,41 @@ pk11_numbits(CK_BYTE_PTR data, unsigned int bytecnt) {
+ bitcnt -= 8;
+ continue;
+ }
+- if (top & 0x80)
+- return (bitcnt);
+- if (top & 0x40)
+- return (bitcnt - 1);
+- if (top & 0x20)
+- return (bitcnt - 2);
+- if (top & 0x10)
+- return (bitcnt - 3);
+- if (top & 0x08)
+- return (bitcnt - 4);
+- if (top & 0x04)
+- return (bitcnt - 5);
+- if (top & 0x02)
+- return (bitcnt - 6);
+- if (top & 0x01)
+- return (bitcnt - 7);
++ if (top & 0x80) {
++ *bits = bitcnt;
++ return (ISC_R_SUCCESS);
++ }
++ if (top & 0x40) {
++ *bits = bitcnt - 1;
++ return (ISC_R_SUCCESS);
++ }
++ if (top & 0x20) {
++ *bits = bitcnt - 2;
++ return (ISC_R_SUCCESS);
++ }
++ if (top & 0x10) {
++ *bits = bitcnt - 3;
++ return (ISC_R_SUCCESS);
++ }
++ if (top & 0x08) {
++ *bits = bitcnt - 4;
++ return (ISC_R_SUCCESS);
++ }
++ if (top & 0x04) {
++ *bits = bitcnt - 5;
++ return (ISC_R_SUCCESS);
++ }
++ if (top & 0x02) {
++ *bits = bitcnt - 6;
++ return (ISC_R_SUCCESS);
++ }
++ if (top & 0x01) {
++ *bits = bitcnt - 7;
++ return (ISC_R_SUCCESS);
++ }
+ break;
+ }
+- INSIST(0);
++ return (ISC_R_RANGE);
+ }
+
+ CK_ATTRIBUTE *
diff --git a/debian/patches/0023-Wait-more-than-1-second-for-NSEC3-chain-changes.patch b/debian/patches/0023-Wait-more-than-1-second-for-NSEC3-chain-changes.patch
new file mode 100644
index 0000000..6f2593c
--- /dev/null
+++ b/debian/patches/0023-Wait-more-than-1-second-for-NSEC3-chain-changes.patch
@@ -0,0 +1,71 @@
+From: =?utf-8?b?TWljaGHFgiBLxJlwaWXFhA==?= <michal@isc.org>
+Date: Tue, 23 Apr 2019 14:59:05 +0200
+Subject: Wait more than 1 second for NSEC3 chain changes
+
+Origin: https://gitlab.isc.org/isc-projects/bind9/commit/4e2cc911d2ceb90ec04605451fb1bb518ef75cd0
+
+One second may not be enough for an NSEC3 chain change triggered by an
+UPDATE message to complete. Wait up to 10 seconds when checking whether
+a given NSEC3 chain change is complete in the "nsupdate" system test.
+
+(cherry picked from commit f8746cddbce838fec2a28f00617df6af1319fbc8)
+---
+ bin/tests/system/nsupdate/tests.sh | 30 +++++++++++++++++++++---------
+ 1 file changed, 21 insertions(+), 9 deletions(-)
+
+diff --git a/bin/tests/system/nsupdate/tests.sh b/bin/tests/system/nsupdate/tests.sh
+index 9f26572..c72753b 100755
+--- a/bin/tests/system/nsupdate/tests.sh
++++ b/bin/tests/system/nsupdate/tests.sh
+@@ -454,18 +454,24 @@ grep "flags:[^;]* aa[ ;]" dig.out.ns3.$n > /dev/null || ret=1
+
+ n=`expr $n + 1`
+ ret=0
+-echo_i "add a new the NSEC3PARAM via update ($n)"
++echo_i "add a new NSEC3PARAM via update ($n)"
+ $NSUPDATE << EOF
+ server 10.53.0.3 ${PORT}
+ update add nsec3param.test 3600 NSEC3PARAM 1 0 4 -
+ send
+ EOF
+
+-sleep 1
++_ret=1
++for i in 0 1 2 3 4 5 6 7 8 9; do
++ $DIG $DIGOPTS +tcp +norec +time=1 +tries=1 @10.53.0.3 nsec3param.test. NSEC3PARAM > dig.out.ns3.$n || _ret=1
++ if grep "ANSWER: 2" dig.out.ns3.$n > /dev/null; then
++ _ret=0
++ break
++ fi
++ sleep 1
++done
+
+-$DIG $DIGOPTS +tcp +noadd +nosea +nostat +noquest +nocmd +norec nsec3param.test.\
+- @10.53.0.3 nsec3param > dig.out.ns3.$n || ret=1
+-grep "ANSWER: 2" dig.out.ns3.$n > /dev/null || ret=1
++if [ $_ret -ne 0 ]; then ret=1; fi
+ grep "NSEC3PARAM 1 0 4 -" dig.out.ns3.$n > /dev/null || ret=1
+ grep "flags:[^;]* aa[ ;]" dig.out.ns3.$n > /dev/null || ret=1
+ if [ $ret != 0 ] ; then echo_i "failed"; status=`expr $ret + $status`; fi
+@@ -480,11 +486,17 @@ update add nsec3param.test 7200 NSEC3PARAM 1 0 5 -
+ send
+ EOF
+
+-sleep 1
++_ret=1
++for i in 0 1 2 3 4 5 6 7 8 9; do
++ $DIG $DIGOPTS +tcp +norec +time=1 +tries=1 @10.53.0.3 nsec3param.test. NSEC3PARAM > dig.out.ns3.$n || _ret=1
++ if grep "ANSWER: 1" dig.out.ns3.$n > /dev/null; then
++ _ret=0
++ break
++ fi
++ sleep 1
++done
+
+-$DIG $DIGOPTS +tcp +noadd +nosea +nostat +noquest +nocmd +norec nsec3param.test.\
+- @10.53.0.3 nsec3param > dig.out.ns3.$n || ret=1
+-grep "ANSWER: 1" dig.out.ns3.$n > /dev/null || ret=1
++if [ $_ret -ne 0 ]; then ret=1; fi
+ grep "7200.*NSEC3PARAM 1 0 5 -" dig.out.ns3.$n > /dev/null || ret=1
+ grep "flags:[^;]* aa[ ;]" dig.out.ns3.$n > /dev/null || ret=1
+ $JOURNALPRINT ns3/nsec3param.test.db.signed.jnl > jp.out.ns3.$n
diff --git a/debian/patches/0024-Update-policy-subdomain-was-incorrectly-treated-as-z.patch b/debian/patches/0024-Update-policy-subdomain-was-incorrectly-treated-as-z.patch
new file mode 100644
index 0000000..b2e6ac9
--- /dev/null
+++ b/debian/patches/0024-Update-policy-subdomain-was-incorrectly-treated-as-z.patch
@@ -0,0 +1,27 @@
+From: Mark Andrews <marka@isc.org>
+Date: Wed, 29 Jul 2020 23:36:03 +1000
+Subject: [1/3] Update-policy 'subdomain' was incorrectly treated as 'zonesub'
+
+Origin: https://gitlab.isc.org/isc-projects/bind9/commit/e4cccf9668c7adee4724a7649ec64685f82c8677
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2020-8624
+
+resulting in names outside the specified subdomain having the wrong
+restrictions for the given key.
+---
+ bin/named/zoneconf.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c
+index e237bdb..4898447 100644
+--- a/bin/named/zoneconf.c
++++ b/bin/named/zoneconf.c
+@@ -237,7 +237,8 @@ configure_zone_ssutable(const cfg_obj_t *zconfig, dns_zone_t *zone,
+
+ str = cfg_obj_asstring(matchtype);
+ CHECK(dns_ssu_mtypefromstring(str, &mtype));
+- if (mtype == dns_ssumatchtype_subdomain) {
++ if (mtype == dns_ssumatchtype_subdomain &&
++ strcasecmp(str, "zonesub") == 0) {
+ usezone = true;
+ }
+
diff --git a/debian/patches/0025-Add-a-test-for-update-policy-subdomain.patch b/debian/patches/0025-Add-a-test-for-update-policy-subdomain.patch
new file mode 100644
index 0000000..adcc70d
--- /dev/null
+++ b/debian/patches/0025-Add-a-test-for-update-policy-subdomain.patch
@@ -0,0 +1,73 @@
+From: Mark Andrews <marka@isc.org>
+Date: Wed, 29 Jul 2020 23:36:03 +1000
+Subject: [2/3] Add a test for update-policy 'subdomain'
+
+Origin: https://gitlab.isc.org/isc-projects/bind9/commit/393e8f643c02215fa4e6d4edf67be7d77085da0e
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2020-8624
+
+The new test checks that 'update-policy subdomain' is properly enforced.
+---
+ bin/tests/system/nsupdate/ns1/named.conf.in | 6 ++++++
+ bin/tests/system/nsupdate/tests.sh | 25 +++++++++++++++++++++++++
+ 2 files changed, 31 insertions(+)
+
+diff --git a/bin/tests/system/nsupdate/ns1/named.conf.in b/bin/tests/system/nsupdate/ns1/named.conf.in
+index 1d999ad..87904f4 100644
+--- a/bin/tests/system/nsupdate/ns1/named.conf.in
++++ b/bin/tests/system/nsupdate/ns1/named.conf.in
+@@ -36,6 +36,11 @@ key altkey {
+ secret "1234abcd8765";
+ };
+
++key restricted.example.nil {
++ algorithm hmac-md5;
++ secret "1234abcd8765";
++};
++
+ include "ddns.key";
+
+ zone "example.nil" {
+@@ -45,6 +50,7 @@ zone "example.nil" {
+ check-mx ignore;
+ update-policy {
+ grant ddns-key.example.nil subdomain example.nil ANY;
++ grant restricted.example.nil subdomain restricted.example.nil ANY;
+ };
+ allow-transfer { any; };
+ };
+diff --git a/bin/tests/system/nsupdate/tests.sh b/bin/tests/system/nsupdate/tests.sh
+index c72753b..432240e 100755
+--- a/bin/tests/system/nsupdate/tests.sh
++++ b/bin/tests/system/nsupdate/tests.sh
+@@ -635,6 +635,31 @@ then
+ echo_i "failed"; status=1
+ fi
+
++n=`expr $n + 1`
++ret=0
++echo_i "check that 'update-policy subdomain' is properly enforced ($n)"
++# "restricted.example.nil" matches "grant ... subdomain restricted.example.nil"
++# and thus this UPDATE should succeed.
++$NSUPDATE -d <<END > nsupdate.out1-$n 2>&1 || ret=1
++server 10.53.0.1 ${PORT}
++key restricted.example.nil 1234abcd8765
++update add restricted.example.nil 0 IN TXT everywhere.
++send
++END
++$DIG $DIGOPTS +tcp @10.53.0.1 restricted.example.nil TXT > dig.out.1.test$n || ret=1
++grep "TXT.*everywhere" dig.out.1.test$n > /dev/null || ret=1
++# "example.nil" does not match "grant ... subdomain restricted.example.nil" and
++# thus this UPDATE should fail.
++$NSUPDATE -d <<END > nsupdate.out2-$n 2>&1 && ret=1
++server 10.53.0.1 ${PORT}
++key restricted.example.nil 1234abcd8765
++update add example.nil 0 IN TXT everywhere.
++send
++END
++$DIG $DIGOPTS +tcp @10.53.0.1 example.nil TXT > dig.out.2.test$n || ret=1
++grep "TXT.*everywhere" dig.out.2.test$n > /dev/null && ret=1
++[ $ret = 0 ] || { echo_i "failed"; status=1; }
++
+ n=`expr $n + 1`
+ ret=0
+ echo_i "check that changes to the DNSKEY RRset TTL do not have side effects ($n)"
diff --git a/debian/patches/0026-Add-a-test-for-update-policy-zonesub.patch b/debian/patches/0026-Add-a-test-for-update-policy-zonesub.patch
new file mode 100644
index 0000000..771c46c
--- /dev/null
+++ b/debian/patches/0026-Add-a-test-for-update-policy-zonesub.patch
@@ -0,0 +1,111 @@
+From: Mark Andrews <marka@isc.org>
+Date: Tue, 4 Aug 2020 11:41:33 +1000
+Subject: [3/3] Add a test for update-policy 'zonesub'
+
+Origin: https://gitlab.isc.org/isc-projects/bind9/commit/58e560beb50873c699f3431cf57e215dc645d7aa
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2020-8624
+
+The new test checks that 'update-policy zonesub' is properly enforced.
+---
+ bin/tests/system/nsupdate/ns1/named.conf.in | 6 +++++
+ bin/tests/system/nsupdate/tests.sh | 35 +++++++++++++++++++++++++----
+ 2 files changed, 37 insertions(+), 4 deletions(-)
+
+diff --git a/bin/tests/system/nsupdate/ns1/named.conf.in b/bin/tests/system/nsupdate/ns1/named.conf.in
+index 87904f4..e90907a 100644
+--- a/bin/tests/system/nsupdate/ns1/named.conf.in
++++ b/bin/tests/system/nsupdate/ns1/named.conf.in
+@@ -41,6 +41,11 @@ key restricted.example.nil {
+ secret "1234abcd8765";
+ };
+
++key zonesub-key.example.nil {
++ algorithm hmac-md5;
++ secret "1234subk8765";
++};
++
+ include "ddns.key";
+
+ zone "example.nil" {
+@@ -49,6 +54,7 @@ zone "example.nil" {
+ check-integrity no;
+ check-mx ignore;
+ update-policy {
++ grant zonesub-key.example.nil zonesub TXT;
+ grant ddns-key.example.nil subdomain example.nil ANY;
+ grant restricted.example.nil subdomain restricted.example.nil ANY;
+ };
+diff --git a/bin/tests/system/nsupdate/tests.sh b/bin/tests/system/nsupdate/tests.sh
+index 432240e..f2f26ab 100755
+--- a/bin/tests/system/nsupdate/tests.sh
++++ b/bin/tests/system/nsupdate/tests.sh
+@@ -430,7 +430,7 @@ sleep 1
+ # this also proves that the server is still running.
+ $DIG $DIGOPTS +tcp +noadd +nosea +nostat +noquest +nocmd +norec example.\
+ @10.53.0.3 nsec3param > dig.out.ns3.$n || ret=1
+-grep "ANSWER: 0" dig.out.ns3.$n > /dev/null || ret=1
++grep "ANSWER: 0," dig.out.ns3.$n > /dev/null || ret=1
+ grep "flags:[^;]* aa[ ;]" dig.out.ns3.$n > /dev/null || ret=1
+ [ $ret = 0 ] || { echo_i "failed"; status=1; }
+
+@@ -447,7 +447,7 @@ sleep 1
+
+ $DIG $DIGOPTS +tcp +noadd +nosea +nostat +noquest +nocmd +norec nsec3param.test.\
+ @10.53.0.3 nsec3param > dig.out.ns3.$n || ret=1
+-grep "ANSWER: 1" dig.out.ns3.$n > /dev/null || ret=1
++grep "ANSWER: 1," dig.out.ns3.$n > /dev/null || ret=1
+ grep "3600.*NSEC3PARAM" dig.out.ns3.$n > /dev/null || ret=1
+ grep "flags:[^;]* aa[ ;]" dig.out.ns3.$n > /dev/null || ret=1
+ [ $ret = 0 ] || { echo_i "failed"; status=1; }
+@@ -464,7 +464,7 @@ EOF
+ _ret=1
+ for i in 0 1 2 3 4 5 6 7 8 9; do
+ $DIG $DIGOPTS +tcp +norec +time=1 +tries=1 @10.53.0.3 nsec3param.test. NSEC3PARAM > dig.out.ns3.$n || _ret=1
+- if grep "ANSWER: 2" dig.out.ns3.$n > /dev/null; then
++ if grep "ANSWER: 2," dig.out.ns3.$n > /dev/null; then
+ _ret=0
+ break
+ fi
+@@ -489,7 +489,7 @@ EOF
+ _ret=1
+ for i in 0 1 2 3 4 5 6 7 8 9; do
+ $DIG $DIGOPTS +tcp +norec +time=1 +tries=1 @10.53.0.3 nsec3param.test. NSEC3PARAM > dig.out.ns3.$n || _ret=1
+- if grep "ANSWER: 1" dig.out.ns3.$n > /dev/null; then
++ if grep "ANSWER: 1," dig.out.ns3.$n > /dev/null; then
+ _ret=0
+ break
+ fi
+@@ -660,6 +660,33 @@ $DIG $DIGOPTS +tcp @10.53.0.1 example.nil TXT > dig.out.2.test$n || ret=1
+ grep "TXT.*everywhere" dig.out.2.test$n > /dev/null && ret=1
+ [ $ret = 0 ] || { echo_i "failed"; status=1; }
+
++n=`expr $n + 1`
++ret=0
++echo_i "check that 'update-policy zonesub' is properly enforced ($n)"
++# grant zonesub-key.example.nil zonesub TXT;
++# the A record update should be rejected as it is not in the type list
++$NSUPDATE -d <<END > nsupdate.out1-$n 2>&1 && ret=1
++server 10.53.0.1 ${PORT}
++key zonesub-key.example.nil 1234subk8765
++update add zonesub.example.nil 0 IN A 1.2.3.4
++send
++END
++$DIG $DIGOPTS +tcp @10.53.0.1 zonesub.example.nil A > dig.out.1.test$n || ret=1
++grep "status: REFUSED" nsupdate.out1-$n > /dev/null || ret=1
++grep "ANSWER: 0," dig.out.1.test$n > /dev/null || ret=1
++# the TXT record update should be accepted as it is in the type list
++$NSUPDATE -d <<END > nsupdate.out2-$n 2>&1 || ret=1
++server 10.53.0.1 ${PORT}
++key zonesub-key.example.nil 1234subk8765
++update add zonesub.example.nil 0 IN TXT everywhere.
++send
++END
++$DIG $DIGOPTS +tcp @10.53.0.1 zonesub.example.nil TXT > dig.out.2.test$n || ret=1
++grep "status: REFUSED" nsupdate.out2-$n > /dev/null && ret=1
++grep "ANSWER: 1," dig.out.2.test$n > /dev/null || ret=1
++grep "TXT.*everywhere" dig.out.2.test$n > /dev/null || ret=1
++[ $ret = 0 ] || { echo_i "failed"; status=1; }
++
+ n=`expr $n + 1`
+ ret=0
+ echo_i "check that changes to the DNSKEY RRset TTL do not have side effects ($n)"
diff --git a/debian/patches/0027-CVE-2020-8625.patch b/debian/patches/0027-CVE-2020-8625.patch
new file mode 100644
index 0000000..1e036c8
--- /dev/null
+++ b/debian/patches/0027-CVE-2020-8625.patch
@@ -0,0 +1,25 @@
+From: Debian DNS Team <team+dns@tracker.debian.org>
+Date: Thu, 29 Apr 2021 13:06:03 +0200
+Subject: Buffer overflow in GSSAPI security policy negotiation
+ (CVE-2020-8625)
+
+Origin: vendor
+Forwarded: not-needed
+Last-Update: 2021-02-09
+---
+ lib/dns/spnego.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/lib/dns/spnego.c b/lib/dns/spnego.c
+index ad77f24..28ead69 100644
+--- a/lib/dns/spnego.c
++++ b/lib/dns/spnego.c
+@@ -877,7 +877,7 @@ der_get_oid(const unsigned char *p, size_t len, oid *data, size_t *size) {
+ return (ASN1_OVERRUN);
+ }
+
+- data->components = malloc(len * sizeof(*data->components));
++ data->components = malloc((len + 1) * sizeof(*data->components));
+ if (data->components == NULL) {
+ return (ENOMEM);
+ }
diff --git a/debian/patches/0028-Free-resources-when-gss_accept_sec_context-fails.patch b/debian/patches/0028-Free-resources-when-gss_accept_sec_context-fails.patch
new file mode 100644
index 0000000..b8ed135
--- /dev/null
+++ b/debian/patches/0028-Free-resources-when-gss_accept_sec_context-fails.patch
@@ -0,0 +1,28 @@
+From: =?utf-8?b?TWljaGHFgiBLxJlwaWXFhA==?= <michal@isc.org>
+Date: Thu, 8 Apr 2021 10:33:44 +0200
+Subject: Free resources when gss_accept_sec_context() fails
+
+Even if a call to gss_accept_sec_context() fails, it might still cause a
+GSS-API response token to be allocated and left for the caller to
+release. Make sure the token is released before an early return from
+dst_gssapi_acceptctx().
+
+(cherry picked from commit d954e152d9f2901118b1fe36d3931ec244317fab)
+---
+ lib/dns/gssapictx.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/lib/dns/gssapictx.c b/lib/dns/gssapictx.c
+index 8bd99af..6d787d3 100644
+--- a/lib/dns/gssapictx.c
++++ b/lib/dns/gssapictx.c
+@@ -746,6 +746,9 @@ dst_gssapi_acceptctx(gss_cred_id_t cred,
+ default:
+ gss_log(3, "failed gss_accept_sec_context: %s",
+ gss_error_tostring(gret, minor, buf, sizeof(buf)));
++ if (gouttoken.length > 0U) {
++ (void)gss_release_buffer(&minor, &gouttoken);
++ }
+ return (result);
+ }
+
diff --git a/debian/patches/0029-Check-SOA-owner-names-in-zone-transfers.patch b/debian/patches/0029-Check-SOA-owner-names-in-zone-transfers.patch
new file mode 100644
index 0000000..5af5d36
--- /dev/null
+++ b/debian/patches/0029-Check-SOA-owner-names-in-zone-transfers.patch
@@ -0,0 +1,40 @@
+From: Mark Andrews <marka@isc.org>
+Date: Wed, 3 Feb 2021 11:10:20 +1100
+Subject: Check SOA owner names in zone transfers
+
+An IXFR containing SOA records with owner names different than the
+transferred zone's origin can result in named serving a version of that
+zone without an SOA record at the apex. This causes a RUNTIME_CHECK
+assertion failure the next time such a zone is refreshed. Fix by
+immediately rejecting a zone transfer (either an incremental or
+non-incremental one) upon detecting an SOA record not placed at the apex
+of the transferred zone.
+---
+ lib/dns/xfrin.c | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c
+index d39ca26..0baf170 100644
+--- a/lib/dns/xfrin.c
++++ b/lib/dns/xfrin.c
+@@ -477,6 +477,20 @@ xfr_rr(dns_xfrin_ctx_t *xfr, dns_name_t *name, uint32_t ttl,
+ dns_rdatatype_ismeta(rdata->type))
+ FAIL(DNS_R_FORMERR);
+
++ /*
++ * Immediately reject the entire transfer if the RR that is currently
++ * being processed is an SOA record that is not placed at the zone
++ * apex.
++ */
++ if (rdata->type == dns_rdatatype_soa &&
++ !dns_name_equal(&xfr->name, name)) {
++ char namebuf[DNS_NAME_FORMATSIZE];
++ dns_name_format(name, namebuf, sizeof(namebuf));
++ xfrin_log(xfr, ISC_LOG_DEBUG(3), "SOA name mismatch: '%s'",
++ namebuf);
++ FAIL(DNS_R_NOTZONETOP);
++ }
++
+ redo:
+ switch (xfr->state) {
+ case XFRST_SOAQUERY:
diff --git a/debian/patches/0030-Address-inconsistencies-in-checking-added-RRsets.patch b/debian/patches/0030-Address-inconsistencies-in-checking-added-RRsets.patch
new file mode 100644
index 0000000..23a1b29
--- /dev/null
+++ b/debian/patches/0030-Address-inconsistencies-in-checking-added-RRsets.patch
@@ -0,0 +1,46 @@
+From: Mark Andrews <marka@isc.org>
+Date: Fri, 12 Feb 2021 14:51:28 +1100
+Subject: Address inconsistencies in checking added RRsets
+
+loading_addrdataset() rejects SOA RRsets which are not at top of zone.
+addrdataset() should similarly reject such RRsets.
+---
+ lib/dns/rbtdb.c | 12 ++++++++++--
+ 1 file changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c
+index 792c443..b1b928c 100644
+--- a/lib/dns/rbtdb.c
++++ b/lib/dns/rbtdb.c
+@@ -117,7 +117,7 @@ struct rbtdb_file_header {
+ /*%
+ * Note that "impmagic" is not the first four bytes of the struct, so
+ * ISC_MAGIC_VALID cannot be used.
+- */
++a */
+ #define VALID_RBTDB(rbtdb) ((rbtdb) != NULL && \
+ (rbtdb)->common.impmagic == RBTDB_MAGIC)
+
+@@ -6806,13 +6806,21 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ REQUIRE(VALID_RBTDB(rbtdb));
+ INSIST(rbtversion == NULL || rbtversion->rbtdb == rbtdb);
+
+- if (rbtdb->common.methods == &zone_methods)
++ if (rbtdb->common.methods == &zone_methods) {
++ /*
++ * SOA records are only allowed at top of zone.
++ */
++ if (rdataset->type == dns_rdatatype_soa &&
++ node != rbtdb->origin_node) {
++ return (DNS_R_NOTZONETOP);
++ }
+ REQUIRE(((rbtnode->nsec == DNS_RBT_NSEC_NSEC3 &&
+ (rdataset->type == dns_rdatatype_nsec3 ||
+ rdataset->covers == dns_rdatatype_nsec3)) ||
+ (rbtnode->nsec != DNS_RBT_NSEC_NSEC3 &&
+ rdataset->type != dns_rdatatype_nsec3 &&
+ rdataset->covers != dns_rdatatype_nsec3)));
++ }
+
+ if (rbtversion == NULL) {
+ if (now == 0)
diff --git a/debian/patches/0031-Unload-a-zone-if-a-transfer-breaks-its-SOA-record.patch b/debian/patches/0031-Unload-a-zone-if-a-transfer-breaks-its-SOA-record.patch
new file mode 100644
index 0000000..93c75ec
--- /dev/null
+++ b/debian/patches/0031-Unload-a-zone-if-a-transfer-breaks-its-SOA-record.patch
@@ -0,0 +1,40 @@
+From: Mark Andrews <marka@isc.org>
+Date: Thu, 25 Feb 2021 14:11:05 +1100
+Subject: Unload a zone if a transfer breaks its SOA record
+
+If a zone transfer results in a zone not having any NS records, named
+stops serving it because such a zone is broken. Do the same if an
+incoming zone transfer results in a zone lacking an SOA record at the
+apex or containing more than one SOA record.
+---
+ lib/dns/zone.c | 15 ++++++++++++---
+ 1 file changed, 12 insertions(+), 3 deletions(-)
+
+diff --git a/lib/dns/zone.c b/lib/dns/zone.c
+index 6d4426a..c3c8f94 100644
+--- a/lib/dns/zone.c
++++ b/lib/dns/zone.c
+@@ -15352,11 +15352,20 @@ zone_xfrdone(dns_zone_t *zone, isc_result_t result) {
+ &retry, &expire, &minimum, NULL);
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+ if (result == ISC_R_SUCCESS) {
+- if (soacount != 1)
++ if (soacount != 1) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "transferred zone "
+- "has %d SOA record%s", soacount,
+- (soacount != 0) ? "s" : "");
++ "has %d SOA records",
++ soacount);
++ if (DNS_ZONE_FLAG(zone,
++ DNS_ZONEFLG_HAVETIMERS)) {
++ zone->refresh = DNS_ZONE_DEFAULTREFRESH;
++ zone->retry = DNS_ZONE_DEFAULTRETRY;
++ }
++ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_HAVETIMERS);
++ zone_unload(zone);
++ goto next_master;
++ }
+ if (nscount == 0) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "transferred zone "
diff --git a/debian/patches/0032-Handle-DNAME-lookup-via-itself.patch b/debian/patches/0032-Handle-DNAME-lookup-via-itself.patch
new file mode 100644
index 0000000..1b73aad
--- /dev/null
+++ b/debian/patches/0032-Handle-DNAME-lookup-via-itself.patch
@@ -0,0 +1,41 @@
+From: Mark Andrews <marka@isc.org>
+Date: Mon, 1 Mar 2021 16:46:07 +1100
+Subject: Handle DNAME lookup via itself
+
+When answering a query, named should never attempt to add the same RRset
+to the ANSWER section more than once. However, such a situation may
+arise when chasing DNAME records: one of the DNAME records placed in the
+ANSWER section may turn out to be the final answer to a client query,
+but there is no way to know that in advance. Tweak the relevant INSIST
+assertion in query_find() so that it handles this case properly. The
+rdataset is freed later anyway, so there is no need to clean it up
+immediately.
+---
+ bin/named/query.c | 13 ++++++++++---
+ 1 file changed, 10 insertions(+), 3 deletions(-)
+
+diff --git a/bin/named/query.c b/bin/named/query.c
+index f8dbef2..b674b67 100644
+--- a/bin/named/query.c
++++ b/bin/named/query.c
+@@ -9087,10 +9087,17 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
+ if (noqname != NULL)
+ query_addnoqnameproof(client, noqname);
+ /*
+- * We shouldn't ever fail to add 'rdataset'
+- * because it's already in the answer.
++ * 'rdataset' will only be non-NULL here if the ANSWER section
++ * of the message to be sent to the client already contains an
++ * RRset with the same owner name and the same type as
++ * 'rdataset'. This should never happen, with one exception:
++ * when chasing DNAME records, one of the DNAME records placed
++ * in the ANSWER section may turn out to be the final answer to
++ * the client's query, but we have no way of knowing that until
++ * now. In such a case, 'rdataset' will be freed later, so we
++ * do not need to free it here.
+ */
+- INSIST(rdataset == NULL);
++ INSIST(rdataset == NULL || qtype == dns_rdatatype_dname);
+ }
+
+ addauth:
diff --git a/debian/patches/0033-Disable-lame-ttl-cache.patch b/debian/patches/0033-Disable-lame-ttl-cache.patch
new file mode 100644
index 0000000..efd2132
--- /dev/null
+++ b/debian/patches/0033-Disable-lame-ttl-cache.patch
@@ -0,0 +1,70 @@
+From: =?utf-8?b?T25kxZllaiBTdXLDvQ==?= <ondrej@sury.org>
+Date: Fri, 24 Sep 2021 09:35:11 +0200
+Subject: Disable lame-ttl cache
+
+The lame-ttl cache is implemented in ADB as per-server locked
+linked-list "indexed" with <qname,qtype>. This list has to be walked
+every time there's a new query or new record added into the lame cache.
+Determined attacker can use this to degrade performance of the resolver.
+
+Resolver testing has shown that disabling the lame cache has little
+impact on the resolver performance and it's a minimal viable defense
+against this kind of attack.
+---
+ bin/named/config.c | 2 +-
+ bin/named/server.c | 8 ++++++--
+ doc/arm/Bv9ARM-book.xml | 9 +++------
+ 3 files changed, 10 insertions(+), 9 deletions(-)
+
+diff --git a/bin/named/config.c b/bin/named/config.c
+index d22ee4b..ba5fa8a 100644
+--- a/bin/named/config.c
++++ b/bin/named/config.c
+@@ -170,7 +170,7 @@ options {\n\
+ #ifdef HAVE_GEOIP
+ " geoip-use-ecs yes;\n"
+ #endif
+-" lame-ttl 600;\n"
++" lame-ttl 0;\n"
+ #ifdef HAVE_LMDB
+ " lmdb-mapsize 32M;\n"
+ #endif
+diff --git a/bin/named/server.c b/bin/named/server.c
+index 149458e..748a150 100644
+--- a/bin/named/server.c
++++ b/bin/named/server.c
+@@ -3971,8 +3971,12 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist,
+ result = ns_config_get(maps, "lame-ttl", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ lame_ttl = cfg_obj_asuint32(obj);
+- if (lame_ttl > 1800)
+- lame_ttl = 1800;
++ if (lame_ttl > 0) {
++ cfg_obj_log(obj, ns_g_lctx, ISC_LOG_WARNING,
++ "disabling lame cache despite lame-ttl > 0 as it "
++ "may cause performance issues");
++ lame_ttl = 0;
++ }
+ dns_resolver_setlamettl(view->resolver, lame_ttl);
+
+ /*
+diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml
+index 4b36bd0..7852b5a 100644
+--- a/doc/arm/Bv9ARM-book.xml
++++ b/doc/arm/Bv9ARM-book.xml
+@@ -8855,12 +8855,9 @@ avoid-v6-udp-ports { 40000; range 50000 60000; };
+ <term><command>lame-ttl</command></term>
+ <listitem>
+ <para>
+- Sets the number of seconds to cache a
+- lame server indication. 0 disables caching. (This is
+- <emphasis role="bold">NOT</emphasis> recommended.)
+- The default is <literal>600</literal> (10 minutes) and the
+- maximum value is
+- <literal>1800</literal> (30 minutes).
++ This is always set to 0. More information is available
++ in the <link xmlns:xlink="http://www.w3.org/1999/xlink"
++ xlink:href="https://kb.isc.org/docs/cve-2021-25219">security advisory for CVE-2021-25219</link>.
+ </para>
+
+ </listitem>
diff --git a/debian/patches/0034-Enable-lame-response-detection-even-with-disabled-la.patch b/debian/patches/0034-Enable-lame-response-detection-even-with-disabled-la.patch
new file mode 100644
index 0000000..790b7c1
--- /dev/null
+++ b/debian/patches/0034-Enable-lame-response-detection-even-with-disabled-la.patch
@@ -0,0 +1,48 @@
+From: =?utf-8?b?T25kxZllaiBTdXLDvQ==?= <ondrej@sury.org>
+Date: Fri, 24 Sep 2021 09:48:50 +0200
+Subject: Enable lame response detection even with disabled lame cache
+
+Previously, when lame cache would be disabled by setting lame-ttl to 0,
+it would also disable lame answer detection. In this commit, we enable
+the lame response detection even when the lame cache is disabled. This
+enables stopping answer processing early rather than going through the
+whole answer processing flow.
+---
+ lib/dns/resolver.c | 22 ++++++++++++----------
+ 1 file changed, 12 insertions(+), 10 deletions(-)
+
+diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c
+index 1e4fcab..bfc1ae2 100644
+--- a/lib/dns/resolver.c
++++ b/lib/dns/resolver.c
+@@ -8323,18 +8323,20 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
+ /*
+ * Is the server lame?
+ */
+- if (res->lame_ttl != 0 && !ISFORWARDER(query->addrinfo) &&
+- is_lame(fctx)) {
++ if (!ISFORWARDER(query->addrinfo) && is_lame(fctx)) {
+ inc_stats(res, dns_resstatscounter_lame);
+ log_lame(fctx, query->addrinfo);
+- result = dns_adb_marklame(fctx->adb, query->addrinfo,
+- &fctx->name, fctx->type,
+- now + res->lame_ttl);
+- if (result != ISC_R_SUCCESS)
+- isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
+- DNS_LOGMODULE_RESOLVER, ISC_LOG_ERROR,
+- "could not mark server as lame: %s",
+- isc_result_totext(result));
++ if (res->lame_ttl != 0) {
++ result = dns_adb_marklame(fctx->adb, query->addrinfo,
++ &fctx->name, fctx->type,
++ now + res->lame_ttl);
++ if (result != ISC_R_SUCCESS) {
++ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
++ DNS_LOGMODULE_RESOLVER, ISC_LOG_ERROR,
++ "could not mark server as lame: %s",
++ isc_result_totext(result));
++ }
++ }
+ broken_server = DNS_R_LAME;
+ keep_trying = true;
+ FCTXTRACE("lame server");
diff --git a/debian/patches/0035-CVE-2021-25220.patch b/debian/patches/0035-CVE-2021-25220.patch
new file mode 100644
index 0000000..48652e1
--- /dev/null
+++ b/debian/patches/0035-CVE-2021-25220.patch
@@ -0,0 +1,203 @@
+From: Debian DNS Team <team+dns@tracker.debian.org>
+Date: Mon, 14 Mar 2022 15:31:10 +0100
+Subject: CVE-2021-25220
+
+---
+ lib/dns/resolver.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++++++---
+ 1 file changed, 120 insertions(+), 5 deletions(-)
+
+diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c
+index bfc1ae2..cabcbdc 100644
+--- a/lib/dns/resolver.c
++++ b/lib/dns/resolver.c
+@@ -68,6 +68,7 @@
+ #include <dns/stats.h>
+ #include <dns/tsig.h>
+ #include <dns/validator.h>
++#include <dns/zone.h>
+
+ #ifdef WANT_QUERYTRACE
+ #define RTRACE(m) isc_log_write(dns_lctx, \
+@@ -314,6 +315,8 @@ struct fetchctx {
+ bool ns_ttl_ok;
+ uint32_t ns_ttl;
+ isc_counter_t * qc;
++ dns_fixedname_t fwdfname;
++ dns_name_t *fwdname;
+
+ /*%
+ * The number of events we're waiting for.
+@@ -3304,6 +3307,7 @@ fctx_getaddresses(fetchctx_t *fctx, bool badcache) {
+ if (result == ISC_R_SUCCESS) {
+ fwd = ISC_LIST_HEAD(forwarders->fwdrs);
+ fctx->fwdpolicy = forwarders->fwdpolicy;
++ dns_name_copy(domain, fctx->fwdname, NULL);
+ if (fctx->fwdpolicy == dns_fwdpolicy_only &&
+ isstrictsubdomain(domain, &fctx->domain)) {
+ fcount_decr(fctx);
+@@ -4334,6 +4338,9 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
+ fctx->restarts = 0;
+ fctx->querysent = 0;
+ fctx->referrals = 0;
++
++ fctx->fwdname = dns_fixedname_initname(&fctx->fwdfname);
++
+ TIME_NOW(&fctx->start);
+ fctx->timeouts = 0;
+ fctx->lamecount = 0;
+@@ -4386,8 +4393,10 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
+ domain = dns_fixedname_initname(&fixed);
+ result = dns_fwdtable_find2(fctx->res->view->fwdtable, fwdname,
+ domain, &forwarders);
+- if (result == ISC_R_SUCCESS)
++ if (result == ISC_R_SUCCESS) {
+ fctx->fwdpolicy = forwarders->fwdpolicy;
++ dns_name_copy(domain, fctx->fwdname, NULL);
++ }
+
+ if (fctx->fwdpolicy != dns_fwdpolicy_only) {
+ /*
+@@ -6144,6 +6153,107 @@ mark_related(dns_name_t *name, dns_rdataset_t *rdataset,
+ rdataset->attributes |= DNS_RDATASETATTR_EXTERNAL;
+ }
+
++/*
++ * Returns true if 'name' is external to the namespace for which
++ * the server being queried can answer, either because it's not a
++ * subdomain or because it's below a forward declaration or a
++ * locally served zone.
++ */
++static inline bool
++name_external(dns_name_t *name, dns_rdatatype_t type, fetchctx_t *fctx) {
++ isc_result_t result;
++ dns_forwarders_t *forwarders = NULL;
++ dns_fixedname_t fixed, zfixed;
++ dns_name_t *fname = dns_fixedname_initname(&fixed);
++ dns_name_t *zfname = dns_fixedname_initname(&zfixed);
++ dns_name_t *apex = NULL;
++ dns_name_t suffix;
++ dns_zone_t *zone = NULL;
++ unsigned int labels;
++ dns_namereln_t rel;
++
++ apex = ISFORWARDER(fctx->addrinfo) ? fctx->fwdname : &fctx->domain;
++
++ /*
++ * The name is outside the queried namespace.
++ */
++ rel = dns_name_fullcompare(name, apex, &(int){ 0 },
++ &(unsigned int){ 0U });
++ if (rel != dns_namereln_subdomain && rel != dns_namereln_equal) {
++ return (true);
++ }
++
++ /*
++ * If the record lives in the parent zone, adjust the name so we
++ * look for the correct zone or forward clause.
++ */
++ labels = dns_name_countlabels(name);
++ if (dns_rdatatype_atparent(type) && labels > 1U) {
++ dns_name_init(&suffix, NULL);
++ dns_name_getlabelsequence(name, 1, labels - 1, &suffix);
++ name = &suffix;
++ } else if (rel == dns_namereln_equal) {
++ /* If 'name' is 'apex', no further checking is needed. */
++ return (false);
++ }
++
++ /*
++ * If there is a locally served zone between 'apex' and 'name'
++ * then don't cache.
++ */
++ LOCK(&fctx->res->view->lock);
++ if (fctx->res->view->zonetable != NULL) {
++ unsigned int options = DNS_ZTFIND_NOEXACT;
++ result = dns_zt_find(fctx->res->view->zonetable, name, options,
++ zfname, &zone);
++ if (zone != NULL) {
++ dns_zone_detach(&zone);
++ }
++ if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
++ if (dns_name_fullcompare(zfname, apex, &(int){ 0 },
++ &(unsigned int){ 0U }) ==
++ dns_namereln_subdomain)
++ {
++ UNLOCK(&fctx->res->view->lock);
++ return (true);
++ }
++ }
++ }
++ UNLOCK(&fctx->res->view->lock);
++
++ /*
++ * Look for a forward declaration below 'name'.
++ */
++ result = dns_fwdtable_find2(fctx->res->view->fwdtable, name, fname,
++ &forwarders);
++
++ if (ISFORWARDER(fctx->addrinfo)) {
++ /*
++ * See if the forwarder declaration is better.
++ */
++ if (result == ISC_R_SUCCESS) {
++ return (!dns_name_equal(fname, fctx->fwdname));
++ }
++
++ /*
++ * If the lookup failed, the configuration must have
++ * changed: play it safe and don't cache.
++ */
++ return (true);
++ } else if (result == ISC_R_SUCCESS &&
++ forwarders->fwdpolicy == dns_fwdpolicy_only &&
++ !ISC_LIST_EMPTY(forwarders->fwdrs))
++ {
++ /*
++ * If 'name' is covered by a 'forward only' clause then we
++ * can't cache this repsonse.
++ */
++ return (true);
++ }
++
++ return (false);
++}
++
+ static isc_result_t
+ check_section(void *arg, dns_name_t *addname, dns_rdatatype_t type,
+ dns_section_t section)
+@@ -6170,7 +6280,7 @@ check_section(void *arg, dns_name_t *addname, dns_rdatatype_t type,
+ result = dns_message_findname(fctx->rmessage, section, addname,
+ dns_rdatatype_any, 0, &name, NULL);
+ if (result == ISC_R_SUCCESS) {
+- external = !dns_name_issubdomain(name, &fctx->domain);
++ external = name_external(name, type, fctx);
+ if (type == dns_rdatatype_a) {
+ for (rdataset = ISC_LIST_HEAD(name->list);
+ rdataset != NULL;
+@@ -7018,6 +7128,13 @@ answer_response(fetchctx_t *fctx) {
+ break;
+
+ case dns_namereln_subdomain:
++ /*
++ * Don't accept DNAME from parent namespace.
++ */
++ if (name_external(name, dns_rdatatype_dname, fctx)) {
++ continue;
++ }
++
+ /*
+ * In-scope DNAME records must have at least
+ * as many labels as the domain being queried.
+@@ -7246,11 +7363,9 @@ answer_response(fetchctx_t *fctx) {
+ */
+ result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
+ while (!done && result == ISC_R_SUCCESS) {
+- bool external;
+ name = NULL;
+ dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
+- external = !dns_name_issubdomain(name, &fctx->domain);
+- if (!external) {
++ if (!name_external(name, dns_rdatatype_ns, fctx)) {
+ /*
+ * We expect to find NS or SIG NS rdatasets, and
+ * nothing else.
diff --git a/debian/patches/series b/debian/patches/series
new file mode 100644
index 0000000..bce493a
--- /dev/null
+++ b/debian/patches/series
@@ -0,0 +1,35 @@
+0001-non-linux.diff
+0002-multiarch.diff
+0003-min-cache-ttl.diff
+0004-library_paths.diff
+0005-resource_missing_include.diff
+0006-prepare_native_pkcs11.diff
+0007-ctxstart_no_sighandling.diff
+0008-reproducible_build.diff
+0009-Add_--install-layout=deb_to_setup.py_call.patch
+0010-skip-rtld-deepbind-for-dyndb.diff
+0011-keymgr-dont-immediately-delete.diff
+0012-CVE-2018-5743-Limiting-simultaneous-TCP-clients-is-i.patch
+0013-Replace-atomic-operations-in-bin-named-client.c-with.patch
+0014-Disable-broken-Ed448-support.patch
+0015-move-item_out-test-inside-lock-in-dns_dispatch_getne.patch
+0016-Set-a-limit-on-number-of-simultaneous-pipelined-TCP-.patch
+0017-libns-Rename-ns_tcpconn-refs-member-to-clients.patch
+0018-CVE-2020-8616.patch
+0019-CVE-2020-8617.patch
+0020-Remove-INSIST-from-from-new_reference.patch
+0021-Always-keep-a-copy-of-the-message.patch
+0022-Fix-crash-in-pk11_numbits-when-native-pkcs11-is-used.patch
+0023-Wait-more-than-1-second-for-NSEC3-chain-changes.patch
+0024-Update-policy-subdomain-was-incorrectly-treated-as-z.patch
+0025-Add-a-test-for-update-policy-subdomain.patch
+0026-Add-a-test-for-update-policy-zonesub.patch
+0027-CVE-2020-8625.patch
+0028-Free-resources-when-gss_accept_sec_context-fails.patch
+0029-Check-SOA-owner-names-in-zone-transfers.patch
+0030-Address-inconsistencies-in-checking-added-RRsets.patch
+0031-Unload-a-zone-if-a-transfer-breaks-its-SOA-record.patch
+0032-Handle-DNAME-lookup-via-itself.patch
+0033-Disable-lame-ttl-cache.patch
+0034-Enable-lame-response-detection-even-with-disabled-la.patch
+0035-CVE-2021-25220.patch