git: c6edb21e3763 - releng/13.3 - unbound: Vendor import 1.19.1

From: Cy Schubert <cy_at_FreeBSD.org>
Date: Sun, 18 Feb 2024 04:46:36 UTC
The branch releng/13.3 has been updated by cy:

URL: https://cgit.FreeBSD.org/src/commit/?id=c6edb21e3763b55b4102cb08d62a51abcf3cbd4c

commit c6edb21e3763b55b4102cb08d62a51abcf3cbd4c
Author:     Cy Schubert <cy@FreeBSD.org>
AuthorDate: 2023-11-13 19:44:16 +0000
Commit:     Cy Schubert <cy@FreeBSD.org>
CommitDate: 2024-02-18 04:00:01 +0000

    unbound: Vendor import 1.19.1
    
    Release notes at
        https://www.nlnetlabs.nl/news/2024/Feb/13/unbound-1.19.1-released/
    
    Security:       CVE-2023-50387, CVE-2023-50868
    Approved by:    re (cperciva)
    
    (cherry picked from commit b76ef9a7cb8a7c62d10ae8101f41014f34819174)
    (cherry picked from commit abe4ced2b9de0a3dd44d7e2068cfd7fa2b428c16)
---
 contrib/unbound/config.guess                  |  11 +-
 contrib/unbound/config.sub                    |  29 +-
 contrib/unbound/configure                     |  25 +-
 contrib/unbound/configure.ac                  |   5 +-
 contrib/unbound/doc/README                    |   2 +-
 contrib/unbound/doc/example.conf.in           |   2 +-
 contrib/unbound/doc/libunbound.3.in           |   4 +-
 contrib/unbound/doc/unbound-anchor.8.in       |   2 +-
 contrib/unbound/doc/unbound-checkconf.8.in    |   2 +-
 contrib/unbound/doc/unbound-control.8.in      |   2 +-
 contrib/unbound/doc/unbound-host.1.in         |   2 +-
 contrib/unbound/doc/unbound.8.in              |   4 +-
 contrib/unbound/doc/unbound.conf.5.in         |   2 +-
 contrib/unbound/services/authzone.c           |   3 +-
 contrib/unbound/services/cache/dns.c          |  22 ++
 contrib/unbound/services/cache/dns.h          |   9 +
 contrib/unbound/testdata/val_any_negcache.rpl |   3 +
 contrib/unbound/util/fptr_wlist.c             |   1 +
 contrib/unbound/validator/val_nsec.c          |   3 +-
 contrib/unbound/validator/val_nsec3.c         | 316 ++++++++++++----
 contrib/unbound/validator/val_nsec3.h         |  60 +++-
 contrib/unbound/validator/val_sigcrypt.c      |  37 +-
 contrib/unbound/validator/val_sigcrypt.h      |   3 +-
 contrib/unbound/validator/val_utils.c         |  22 +-
 contrib/unbound/validator/val_utils.h         |   4 +-
 contrib/unbound/validator/validator.c         | 499 ++++++++++++++++++++++----
 contrib/unbound/validator/validator.h         |  18 +
 usr.sbin/unbound/config.h                     |   4 +-
 28 files changed, 889 insertions(+), 207 deletions(-)

diff --git a/contrib/unbound/config.guess b/contrib/unbound/config.guess
index cdfc4392047c..f6d217a49f8f 100755
--- a/contrib/unbound/config.guess
+++ b/contrib/unbound/config.guess
@@ -1,10 +1,10 @@
 #! /bin/sh
 # Attempt to guess a canonical system name.
-#   Copyright 1992-2023 Free Software Foundation, Inc.
+#   Copyright 1992-2024 Free Software Foundation, Inc.
 
 # shellcheck disable=SC2006,SC2268 # see below for rationale
 
-timestamp='2023-08-22'
+timestamp='2024-01-01'
 
 # This file is free software; you can redistribute it and/or modify it
 # under the terms of the GNU General Public License as published by
@@ -60,7 +60,7 @@ version="\
 GNU config.guess ($timestamp)
 
 Originally written by Per Bothner.
-Copyright 1992-2023 Free Software Foundation, Inc.
+Copyright 1992-2024 Free Software Foundation, Inc.
 
 This is free software; see the source for copying conditions.  There is NO
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@@ -165,6 +165,8 @@ Linux|GNU|GNU/*)
 	LIBC=dietlibc
 	#elif defined(__GLIBC__)
 	LIBC=gnu
+	#elif defined(__LLVM_LIBC__)
+	LIBC=llvm
 	#else
 	#include <stdarg.h>
 	/* First heuristic to detect musl libc.  */
@@ -1593,6 +1595,9 @@ EOF
     *:Unleashed:*:*)
 	GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE
 	;;
+    *:Ironclad:*:*)
+	GUESS=$UNAME_MACHINE-unknown-ironclad
+	;;
 esac
 
 # Do we have a guess based on uname results?
diff --git a/contrib/unbound/config.sub b/contrib/unbound/config.sub
index defe52c0c874..2c6a07ab3c34 100755
--- a/contrib/unbound/config.sub
+++ b/contrib/unbound/config.sub
@@ -1,10 +1,10 @@
 #! /bin/sh
 # Configuration validation subroutine script.
-#   Copyright 1992-2023 Free Software Foundation, Inc.
+#   Copyright 1992-2024 Free Software Foundation, Inc.
 
 # shellcheck disable=SC2006,SC2268 # see below for rationale
 
-timestamp='2023-09-19'
+timestamp='2024-01-01'
 
 # This file is free software; you can redistribute it and/or modify it
 # under the terms of the GNU General Public License as published by
@@ -76,7 +76,7 @@ Report bugs and patches to <config-patches@gnu.org>."
 version="\
 GNU config.sub ($timestamp)
 
-Copyright 1992-2023 Free Software Foundation, Inc.
+Copyright 1992-2024 Free Software Foundation, Inc.
 
 This is free software; see the source for copying conditions.  There is NO
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@@ -1222,6 +1222,7 @@ case $cpu-$vendor in
 			| moxie \
 			| mt \
 			| msp430 \
+			| nanomips* \
 			| nds32 | nds32le | nds32be \
 			| nfp \
 			| nios | nios2 | nios2eb | nios2el \
@@ -1253,6 +1254,7 @@ case $cpu-$vendor in
 			| ubicom32 \
 			| v70 | v850 | v850e | v850e1 | v850es | v850e2 | v850e2v3 \
 			| vax \
+			| vc4 \
 			| visium \
 			| w65 \
 			| wasm32 | wasm64 \
@@ -1597,7 +1599,7 @@ case $cpu-$vendor in
 		os=
 		obj=elf
 		;;
-	mips*-*)
+	mips*-*|nanomips*-*)
 		os=
 		obj=elf
 		;;
@@ -1721,7 +1723,7 @@ fi
 
 case $os in
 	# Sometimes we do "kernel-libc", so those need to count as OSes.
-	musl* | newlib* | relibc* | uclibc*)
+	llvm* | musl* | newlib* | relibc* | uclibc*)
 		;;
 	# Likewise for "kernel-abi"
 	eabi* | gnueabi*)
@@ -1766,12 +1768,19 @@ case $os in
 	     | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \
 	     | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \
 	     | nsk* | powerunix* | genode* | zvmoe* | qnx* | emx* | zephyr* \
-	     | fiwix* | mlibc* | cos* | mbr* )
+	     | fiwix* | mlibc* | cos* | mbr* | ironclad* )
 		;;
 	# This one is extra strict with allowed versions
 	sco3.2v2 | sco3.2v[4-9]* | sco5v6*)
 		# Don't forget version if it is 3.2v4 or newer.
 		;;
+	# This refers to builds using the UEFI calling convention
+	# (which depends on the architecture) and PE file format.
+	# Note that this is both a different calling convention and
+	# different file format than that of GNU-EFI
+	# (x86_64-w64-mingw32).
+	uefi)
+		;;
 	none)
 		;;
 	kernel* | msvc* )
@@ -1818,8 +1827,9 @@ esac
 # As a final step for OS-related things, validate the OS-kernel combination
 # (given a valid OS), if there is a kernel.
 case $kernel-$os-$obj in
-	linux-gnu*- | linux-dietlibc*- | linux-android*- | linux-newlib*- \
-		   | linux-musl*- | linux-relibc*- | linux-uclibc*- | linux-mlibc*- )
+	linux-gnu*- | linux-android*- | linux-dietlibc*- | linux-llvm*- \
+		    | linux-mlibc*- | linux-musl*- | linux-newlib*- \
+		    | linux-relibc*- | linux-uclibc*- )
 		;;
 	uclinux-uclibc*- )
 		;;
@@ -1827,7 +1837,8 @@ case $kernel-$os-$obj in
 		;;
 	windows*-msvc*-)
 		;;
-	-dietlibc*- | -newlib*- | -musl*- | -relibc*- | -uclibc*- | -mlibc*- )
+	-dietlibc*- | -llvm*- | -mlibc*- | -musl*- | -newlib*- | -relibc*- \
+		    | -uclibc*- )
 		# These are just libc implementations, not actual OSes, and thus
 		# require a kernel.
 		echo "Invalid configuration '$1': libc '$os' needs explicit kernel." 1>&2
diff --git a/contrib/unbound/configure b/contrib/unbound/configure
index fbe6f8697742..c87c669c8435 100755
--- a/contrib/unbound/configure
+++ b/contrib/unbound/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for unbound 1.19.0.
+# Generated by GNU Autoconf 2.69 for unbound 1.19.1.
 #
 # Report bugs to <unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues>.
 #
@@ -591,8 +591,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='unbound'
 PACKAGE_TARNAME='unbound'
-PACKAGE_VERSION='1.19.0'
-PACKAGE_STRING='unbound 1.19.0'
+PACKAGE_VERSION='1.19.1'
+PACKAGE_STRING='unbound 1.19.1'
 PACKAGE_BUGREPORT='unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues'
 PACKAGE_URL=''
 
@@ -1477,7 +1477,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures unbound 1.19.0 to adapt to many kinds of systems.
+\`configure' configures unbound 1.19.1 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1543,7 +1543,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of unbound 1.19.0:";;
+     short | recursive ) echo "Configuration of unbound 1.19.1:";;
    esac
   cat <<\_ACEOF
 
@@ -1785,7 +1785,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-unbound configure 1.19.0
+unbound configure 1.19.1
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2494,7 +2494,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by unbound $as_me 1.19.0, which was
+It was created by unbound $as_me 1.19.1, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -2846,11 +2846,11 @@ UNBOUND_VERSION_MAJOR=1
 
 UNBOUND_VERSION_MINOR=19
 
-UNBOUND_VERSION_MICRO=0
+UNBOUND_VERSION_MICRO=1
 
 
 LIBUNBOUND_CURRENT=9
-LIBUNBOUND_REVISION=23
+LIBUNBOUND_REVISION=24
 LIBUNBOUND_AGE=1
 # 1.0.0 had 0:12:0
 # 1.0.1 had 0:13:0
@@ -2941,6 +2941,7 @@ LIBUNBOUND_AGE=1
 # 1.17.1 had 9:21:1
 # 1.18.0 had 9:22:1
 # 1.19.0 had 9:23:1
+# 1.19.1 had 9:24:1
 
 #   Current  -- the number of the binary API that we're implementing
 #   Revision -- which iteration of the implementation of the binary
@@ -21894,7 +21895,7 @@ _ACEOF
 
 
 
-version=1.19.0
+version=1.19.1
 
 date=`date +'%b %e, %Y'`
 
@@ -22413,7 +22414,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by unbound $as_me 1.19.0, which was
+This file was extended by unbound $as_me 1.19.1, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -22479,7 +22480,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-unbound config.status 1.19.0
+unbound config.status 1.19.1
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
diff --git a/contrib/unbound/configure.ac b/contrib/unbound/configure.ac
index 1b999596d09a..70fc7e7fdf49 100644
--- a/contrib/unbound/configure.ac
+++ b/contrib/unbound/configure.ac
@@ -11,14 +11,14 @@ sinclude(dnscrypt/dnscrypt.m4)
 # must be numbers. ac_defun because of later processing
 m4_define([VERSION_MAJOR],[1])
 m4_define([VERSION_MINOR],[19])
-m4_define([VERSION_MICRO],[0])
+m4_define([VERSION_MICRO],[1])
 AC_INIT([unbound],m4_defn([VERSION_MAJOR]).m4_defn([VERSION_MINOR]).m4_defn([VERSION_MICRO]),[unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues],[unbound])
 AC_SUBST(UNBOUND_VERSION_MAJOR, [VERSION_MAJOR])
 AC_SUBST(UNBOUND_VERSION_MINOR, [VERSION_MINOR])
 AC_SUBST(UNBOUND_VERSION_MICRO, [VERSION_MICRO])
 
 LIBUNBOUND_CURRENT=9
-LIBUNBOUND_REVISION=23
+LIBUNBOUND_REVISION=24
 LIBUNBOUND_AGE=1
 # 1.0.0 had 0:12:0
 # 1.0.1 had 0:13:0
@@ -109,6 +109,7 @@ LIBUNBOUND_AGE=1
 # 1.17.1 had 9:21:1
 # 1.18.0 had 9:22:1
 # 1.19.0 had 9:23:1
+# 1.19.1 had 9:24:1
 
 #   Current  -- the number of the binary API that we're implementing
 #   Revision -- which iteration of the implementation of the binary
diff --git a/contrib/unbound/doc/README b/contrib/unbound/doc/README
index 592a9f4ae8d2..eef91ce02836 100644
--- a/contrib/unbound/doc/README
+++ b/contrib/unbound/doc/README
@@ -1,4 +1,4 @@
-README for Unbound 1.19.0
+README for Unbound 1.19.1
 Copyright 2007 NLnet Labs
 http://unbound.net
 
diff --git a/contrib/unbound/doc/example.conf.in b/contrib/unbound/doc/example.conf.in
index fe0dde69fa19..fcfb1da815db 100644
--- a/contrib/unbound/doc/example.conf.in
+++ b/contrib/unbound/doc/example.conf.in
@@ -1,7 +1,7 @@
 #
 # Example configuration file.
 #
-# See unbound.conf(5) man page, version 1.19.0.
+# See unbound.conf(5) man page, version 1.19.1.
 #
 # this is a comment.
 
diff --git a/contrib/unbound/doc/libunbound.3.in b/contrib/unbound/doc/libunbound.3.in
index fa090d58186f..4a55eaa9e2ca 100644
--- a/contrib/unbound/doc/libunbound.3.in
+++ b/contrib/unbound/doc/libunbound.3.in
@@ -1,4 +1,4 @@
-.TH "libunbound" "3" "Nov  8, 2023" "NLnet Labs" "unbound 1.19.0"
+.TH "libunbound" "3" "Feb 13, 2024" "NLnet Labs" "unbound 1.19.1"
 .\"
 .\" libunbound.3 -- unbound library functions manual
 .\"
@@ -44,7 +44,7 @@
 .B ub_ctx_zone_remove,
 .B ub_ctx_data_add,
 .B ub_ctx_data_remove
-\- Unbound DNS validating resolver 1.19.0 functions.
+\- Unbound DNS validating resolver 1.19.1 functions.
 .SH "SYNOPSIS"
 .B #include <unbound.h>
 .LP
diff --git a/contrib/unbound/doc/unbound-anchor.8.in b/contrib/unbound/doc/unbound-anchor.8.in
index a108db9faa72..fee56e9dfa51 100644
--- a/contrib/unbound/doc/unbound-anchor.8.in
+++ b/contrib/unbound/doc/unbound-anchor.8.in
@@ -1,4 +1,4 @@
-.TH "unbound-anchor" "8" "Nov  8, 2023" "NLnet Labs" "unbound 1.19.0"
+.TH "unbound-anchor" "8" "Feb 13, 2024" "NLnet Labs" "unbound 1.19.1"
 .\"
 .\" unbound-anchor.8 -- unbound anchor maintenance utility manual
 .\"
diff --git a/contrib/unbound/doc/unbound-checkconf.8.in b/contrib/unbound/doc/unbound-checkconf.8.in
index b80c723cd3f0..9a14ef06bc3d 100644
--- a/contrib/unbound/doc/unbound-checkconf.8.in
+++ b/contrib/unbound/doc/unbound-checkconf.8.in
@@ -1,4 +1,4 @@
-.TH "unbound-checkconf" "8" "Nov  8, 2023" "NLnet Labs" "unbound 1.19.0"
+.TH "unbound-checkconf" "8" "Feb 13, 2024" "NLnet Labs" "unbound 1.19.1"
 .\"
 .\" unbound-checkconf.8 -- unbound configuration checker manual
 .\"
diff --git a/contrib/unbound/doc/unbound-control.8.in b/contrib/unbound/doc/unbound-control.8.in
index 44e73c93dfd5..e747ec47e25a 100644
--- a/contrib/unbound/doc/unbound-control.8.in
+++ b/contrib/unbound/doc/unbound-control.8.in
@@ -1,4 +1,4 @@
-.TH "unbound-control" "8" "Nov  8, 2023" "NLnet Labs" "unbound 1.19.0"
+.TH "unbound-control" "8" "Feb 13, 2024" "NLnet Labs" "unbound 1.19.1"
 .\"
 .\" unbound-control.8 -- unbound remote control manual
 .\"
diff --git a/contrib/unbound/doc/unbound-host.1.in b/contrib/unbound/doc/unbound-host.1.in
index 36f22ee9b6d1..9c9e9e2bf4a0 100644
--- a/contrib/unbound/doc/unbound-host.1.in
+++ b/contrib/unbound/doc/unbound-host.1.in
@@ -1,4 +1,4 @@
-.TH "unbound\-host" "1" "Nov  8, 2023" "NLnet Labs" "unbound 1.19.0"
+.TH "unbound\-host" "1" "Feb 13, 2024" "NLnet Labs" "unbound 1.19.1"
 .\"
 .\" unbound-host.1 -- unbound DNS lookup utility
 .\"
diff --git a/contrib/unbound/doc/unbound.8.in b/contrib/unbound/doc/unbound.8.in
index 3d56b7bfa190..4967a22d328c 100644
--- a/contrib/unbound/doc/unbound.8.in
+++ b/contrib/unbound/doc/unbound.8.in
@@ -1,4 +1,4 @@
-.TH "unbound" "8" "Nov  8, 2023" "NLnet Labs" "unbound 1.19.0"
+.TH "unbound" "8" "Feb 13, 2024" "NLnet Labs" "unbound 1.19.1"
 .\"
 .\" unbound.8 -- unbound manual
 .\"
@@ -9,7 +9,7 @@
 .\"
 .SH "NAME"
 .B unbound
-\- Unbound DNS validating resolver 1.19.0.
+\- Unbound DNS validating resolver 1.19.1.
 .SH "SYNOPSIS"
 .B unbound
 .RB [ \-h ]
diff --git a/contrib/unbound/doc/unbound.conf.5.in b/contrib/unbound/doc/unbound.conf.5.in
index ac8fa7953f3c..79ca04904c96 100644
--- a/contrib/unbound/doc/unbound.conf.5.in
+++ b/contrib/unbound/doc/unbound.conf.5.in
@@ -1,4 +1,4 @@
-.TH "unbound.conf" "5" "Nov  8, 2023" "NLnet Labs" "unbound 1.19.0"
+.TH "unbound.conf" "5" "Feb 13, 2024" "NLnet Labs" "unbound 1.19.1"
 .\"
 .\" unbound.conf.5 -- unbound.conf manual
 .\"
diff --git a/contrib/unbound/services/authzone.c b/contrib/unbound/services/authzone.c
index 87844870a25a..761bcc6d9a75 100644
--- a/contrib/unbound/services/authzone.c
+++ b/contrib/unbound/services/authzone.c
@@ -7774,6 +7774,7 @@ static int zonemd_dnssec_verify_rrset(struct auth_zone* z,
 	enum sec_status sec;
 	struct val_env* ve;
 	int m;
+	int verified = 0;
 	m = modstack_find(mods, "validator");
 	if(m == -1) {
 		auth_zone_log(z->name, VERB_ALGO, "zonemd dnssec verify: have "
@@ -7797,7 +7798,7 @@ static int zonemd_dnssec_verify_rrset(struct auth_zone* z,
 			"zonemd: verify %s RRset with DNSKEY", typestr);
 	}
 	sec = dnskeyset_verify_rrset(env, ve, &pk, dnskey, sigalg, why_bogus, NULL,
-		LDNS_SECTION_ANSWER, NULL);
+		LDNS_SECTION_ANSWER, NULL, &verified);
 	if(sec == sec_status_secure) {
 		return 1;
 	}
diff --git a/contrib/unbound/services/cache/dns.c b/contrib/unbound/services/cache/dns.c
index 9b4ad5888721..7bc1b7b47bf1 100644
--- a/contrib/unbound/services/cache/dns.c
+++ b/contrib/unbound/services/cache/dns.c
@@ -690,6 +690,28 @@ tomsg(struct module_env* env, struct query_info* q, struct reply_info* r,
 	return msg;
 }
 
+struct dns_msg*
+dns_msg_deepcopy_region(struct dns_msg* origin, struct regional* region)
+{
+	size_t i;
+	struct dns_msg* res = NULL;
+	res = gen_dns_msg(region, &origin->qinfo, origin->rep->rrset_count);
+	if(!res) return NULL;
+	*res->rep = *origin->rep;
+	if(origin->rep->reason_bogus_str) {
+		res->rep->reason_bogus_str = regional_strdup(region,
+			origin->rep->reason_bogus_str);
+	}
+	for(i=0; i<res->rep->rrset_count; i++) {
+		res->rep->rrsets[i] = packed_rrset_copy_region(
+			origin->rep->rrsets[i], region, 0);
+		if(!res->rep->rrsets[i]) {
+			return NULL;
+		}
+	}
+	return res;
+}
+
 /** synthesize RRset-only response from cached RRset item */
 static struct dns_msg*
 rrset_msg(struct ub_packed_rrset_key* rrset, struct regional* region, 
diff --git a/contrib/unbound/services/cache/dns.h b/contrib/unbound/services/cache/dns.h
index 147f992cbc74..c2bf23c6de54 100644
--- a/contrib/unbound/services/cache/dns.h
+++ b/contrib/unbound/services/cache/dns.h
@@ -164,6 +164,15 @@ struct dns_msg* tomsg(struct module_env* env, struct query_info* q,
 	struct reply_info* r, struct regional* region, time_t now,
 	int allow_expired, struct regional* scratch);
 
+/**
+ * Deep copy a dns_msg to a region.
+ * @param origin: the dns_msg to copy.
+ * @param region: the region to copy all the data to.
+ * @return the new dns_msg or NULL on malloc error.
+ */
+struct dns_msg* dns_msg_deepcopy_region(struct dns_msg* origin,
+	struct regional* region);
+
 /** 
  * Find cached message 
  * @param env: module environment with the DNS cache.
diff --git a/contrib/unbound/testdata/val_any_negcache.rpl b/contrib/unbound/testdata/val_any_negcache.rpl
index 77aacba8cc13..8800a2140219 100644
--- a/contrib/unbound/testdata/val_any_negcache.rpl
+++ b/contrib/unbound/testdata/val_any_negcache.rpl
@@ -199,6 +199,9 @@ SECTION QUESTION
 example.com. IN ANY
 ENTRY_END
 
+; Allow validation resuming for the RRSIGs
+STEP 21 TIME_PASSES ELAPSE 0.05
+
 ; recursion happens here.
 STEP 30 CHECK_ANSWER
 ENTRY_BEGIN
diff --git a/contrib/unbound/util/fptr_wlist.c b/contrib/unbound/util/fptr_wlist.c
index 43d38dc3797d..a792a3429549 100644
--- a/contrib/unbound/util/fptr_wlist.c
+++ b/contrib/unbound/util/fptr_wlist.c
@@ -131,6 +131,7 @@ fptr_whitelist_comm_timer(void (*fptr)(void*))
 	else if(fptr == &pending_udp_timer_delay_cb) return 1;
 	else if(fptr == &worker_stat_timer_cb) return 1;
 	else if(fptr == &worker_probe_timer_cb) return 1;
+	else if(fptr == &validate_suspend_timer_cb) return 1;
 #ifdef UB_ON_WINDOWS
 	else if(fptr == &wsvc_cron_cb) return 1;
 #endif
diff --git a/contrib/unbound/validator/val_nsec.c b/contrib/unbound/validator/val_nsec.c
index 17c90d83f594..d0cc67ff5d0b 100644
--- a/contrib/unbound/validator/val_nsec.c
+++ b/contrib/unbound/validator/val_nsec.c
@@ -181,6 +181,7 @@ nsec_verify_rrset(struct module_env* env, struct val_env* ve,
 {
 	struct packed_rrset_data* d = (struct packed_rrset_data*)
 		nsec->entry.data;
+	int verified = 0;
 	if(!d) return 0;
 	if(d->security == sec_status_secure)
 		return 1;
@@ -188,7 +189,7 @@ nsec_verify_rrset(struct module_env* env, struct val_env* ve,
 	if(d->security == sec_status_secure)
 		return 1;
 	d->security = val_verify_rrset_entry(env, ve, nsec, kkey, reason,
-		reason_bogus, LDNS_SECTION_AUTHORITY, qstate);
+		reason_bogus, LDNS_SECTION_AUTHORITY, qstate, &verified);
 	if(d->security == sec_status_secure) {
 		rrset_update_sec_status(env->rrset_cache, nsec, *env->now);
 		return 1;
diff --git a/contrib/unbound/validator/val_nsec3.c b/contrib/unbound/validator/val_nsec3.c
index a2b3794f6019..95d1e4d7e4fe 100644
--- a/contrib/unbound/validator/val_nsec3.c
+++ b/contrib/unbound/validator/val_nsec3.c
@@ -57,6 +57,19 @@
 /* we include nsec.h for the bitmap_has_type function */
 #include "validator/val_nsec.h"
 #include "sldns/sbuffer.h"
+#include "util/config_file.h"
+
+/**
+ * Max number of NSEC3 calculations at once, suspend query for later.
+ * 8 is low enough and allows for cases where multiple proofs are needed.
+ */
+#define MAX_NSEC3_CALCULATIONS 8
+/**
+ * When all allowed NSEC3 calculations at once resulted in error treat as
+ * bogus. NSEC3 hash errors are not cached and this helps breaks loops with
+ * erroneous data.
+ */
+#define MAX_NSEC3_ERRORS -1
 
 /** 
  * This function we get from ldns-compat or from base system 
@@ -532,6 +545,17 @@ nsec3_hash_cmp(const void* c1, const void* c2)
 	return memcmp(s1, s2, s1len);
 }
 
+int
+nsec3_cache_table_init(struct nsec3_cache_table* ct, struct regional* region)
+{
+	if(ct->ct) return 1;
+	ct->ct = (rbtree_type*)regional_alloc(region, sizeof(*ct->ct));
+	if(!ct->ct) return 0;
+	ct->region = region;
+	rbtree_init(ct->ct, &nsec3_hash_cmp);
+	return 1;
+}
+
 size_t
 nsec3_get_hashed(sldns_buffer* buf, uint8_t* nm, size_t nmlen, int algo, 
 	size_t iter, uint8_t* salt, size_t saltlen, uint8_t* res, size_t max)
@@ -646,7 +670,7 @@ nsec3_hash_name(rbtree_type* table, struct regional* region, sldns_buffer* buf,
 	c = (struct nsec3_cached_hash*)rbtree_search(table, &looki);
 	if(c) {
 		*hash = c;
-		return 1;
+		return 2;
 	}
 	/* create a new entry */
 	c = (struct nsec3_cached_hash*)regional_alloc(region, sizeof(*c));
@@ -658,10 +682,10 @@ nsec3_hash_name(rbtree_type* table, struct regional* region, sldns_buffer* buf,
 	c->dname_len = dname_len;
 	r = nsec3_calc_hash(region, buf, c);
 	if(r != 1)
-		return r;
+		return r;  /* returns -1 or 0 */
 	r = nsec3_calc_b32(region, buf, c);
 	if(r != 1)
-		return r;
+		return r;  /* returns 0 */
 #ifdef UNBOUND_DEBUG
 	n =
 #else
@@ -704,6 +728,7 @@ nsec3_hash_matches_owner(struct nsec3_filter* flt,
 	struct nsec3_cached_hash* hash, struct ub_packed_rrset_key* s)
 {
 	uint8_t* nm = s->rk.dname;
+	if(!hash) return 0; /* please clang */
 	/* compare, does hash of name based on params in this NSEC3
 	 * match the owner name of this NSEC3? 
 	 * name must be: <hashlength>base32 . zone name 
@@ -730,34 +755,50 @@ nsec3_hash_matches_owner(struct nsec3_filter* flt,
  * @param nmlen: length of name.
  * @param rrset: nsec3 that matches is returned here.
  * @param rr: rr number in nsec3 rrset that matches.
+ * @param calculations: current hash calculations.
  * @return true if a matching NSEC3 is found, false if not.
  */
 static int
 find_matching_nsec3(struct module_env* env, struct nsec3_filter* flt,
-	rbtree_type* ct, uint8_t* nm, size_t nmlen, 
-	struct ub_packed_rrset_key** rrset, int* rr)
+	struct nsec3_cache_table* ct, uint8_t* nm, size_t nmlen,
+	struct ub_packed_rrset_key** rrset, int* rr,
+	int* calculations)
 {
 	size_t i_rs;
 	int i_rr;
 	struct ub_packed_rrset_key* s;
 	struct nsec3_cached_hash* hash = NULL;
 	int r;
+	int calc_errors = 0;
 
 	/* this loop skips other-zone and unknown NSEC3s, also non-NSEC3 RRs */
 	for(s=filter_first(flt, &i_rs, &i_rr); s; 
 		s=filter_next(flt, &i_rs, &i_rr)) {
+		/* check if we are allowed more calculations */
+		if(*calculations >= MAX_NSEC3_CALCULATIONS) {
+			if(calc_errors == *calculations) {
+				*calculations = MAX_NSEC3_ERRORS;
+			}
+			break;
+		}
 		/* get name hashed for this NSEC3 RR */
-		r = nsec3_hash_name(ct, env->scratch, env->scratch_buffer,
+		r = nsec3_hash_name(ct->ct, ct->region, env->scratch_buffer,
 			s, i_rr, nm, nmlen, &hash);
 		if(r == 0) {
 			log_err("nsec3: malloc failure");
 			break; /* alloc failure */
-		} else if(r != 1)
-			continue; /* malformed NSEC3 */
-		else if(nsec3_hash_matches_owner(flt, hash, s)) {
-			*rrset = s; /* rrset with this name */
-			*rr = i_rr; /* matches hash with these parameters */
-			return 1;
+		} else if(r < 0) {
+			/* malformed NSEC3 */
+			calc_errors++;
+			(*calculations)++;
+			continue;
+		} else {
+			if(r == 1) (*calculations)++;
+			if(nsec3_hash_matches_owner(flt, hash, s)) {
+				*rrset = s; /* rrset with this name */
+				*rr = i_rr; /* matches hash with these parameters */
+				return 1;
+			}
 		}
 	}
 	*rrset = NULL;
@@ -775,6 +816,7 @@ nsec3_covers(uint8_t* zone, struct nsec3_cached_hash* hash,
 	if(!nsec3_get_nextowner(rrset, rr, &next, &nextlen))
 		return 0; /* malformed RR proves nothing */
 
+	if(!hash) return 0; /* please clang */
 	/* check the owner name is a hashed value . apex
 	 * base32 encoded values must have equal length. 
 	 * hash_value and next hash value must have equal length. */
@@ -823,35 +865,51 @@ nsec3_covers(uint8_t* zone, struct nsec3_cached_hash* hash,
  * @param nmlen: length of name.
  * @param rrset: covering NSEC3 rrset is returned here.
  * @param rr: rr of cover is returned here.
+ * @param calculations: current hash calculations.
  * @return true if a covering NSEC3 is found, false if not.
  */
 static int
 find_covering_nsec3(struct module_env* env, struct nsec3_filter* flt,
-        rbtree_type* ct, uint8_t* nm, size_t nmlen, 
-	struct ub_packed_rrset_key** rrset, int* rr)
+	struct nsec3_cache_table* ct, uint8_t* nm, size_t nmlen,
+	struct ub_packed_rrset_key** rrset, int* rr,
+	int* calculations)
 {
 	size_t i_rs;
 	int i_rr;
 	struct ub_packed_rrset_key* s;
 	struct nsec3_cached_hash* hash = NULL;
 	int r;
+	int calc_errors = 0;
 
 	/* this loop skips other-zone and unknown NSEC3s, also non-NSEC3 RRs */
 	for(s=filter_first(flt, &i_rs, &i_rr); s; 
 		s=filter_next(flt, &i_rs, &i_rr)) {
+		/* check if we are allowed more calculations */
+		if(*calculations >= MAX_NSEC3_CALCULATIONS) {
+			if(calc_errors == *calculations) {
+				*calculations = MAX_NSEC3_ERRORS;
+			}
+			break;
+		}
 		/* get name hashed for this NSEC3 RR */
-		r = nsec3_hash_name(ct, env->scratch, env->scratch_buffer,
+		r = nsec3_hash_name(ct->ct, ct->region, env->scratch_buffer,
 			s, i_rr, nm, nmlen, &hash);
 		if(r == 0) {
 			log_err("nsec3: malloc failure");
 			break; /* alloc failure */
-		} else if(r != 1)
-			continue; /* malformed NSEC3 */
-		else if(nsec3_covers(flt->zone, hash, s, i_rr, 
-			env->scratch_buffer)) {
-			*rrset = s; /* rrset with this name */
-			*rr = i_rr; /* covers hash with these parameters */
-			return 1;
+		} else if(r < 0) {
+			/* malformed NSEC3 */
+			calc_errors++;
+			(*calculations)++;
+			continue;
+		} else {
+			if(r == 1) (*calculations)++;
+			if(nsec3_covers(flt->zone, hash, s, i_rr,
+				env->scratch_buffer)) {
+				*rrset = s; /* rrset with this name */
+				*rr = i_rr; /* covers hash with these parameters */
+				return 1;
+			}
 		}
 	}
 	*rrset = NULL;
@@ -869,11 +927,13 @@ find_covering_nsec3(struct module_env* env, struct nsec3_filter* flt,
  * @param ct: cached hashes table.
  * @param qinfo: query that is verified for.
  * @param ce: closest encloser information is returned in here.
+ * @param calculations: current hash calculations.
  * @return true if a closest encloser candidate is found, false if not.
  */
 static int
-nsec3_find_closest_encloser(struct module_env* env, struct nsec3_filter* flt, 
-	rbtree_type* ct, struct query_info* qinfo, struct ce_response* ce)
+nsec3_find_closest_encloser(struct module_env* env, struct nsec3_filter* flt,
+	struct nsec3_cache_table* ct, struct query_info* qinfo,
+	struct ce_response* ce, int* calculations)
 {
 	uint8_t* nm = qinfo->qname;
 	size_t nmlen = qinfo->qname_len;
@@ -888,8 +948,12 @@ nsec3_find_closest_encloser(struct module_env* env, struct nsec3_filter* flt,
 	 * may be the case. */
 
 	while(dname_subdomain_c(nm, flt->zone)) {
+		if(*calculations >= MAX_NSEC3_CALCULATIONS ||
+			*calculations == MAX_NSEC3_ERRORS) {
+			return 0;
+		}
 		if(find_matching_nsec3(env, flt, ct, nm, nmlen, 
-			&ce->ce_rrset, &ce->ce_rr)) {
+			&ce->ce_rrset, &ce->ce_rr, calculations)) {
 			ce->ce = nm;
 			ce->ce_len = nmlen;
 			return 1;
@@ -933,22 +997,38 @@ next_closer(uint8_t* qname, size_t qnamelen, uint8_t* ce,
  * 	If set true, and the return value is true, then you can be 
  * 	certain that the ce.nc_rrset and ce.nc_rr are set properly.
  * @param ce: closest encloser information is returned in here.
+ * @param calculations: pointer to the current NSEC3 hash calculations.
  * @return bogus if no closest encloser could be proven.
  * 	secure if a closest encloser could be proven, ce is set.
  * 	insecure if the closest-encloser candidate turns out to prove
  * 		that an insecure delegation exists above the qname.
+ *	unchecked if no more hash calculations are allowed at this point.
  */
 static enum sec_status
-nsec3_prove_closest_encloser(struct module_env* env, struct nsec3_filter* flt, 
-	rbtree_type* ct, struct query_info* qinfo, int prove_does_not_exist,
-	struct ce_response* ce)
+nsec3_prove_closest_encloser(struct module_env* env, struct nsec3_filter* flt,
+	struct nsec3_cache_table* ct, struct query_info* qinfo,
+	int prove_does_not_exist, struct ce_response* ce, int* calculations)
 {
 	uint8_t* nc;
 	size_t nc_len;
 	/* robust: clean out ce, in case it gets abused later */
 	memset(ce, 0, sizeof(*ce));
 
-	if(!nsec3_find_closest_encloser(env, flt, ct, qinfo, ce)) {
+	if(!nsec3_find_closest_encloser(env, flt, ct, qinfo, ce, calculations)) {
+		if(*calculations == MAX_NSEC3_ERRORS) {
+			verbose(VERB_ALGO, "nsec3 proveClosestEncloser: could "
+				"not find a candidate for the closest "
+				"encloser; all attempted hash calculations "
+				"were erroneous; bogus");
+			return sec_status_bogus;
+		} else if(*calculations >= MAX_NSEC3_CALCULATIONS) {
+			verbose(VERB_ALGO, "nsec3 proveClosestEncloser: could "
+				"not find a candidate for the closest "
+				"encloser; reached MAX_NSEC3_CALCULATIONS "
+				"(%d); unchecked still",
+				MAX_NSEC3_CALCULATIONS);
+			return sec_status_unchecked;
+		}
 		verbose(VERB_ALGO, "nsec3 proveClosestEncloser: could "
 			"not find a candidate for the closest encloser.");
 		return sec_status_bogus;
@@ -989,9 +1069,23 @@ nsec3_prove_closest_encloser(struct module_env* env, struct nsec3_filter* flt,
 	/* Otherwise, we need to show that the next closer name is covered. */
 	next_closer(qinfo->qname, qinfo->qname_len, ce->ce, &nc, &nc_len);
 	if(!find_covering_nsec3(env, flt, ct, nc, nc_len, 
-		&ce->nc_rrset, &ce->nc_rr)) {
+		&ce->nc_rrset, &ce->nc_rr, calculations)) {
+		if(*calculations == MAX_NSEC3_ERRORS) {
+			verbose(VERB_ALGO, "nsec3: Could not find proof that the "
+				"candidate encloser was the closest encloser; "
+				"all attempted hash calculations were "
+				"erroneous; bogus");
+			return sec_status_bogus;
+		} else if(*calculations >= MAX_NSEC3_CALCULATIONS) {
+			verbose(VERB_ALGO, "nsec3: Could not find proof that the "
+				"candidate encloser was the closest encloser; "
+				"reached MAX_NSEC3_CALCULATIONS (%d); "
+				"unchecked still",
+				MAX_NSEC3_CALCULATIONS);
+			return sec_status_unchecked;
+		}
 		verbose(VERB_ALGO, "nsec3: Could not find proof that the "
-		          "candidate encloser was the closest encloser");
+			"candidate encloser was the closest encloser");
 		return sec_status_bogus;
 	}
 	return sec_status_secure;
@@ -1019,8 +1113,8 @@ nsec3_ce_wildcard(struct regional* region, uint8_t* ce, size_t celen,
 
 /** Do the name error proof */
 static enum sec_status
-nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt, 
-	rbtree_type* ct, struct query_info* qinfo)
+nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt,
+	struct nsec3_cache_table* ct, struct query_info* qinfo, int* calc)
 {
 	struct ce_response ce;
 	uint8_t* wc;
@@ -1032,11 +1126,15 @@ nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt,
 	/* First locate and prove the closest encloser to qname. We will 
 	 * use the variant that fails if the closest encloser turns out 
 	 * to be qname. */
-	sec = nsec3_prove_closest_encloser(env, flt, ct, qinfo, 1, &ce);
+	sec = nsec3_prove_closest_encloser(env, flt, ct, qinfo, 1, &ce, calc);
 	if(sec != sec_status_secure) {
 		if(sec == sec_status_bogus)
 			verbose(VERB_ALGO, "nsec3 nameerror proof: failed "
 				"to prove a closest encloser");
+		else if(sec == sec_status_unchecked)
+			verbose(VERB_ALGO, "nsec3 nameerror proof: will "
+				"continue proving closest encloser after "
+				"suspend");
 		else 	verbose(VERB_ALGO, "nsec3 nameerror proof: closest "
 				"nsec3 is an insecure delegation");
 		return sec;
@@ -1046,9 +1144,27 @@ nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt,
 	/* At this point, we know that qname does not exist. Now we need 
 	 * to prove that the wildcard does not exist. */
 	log_assert(ce.ce);
-	wc = nsec3_ce_wildcard(env->scratch, ce.ce, ce.ce_len, &wclen);
-	if(!wc || !find_covering_nsec3(env, flt, ct, wc, wclen, 
-		&wc_rrset, &wc_rr)) {
+	wc = nsec3_ce_wildcard(ct->region, ce.ce, ce.ce_len, &wclen);
+	if(!wc) {
+		verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove "
+			"that the applicable wildcard did not exist.");
+		return sec_status_bogus;
+	}
+	if(!find_covering_nsec3(env, flt, ct, wc, wclen, &wc_rrset, &wc_rr, calc)) {
+		if(*calc == MAX_NSEC3_ERRORS) {
+			verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove "
+				"that the applicable wildcard did not exist; "
+				"all attempted hash calculations were "
+				"erroneous; bogus");
+			return sec_status_bogus;
+		} else if(*calc >= MAX_NSEC3_CALCULATIONS) {
+			verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove "
+				"that the applicable wildcard did not exist; "
+				"reached MAX_NSEC3_CALCULATIONS (%d); "
+				"unchecked still",
+				MAX_NSEC3_CALCULATIONS);
+			return sec_status_unchecked;
+		}
 		verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove "
 			"that the applicable wildcard did not exist.");
 		return sec_status_bogus;
@@ -1064,14 +1180,13 @@ nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt,
 enum sec_status
 nsec3_prove_nameerror(struct module_env* env, struct val_env* ve,
 	struct ub_packed_rrset_key** list, size_t num,
-	struct query_info* qinfo, struct key_entry_key* kkey)
+	struct query_info* qinfo, struct key_entry_key* kkey,
+	struct nsec3_cache_table* ct, int* calc)
 {
-	rbtree_type ct;
 	struct nsec3_filter flt;
 
 	if(!list || num == 0 || !kkey || !key_entry_isgood(kkey))
 		return sec_status_bogus; /* no valid NSEC3s, bogus */
-	rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */
 	filter_init(&flt, list, num, qinfo); /* init RR iterator */
 	if(!flt.zone)
 		return sec_status_bogus; /* no RRs */
@@ -1079,7 +1194,7 @@ nsec3_prove_nameerror(struct module_env* env, struct val_env* ve,
 		return sec_status_insecure; /* iteration count too high */
 	log_nametypeclass(VERB_ALGO, "start nsec3 nameerror proof, zone", 
 		flt.zone, 0, 0);
-	return nsec3_do_prove_nameerror(env, &flt, &ct, qinfo);
+	return nsec3_do_prove_nameerror(env, &flt, ct, qinfo, calc);
 }
 
 /* 
@@ -1089,8 +1204,9 @@ nsec3_prove_nameerror(struct module_env* env, struct val_env* ve,
 
 /** Do the nodata proof */
 static enum sec_status
-nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, 
-	rbtree_type* ct, struct query_info* qinfo)
+nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt,
+	struct nsec3_cache_table* ct, struct query_info* qinfo,
+	int* calc)
 {
 	struct ce_response ce;
 	uint8_t* wc;
@@ -1100,7 +1216,7 @@ nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt,
 	enum sec_status sec;
 
 	if(find_matching_nsec3(env, flt, ct, qinfo->qname, qinfo->qname_len, 
-		&rrset, &rr)) {
+		&rrset, &rr, calc)) {
 		/* cases 1 and 2 */
 		if(nsec3_has_type(rrset, rr, qinfo->qtype)) {
 			verbose(VERB_ALGO, "proveNodata: Matching NSEC3 "
@@ -1144,11 +1260,23 @@ nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt,
 		}
 		return sec_status_secure;
 	}
+	if(*calc == MAX_NSEC3_ERRORS) {
+		verbose(VERB_ALGO, "proveNodata: all attempted hash "
+			"calculations were erroneous while finding a matching "
+			"NSEC3, bogus");
+		return sec_status_bogus;
+	} else if(*calc >= MAX_NSEC3_CALCULATIONS) {
+		verbose(VERB_ALGO, "proveNodata: reached "
*** 1724 LINES SKIPPED ***