aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCy Schubert <cy@FreeBSD.org>2022-07-13 13:02:01 +0000
committerCy Schubert <cy@FreeBSD.org>2022-07-13 13:02:01 +0000
commitd57351465531b38689892ec862de2725b52842dd (patch)
tree7363167508f031fdb90b9091c07a065f3903325d
parent5f9f82264b91e041df7cba2406625146e7268ce4 (diff)
-rw-r--r--Makefile.in12
-rw-r--r--config.h.in4
-rwxr-xr-xconfigure109
-rw-r--r--configure.ac11
-rw-r--r--contrib/metrics.awk1
-rwxr-xr-xcontrib/unbound_munin_3
-rw-r--r--daemon/daemon.c2
-rw-r--r--daemon/remote.c2
-rw-r--r--daemon/stats.c2
-rw-r--r--daemon/worker.c24
-rw-r--r--doc/Changelog80
-rw-r--r--doc/README2
-rw-r--r--doc/example.conf.in6
-rw-r--r--doc/libunbound.3.in4
-rw-r--r--doc/unbound-anchor.8.in2
-rw-r--r--doc/unbound-checkconf.8.in2
-rw-r--r--doc/unbound-control.8.in6
-rw-r--r--doc/unbound-host.1.in2
-rw-r--r--doc/unbound.8.in6
-rw-r--r--doc/unbound.conf.5.in10
-rw-r--r--edns-subnet/subnetmod.c49
-rw-r--r--edns-subnet/subnetmod.h5
-rw-r--r--iterator/iter_delegpt.c21
-rw-r--r--iterator/iter_delegpt.h3
-rw-r--r--iterator/iter_utils.c6
-rw-r--r--iterator/iterator.c204
-rw-r--r--iterator/iterator.h29
-rw-r--r--libunbound/unbound.h2
-rw-r--r--services/authzone.c4
-rw-r--r--services/mesh.c3
-rw-r--r--services/outside_network.c12
-rw-r--r--services/outside_network.h2
-rw-r--r--sldns/parse.c55
-rw-r--r--smallapp/unbound-control.c2
-rw-r--r--testcode/dohclient.c2
-rwxr-xr-xtestcode/mini_tdir.sh23
-rw-r--r--testcode/unitmain.c2
-rw-r--r--testdata/auth_https.tdir/auth_https.test12
-rw-r--r--testdata/blanks_cached_zone.tdir/blanks.example.com.zone23
-rw-r--r--testdata/blanks_cached_zone.tdir/blanks_cached_zone.conf20
-rw-r--r--testdata/blanks_cached_zone.tdir/blanks_cached_zone.dsc16
-rw-r--r--testdata/blanks_cached_zone.tdir/blanks_cached_zone.post10
-rw-r--r--testdata/blanks_cached_zone.tdir/blanks_cached_zone.pre23
-rw-r--r--testdata/blanks_cached_zone.tdir/blanks_cached_zone.test51
-rw-r--r--testdata/blanks_https.tdir/127.0.0.1/blanks.example.com.zone23
-rw-r--r--testdata/blanks_https.tdir/blanks_https.conf18
-rw-r--r--testdata/blanks_https.tdir/blanks_https.dsc16
-rw-r--r--testdata/blanks_https.tdir/blanks_https.post11
-rw-r--r--testdata/blanks_https.tdir/blanks_https.pre34
-rw-r--r--testdata/blanks_https.tdir/blanks_https.test52
-rw-r--r--testdata/blanks_https.tdir/petal.key21
-rw-r--r--testdata/blanks_https.tdir/petal.pem14
-rw-r--r--testdata/common.sh10
-rw-r--r--testdata/iter_nxns_cached.rpl384
-rw-r--r--testdata/iter_nxns_fallback.rpl380
-rw-r--r--testdata/iter_nxns_parentside.rpl118
-rw-r--r--testdata/serve_expired_client_timeout_no_prefetch.rpl110
-rw-r--r--testdata/stat_values.tdir/stat_values.conf5
-rw-r--r--testdata/stat_values.tdir/stat_values.test6
-rw-r--r--testdata/subnet_prefetch.crpl69
-rw-r--r--testdata/subnet_prefetch_with_client_ecs.crpl84
-rw-r--r--util/iana_ports.inc9
-rw-r--r--util/net_help.c10
-rw-r--r--validator/val_secalgo.c127
-rw-r--r--validator/val_sigcrypt.c148
-rw-r--r--validator/val_utils.c2
66 files changed, 2194 insertions, 326 deletions
diff --git a/Makefile.in b/Makefile.in
index 7dbe5760033b..3189731ad52f 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -345,14 +345,12 @@ test: unittest$(EXEEXT) testbound$(EXEEXT)
./unittest$(EXEEXT)
./testbound$(EXEEXT) -s
for x in $(srcdir)/testdata/*.rpl; do \
- printf "%s" "$$x "; \
- if ./testbound$(EXEEXT) -p $$x >/dev/null 2>&1; then \
- echo OK; \
+ output=`./testbound$(EXEEXT) -p $$x -o -vvvvv 2>&1`; \
+ if test $$? -eq 0; then \
+ printf "%s OK\n" "$$x "; \
else \
- echo failed; \
- ./testbound$(EXEEXT) -p $$x -o -vvvvv; \
- printf "%s" "$$x "; \
- echo failed; \
+ printf "%s\n" "$$output "; \
+ printf "%s failed\n" "$$x "; \
exit 1; \
fi; \
done
diff --git a/config.h.in b/config.h.in
index a080dde0da2e..cc1fbe864818 100644
--- a/config.h.in
+++ b/config.h.in
@@ -222,6 +222,10 @@
/* Define to 1 if you have the `EVP_cleanup' function. */
#undef HAVE_EVP_CLEANUP
+/* Define to 1 if you have the `EVP_default_properties_is_fips_enabled'
+ function. */
+#undef HAVE_EVP_DEFAULT_PROPERTIES_IS_FIPS_ENABLED
+
/* Define to 1 if you have the `EVP_DigestVerify' function. */
#undef HAVE_EVP_DIGESTVERIFY
diff --git a/configure b/configure
index a9ec94479b55..0029d5b42782 100755
--- a/configure
+++ b/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.16.0.
+# Generated by GNU Autoconf 2.69 for unbound 1.16.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.16.0'
-PACKAGE_STRING='unbound 1.16.0'
+PACKAGE_VERSION='1.16.1'
+PACKAGE_STRING='unbound 1.16.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.16.0 to adapt to many kinds of systems.
+\`configure' configures unbound 1.16.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.16.0:";;
+ short | recursive ) echo "Configuration of unbound 1.16.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.16.0
+unbound configure 1.16.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.16.0, which was
+It was created by unbound $as_me 1.16.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=16
-UNBOUND_VERSION_MICRO=0
+UNBOUND_VERSION_MICRO=1
LIBUNBOUND_CURRENT=9
-LIBUNBOUND_REVISION=16
+LIBUNBOUND_REVISION=17
LIBUNBOUND_AGE=1
# 1.0.0 had 0:12:0
# 1.0.1 had 0:13:0
@@ -2934,6 +2934,7 @@ LIBUNBOUND_AGE=1
# 1.14.0 had 9:14:1
# 1.15.0 had 9:15:1
# 1.16.0 had 9:16:1
+# 1.16.1 had 9:17:1
# Current -- the number of the binary API that we're implementing
# Revision -- which iteration of the implementation of the binary
@@ -18545,7 +18546,7 @@ fi
done
-for ac_func in OPENSSL_config EVP_sha1 EVP_sha256 EVP_sha512 FIPS_mode EVP_MD_CTX_new OpenSSL_add_all_digests OPENSSL_init_crypto EVP_cleanup ENGINE_cleanup ERR_load_crypto_strings CRYPTO_cleanup_all_ex_data ERR_free_strings RAND_cleanup DSA_SIG_set0 EVP_dss1 EVP_DigestVerify EVP_aes_256_cbc EVP_EncryptInit_ex HMAC_Init_ex CRYPTO_THREADID_set_callback EVP_MAC_CTX_set_params OSSL_PARAM_BLD_new BIO_set_callback_ex
+for ac_func in OPENSSL_config EVP_sha1 EVP_sha256 EVP_sha512 FIPS_mode EVP_default_properties_is_fips_enabled EVP_MD_CTX_new OpenSSL_add_all_digests OPENSSL_init_crypto EVP_cleanup ENGINE_cleanup ERR_load_crypto_strings CRYPTO_cleanup_all_ex_data ERR_free_strings RAND_cleanup DSA_SIG_set0 EVP_dss1 EVP_DigestVerify EVP_aes_256_cbc EVP_EncryptInit_ex HMAC_Init_ex CRYPTO_THREADID_set_callback EVP_MAC_CTX_set_params OSSL_PARAM_BLD_new BIO_set_callback_ex
do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
@@ -19967,7 +19968,46 @@ if test x_$enable_static_exe = x_yes; then
else
LIBS="$LIBS -lgdi32"
fi
- LIBS="$LIBS -lz"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for compress in -lz" >&5
+$as_echo_n "checking for compress in -lz... " >&6; }
+if ${ac_cv_lib_z_compress+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lz $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 compress ();
+int
+main ()
+{
+return compress ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_z_compress=yes
+else
+ ac_cv_lib_z_compress=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_z_compress" >&5
+$as_echo "$ac_cv_lib_z_compress" >&6; }
+if test "x$ac_cv_lib_z_compress" = xyes; then :
+ LIBS="$LIBS -lz"
+fi
+
LIBS="$LIBS -l:libssp.a"
fi
fi
@@ -19987,7 +20027,46 @@ if test x_$enable_fully_static = x_yes; then
else
LIBS="$LIBS -lgdi32"
fi
- LIBS="$LIBS -lz"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for compress in -lz" >&5
+$as_echo_n "checking for compress in -lz... " >&6; }
+if ${ac_cv_lib_z_compress+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lz $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 compress ();
+int
+main ()
+{
+return compress ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_z_compress=yes
+else
+ ac_cv_lib_z_compress=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_z_compress" >&5
+$as_echo "$ac_cv_lib_z_compress" >&6; }
+if test "x$ac_cv_lib_z_compress" = xyes; then :
+ LIBS="$LIBS -lz"
+fi
+
LIBS="$LIBS -l:libssp.a"
fi
fi
@@ -21934,7 +22013,7 @@ _ACEOF
-version=1.16.0
+version=1.16.1
date=`date +'%b %e, %Y'`
@@ -22453,7 +22532,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.16.0, which was
+This file was extended by unbound $as_me 1.16.1, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -22519,7 +22598,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.16.0
+unbound config.status 1.16.1
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
diff --git a/configure.ac b/configure.ac
index 1453b3a2fe29..e41c811ae826 100644
--- a/configure.ac
+++ b/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],[16])
-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=16
+LIBUNBOUND_REVISION=17
LIBUNBOUND_AGE=1
# 1.0.0 had 0:12:0
# 1.0.1 had 0:13:0
@@ -102,6 +102,7 @@ LIBUNBOUND_AGE=1
# 1.14.0 had 9:14:1
# 1.15.0 had 9:15:1
# 1.16.0 had 9:16:1
+# 1.16.1 had 9:17:1
# Current -- the number of the binary API that we're implementing
# Revision -- which iteration of the implementation of the binary
@@ -906,7 +907,7 @@ else
AC_MSG_RESULT([no])
fi
AC_CHECK_HEADERS([openssl/conf.h openssl/engine.h openssl/bn.h openssl/dh.h openssl/dsa.h openssl/rsa.h openssl/core_names.h openssl/param_build.h],,, [AC_INCLUDES_DEFAULT])
-AC_CHECK_FUNCS([OPENSSL_config EVP_sha1 EVP_sha256 EVP_sha512 FIPS_mode EVP_MD_CTX_new OpenSSL_add_all_digests OPENSSL_init_crypto EVP_cleanup ENGINE_cleanup ERR_load_crypto_strings CRYPTO_cleanup_all_ex_data ERR_free_strings RAND_cleanup DSA_SIG_set0 EVP_dss1 EVP_DigestVerify EVP_aes_256_cbc EVP_EncryptInit_ex HMAC_Init_ex CRYPTO_THREADID_set_callback EVP_MAC_CTX_set_params OSSL_PARAM_BLD_new BIO_set_callback_ex])
+AC_CHECK_FUNCS([OPENSSL_config EVP_sha1 EVP_sha256 EVP_sha512 FIPS_mode EVP_default_properties_is_fips_enabled EVP_MD_CTX_new OpenSSL_add_all_digests OPENSSL_init_crypto EVP_cleanup ENGINE_cleanup ERR_load_crypto_strings CRYPTO_cleanup_all_ex_data ERR_free_strings RAND_cleanup DSA_SIG_set0 EVP_dss1 EVP_DigestVerify EVP_aes_256_cbc EVP_EncryptInit_ex HMAC_Init_ex CRYPTO_THREADID_set_callback EVP_MAC_CTX_set_params OSSL_PARAM_BLD_new BIO_set_callback_ex])
# these check_funcs need -lssl
BAKLIBS="$LIBS"
@@ -1499,7 +1500,7 @@ if test x_$enable_static_exe = x_yes; then
else
LIBS="$LIBS -lgdi32"
fi
- LIBS="$LIBS -lz"
+ AC_CHECK_LIB([z], [compress], [ LIBS="$LIBS -lz" ])
LIBS="$LIBS -l:libssp.a"
fi
fi
@@ -1516,7 +1517,7 @@ if test x_$enable_fully_static = x_yes; then
else
LIBS="$LIBS -lgdi32"
fi
- LIBS="$LIBS -lz"
+ AC_CHECK_LIB([z], [compress], [ LIBS="$LIBS -lz" ])
LIBS="$LIBS -l:libssp.a"
fi
fi
diff --git a/contrib/metrics.awk b/contrib/metrics.awk
index 5a7a2569c29a..ca48c035aa0e 100644
--- a/contrib/metrics.awk
+++ b/contrib/metrics.awk
@@ -28,6 +28,7 @@ END {
print "unbound_hits_queries{type=\"total.num.prefetch\"} " val["total.num.prefetch"];
print "unbound_hits_queries{type=\"num.query.tcp\"} " val["num.query.tcp"];
print "unbound_hits_queries{type=\"num.query.tcpout\"} " val["num.query.tcpout"];
+ print "unbound_hits_queries{type=\"num.query.udpout\"} " val["num.query.udpout"];
print "unbound_hits_queries{type=\"num.query.tls\"} " val["num.query.tls"];
print "unbound_hits_queries{type=\"num.query.tls.resume\"} " val["num.query.tls.resume"];
print "unbound_hits_queries{type=\"num.query.ipv6\"} " val["num.query.ipv6"];
diff --git a/contrib/unbound_munin_ b/contrib/unbound_munin_
index 5037527580e2..a756a5d1ca20 100755
--- a/contrib/unbound_munin_
+++ b/contrib/unbound_munin_
@@ -253,6 +253,7 @@ if test "$1" = "config" ; then
p_config "total.num.prefetch" "cache prefetch" "ABSOLUTE"
p_config "num.query.tcp" "TCP queries" "ABSOLUTE"
p_config "num.query.tcpout" "TCP out queries" "ABSOLUTE"
+ p_config "num.query.udpout" "UDP out queries" "ABSOLUTE"
p_config "num.query.tls" "TLS queries" "ABSOLUTE"
p_config "num.query.tls.resume" "TLS resumes" "ABSOLUTE"
p_config "num.query.ipv6" "IPv6 queries" "ABSOLUTE"
@@ -452,7 +453,7 @@ hits)
for x in `grep "^thread[0-9][0-9]*\.num\.queries=" $state |
sed -e 's/=.*//'` total.num.queries \
total.num.cachehits total.num.prefetch num.query.tcp \
- num.query.tcpout num.query.tls num.query.tls.resume \
+ num.query.tcpout num.query.udpout num.query.tls num.query.tls.resume \
num.query.ipv6 unwanted.queries \
unwanted.replies; do
if grep "^"$x"=" $state >/dev/null 2>&1; then
diff --git a/daemon/daemon.c b/daemon/daemon.c
index 0e3923b4e9f2..4ed531855ee6 100644
--- a/daemon/daemon.c
+++ b/daemon/daemon.c
@@ -795,7 +795,7 @@ daemon_delete(struct daemon* daemon)
ub_c_lex_destroy();
/* libcrypto cleanup */
#ifdef HAVE_SSL
-# if defined(USE_GOST) && defined(HAVE_LDNS_KEY_EVP_UNLOAD_GOST)
+# if defined(USE_GOST)
sldns_key_EVP_unload_gost();
# endif
# if HAVE_DECL_SSL_COMP_GET_COMPRESSION_METHODS && HAVE_DECL_SK_SSL_COMP_POP_FREE
diff --git a/daemon/remote.c b/daemon/remote.c
index 675ef43970d1..ec7a4d5d93f4 100644
--- a/daemon/remote.c
+++ b/daemon/remote.c
@@ -988,6 +988,8 @@ print_ext(RES* ssl, struct ub_stats_info* s)
(unsigned long)s->svr.qtcp)) return 0;
if(!ssl_printf(ssl, "num.query.tcpout"SQ"%lu\n",
(unsigned long)s->svr.qtcp_outgoing)) return 0;
+ if(!ssl_printf(ssl, "num.query.udpout"SQ"%lu\n",
+ (unsigned long)s->svr.qudp_outgoing)) return 0;
if(!ssl_printf(ssl, "num.query.tls"SQ"%lu\n",
(unsigned long)s->svr.qtls)) return 0;
if(!ssl_printf(ssl, "num.query.tls.resume"SQ"%lu\n",
diff --git a/daemon/stats.c b/daemon/stats.c
index d08f18dbb137..57c42827161c 100644
--- a/daemon/stats.c
+++ b/daemon/stats.c
@@ -281,6 +281,7 @@ server_stats_compile(struct worker* worker, struct ub_stats_info* s, int reset)
/* values from outside network */
s->svr.unwanted_replies = (long long)worker->back->unwanted_replies;
s->svr.qtcp_outgoing = (long long)worker->back->num_tcp_outgoing;
+ s->svr.qudp_outgoing = (long long)worker->back->num_udp_outgoing;
/* get and reset validator rrset bogus number */
s->svr.rrset_bogus = (long long)get_rrset_bogus(worker, reset);
@@ -424,6 +425,7 @@ void server_stats_add(struct ub_stats_info* total, struct ub_stats_info* a)
total->svr.qclass_big += a->svr.qclass_big;
total->svr.qtcp += a->svr.qtcp;
total->svr.qtcp_outgoing += a->svr.qtcp_outgoing;
+ total->svr.qudp_outgoing += a->svr.qudp_outgoing;
total->svr.qtls += a->svr.qtls;
total->svr.qtls_resume += a->svr.qtls_resume;
total->svr.qhttps += a->svr.qhttps;
diff --git a/daemon/worker.c b/daemon/worker.c
index bf8c5d6b6763..27626ce938ca 100644
--- a/daemon/worker.c
+++ b/daemon/worker.c
@@ -1639,10 +1639,11 @@ lookup_cache:
is_secure_answer = 0;
h = query_info_hash(lookup_qinfo, sldns_buffer_read_u16_at(c->buffer, 2));
if((e=slabhash_lookup(worker->env.msg_cache, h, lookup_qinfo, 0))) {
+ struct reply_info* rep = (struct reply_info*)e->data;
/* answer from cache - we have acquired a readlock on it */
- if(answer_from_cache(worker, &qinfo,
- cinfo, &need_drop, &is_expired_answer, &is_secure_answer,
- &alias_rrset, &partial_rep, (struct reply_info*)e->data,
+ if(answer_from_cache(worker, &qinfo, cinfo, &need_drop,
+ &is_expired_answer, &is_secure_answer,
+ &alias_rrset, &partial_rep, rep,
*(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
sldns_buffer_read_u16_at(c->buffer, 2), repinfo,
&edns)) {
@@ -1650,15 +1651,13 @@ lookup_cache:
* Note that if there is more than one pass
* its qname must be that used for cache
* lookup. */
- if((worker->env.cfg->prefetch && *worker->env.now >=
- ((struct reply_info*)e->data)->prefetch_ttl) ||
- (worker->env.cfg->serve_expired &&
- *worker->env.now >= ((struct reply_info*)e->data)->ttl)) {
-
- time_t leeway = ((struct reply_info*)e->
- data)->ttl - *worker->env.now;
- if(((struct reply_info*)e->data)->ttl
- < *worker->env.now)
+ if((worker->env.cfg->prefetch &&
+ *worker->env.now >= rep->prefetch_ttl) ||
+ (worker->env.cfg->serve_expired &&
+ *worker->env.now > rep->ttl)) {
+
+ time_t leeway = rep->ttl - *worker->env.now;
+ if(rep->ttl < *worker->env.now)
leeway = 0;
lock_rw_unlock(&e->lock);
@@ -2218,6 +2217,7 @@ void worker_stats_clear(struct worker* worker)
mesh_stats_clear(worker->env.mesh);
worker->back->unwanted_replies = 0;
worker->back->num_tcp_outgoing = 0;
+ worker->back->num_udp_outgoing = 0;
}
void worker_start_accept(void* arg)
diff --git a/doc/Changelog b/doc/Changelog
index 8df5f367c4e1..d3573190e7e2 100644
--- a/doc/Changelog
+++ b/doc/Changelog
@@ -1,6 +1,84 @@
+4 July 2022: George
+ - Fix bug introduced in 'improve val_sigcrypt.c::algo_needs_missing for
+ one loop pass'.
+ - Merge PR #668 from Cristian Rodríguez: Set IP_BIND_ADDRESS_NO_PORT on
+ outbound tcp sockets.
+
+4 July 2022: Wouter
+ - Tag for 1.16.1rc1 release.
+
+3 July 2022: George
+ - Merge PR #671 from Petr Menšík: Disable ED25519 and ED448 in FIPS
+ mode on openssl3.
+ - Merge PR #660 from Petr Menšík: Sha1 runtime insecure.
+ - For #660: formatting, less verbose logging, add EDE information.
+ - Fix for correct openssl error when adding windows CA certificates to
+ the openssl trust store.
+ - Improve val_sigcrypt.c::algo_needs_missing for one loop pass.
+ - Reintroduce documentation and more EDE support for
+ val_sigcrypt.c::dnskeyset_verify_rrset_sig.
+
+1 July 2022: George
+ - Merge PR #706: NXNS fallback.
+ - From #706: Cached NXDOMAIN does not increase the target nx
+ responses.
+ - From #706: Don't generate parent side queries if we already
+ have the lame records in cache.
+ - From #706: When a lame address is the best choice, don't try to
+ generate target queries when the missing targets are all lame.
+
+29 June 2022: Wouter
+ - iana portlist update.
+ - Fix detection of libz on windows compile with static option.
+ - Fix compile warning for windows compile.
+
+29 June 2022: George
+ - Add debug option to the mini_tdir.sh test code.
+ - Fix #704: [FR] Statistics counter for number of outgoing UDP queries
+ sent; introduces 'num.query.udpout' to the 'unbound-control stats'
+ command.
+ - Fix to not count cached NXDOMAIN for MAX_TARGET_NX.
+ - Allow fallback to the parent side when MAX_TARGET_NX is reached.
+ This will also allow MAX_TARGET_NX more NXDOMAINs.
+
+28 June 2022: George
+ - Show the output of the exact .rpl run that failed with 'make test'.
+ - Fix for cached 0 TTL records to not trigger prefetching when
+ serve-expired-client-timeout is set.
+
+28 June 2022: Wouter
+ - Fix test program dohclient close to use portability routine.
+
+23 June 2022: Tom
+ - Clarify -v flag manpage entry (#705)
+
+22 June 2022: Philip
+ - Fix #663: use after free issue with edns options.
+
+21 June 2022: Philip
+ - Fix for loading locally stored zones that have lines with blanks or
+ blanks and comments.
+
+20 June 2022: George
+ - Remove unused LDNS function check for GOST Engine unloading.
+
+14 June 2022: George
+ - Merge PR #688: Rpz url notify issue.
+ - Note in the unbound.conf text that NOTIFY is allowed from the url:
+ addresses for auth and rpz zones.
+
+3 June 2022: George
+ - Fix for edns client subnet to respect not looking in its cache when
+ instructed to do so (e.g., prefetch).
+
+3 June 2022: Wouter
+ - makedist.sh picks up 32bit libssp-0.dll when 32bit compile.
+
27 May 2022: Wouter
- Fix #684: [FTBS] configure script error with libmnl on openSUSE 15.3 (and possibly other distributions)
- - Version is set to 1.16.0 for release. Release tag 1.16.0rc1.
+ - Version is set to 1.16.0 for release. Release tag 1.16.0rc1. This
+ became release 1.16.0 on 2 June 2022. The source code branch
+ continues with version 1.16.1 under development.
20 May 2022: Wouter
- Fix to silence test for ede error output to the console from the
diff --git a/doc/README b/doc/README
index ea93afddcd5f..13992ac7f9ec 100644
--- a/doc/README
+++ b/doc/README
@@ -1,4 +1,4 @@
-README for Unbound 1.16.0
+README for Unbound 1.16.1
Copyright 2007 NLnet Labs
http://unbound.net
diff --git a/doc/example.conf.in b/doc/example.conf.in
index 64adfe9e5e9c..b01d2c58dbfe 100644
--- a/doc/example.conf.in
+++ b/doc/example.conf.in
@@ -1,7 +1,7 @@
#
# Example configuration file.
#
-# See unbound.conf(5) man page, version 1.16.0.
+# See unbound.conf(5) man page, version 1.16.1.
#
# this is a comment.
@@ -1045,8 +1045,8 @@ remote-control:
# has a copy of the root for local usage. The second serves example.org
# authoritatively. zonefile: reads from file (and writes to it if you also
# download it), primary: fetches with AXFR and IXFR, or url to zonefile.
-# With allow-notify: you can give additional (apart from primaries) sources of
-# notifies.
+# With allow-notify: you can give additional (apart from primaries and urls)
+# sources of notifies.
# auth-zone:
# name: "."
# primary: 199.9.14.201 # b.root-servers.net
diff --git a/doc/libunbound.3.in b/doc/libunbound.3.in
index b1be90ce0f0f..8049e3ae29d3 100644
--- a/doc/libunbound.3.in
+++ b/doc/libunbound.3.in
@@ -1,4 +1,4 @@
-.TH "libunbound" "3" "Jun 2, 2022" "NLnet Labs" "unbound 1.16.0"
+.TH "libunbound" "3" "Jul 11, 2022" "NLnet Labs" "unbound 1.16.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.16.0 functions.
+\- Unbound DNS validating resolver 1.16.1 functions.
.SH "SYNOPSIS"
.B #include <unbound.h>
.LP
diff --git a/doc/unbound-anchor.8.in b/doc/unbound-anchor.8.in
index 4da37b1d5ff9..85b71fd30b8e 100644
--- a/doc/unbound-anchor.8.in
+++ b/doc/unbound-anchor.8.in
@@ -1,4 +1,4 @@
-.TH "unbound-anchor" "8" "Jun 2, 2022" "NLnet Labs" "unbound 1.16.0"
+.TH "unbound-anchor" "8" "Jul 11, 2022" "NLnet Labs" "unbound 1.16.1"
.\"
.\" unbound-anchor.8 -- unbound anchor maintenance utility manual
.\"
diff --git a/doc/unbound-checkconf.8.in b/doc/unbound-checkconf.8.in
index 4c607a231b9f..8133feeaa364 100644
--- a/doc/unbound-checkconf.8.in
+++ b/doc/unbound-checkconf.8.in
@@ -1,4 +1,4 @@
-.TH "unbound-checkconf" "8" "Jun 2, 2022" "NLnet Labs" "unbound 1.16.0"
+.TH "unbound-checkconf" "8" "Jul 11, 2022" "NLnet Labs" "unbound 1.16.1"
.\"
.\" unbound-checkconf.8 -- unbound configuration checker manual
.\"
diff --git a/doc/unbound-control.8.in b/doc/unbound-control.8.in
index 3ef1d659f58a..128101e2f887 100644
--- a/doc/unbound-control.8.in
+++ b/doc/unbound-control.8.in
@@ -1,4 +1,4 @@
-.TH "unbound-control" "8" "Jun 2, 2022" "NLnet Labs" "unbound 1.16.0"
+.TH "unbound-control" "8" "Jul 11, 2022" "NLnet Labs" "unbound 1.16.1"
.\"
.\" unbound-control.8 -- unbound remote control manual
.\"
@@ -552,6 +552,10 @@ Number of queries that were made using TCP towards the Unbound server.
Number of queries that the Unbound server made using TCP outgoing towards
other servers.
.TP
+.I num.query.udpout
+Number of queries that the Unbound server made using UDP outgoing towards
+other servers.
+.TP
.I num.query.tls
Number of queries that were made using TLS towards the Unbound server.
These are also counted in num.query.tcp, because TLS uses TCP.
diff --git a/doc/unbound-host.1.in b/doc/unbound-host.1.in
index a30d1dfd216f..fb73e625df47 100644
--- a/doc/unbound-host.1.in
+++ b/doc/unbound-host.1.in
@@ -1,4 +1,4 @@
-.TH "unbound\-host" "1" "Jun 2, 2022" "NLnet Labs" "unbound 1.16.0"
+.TH "unbound\-host" "1" "Jul 11, 2022" "NLnet Labs" "unbound 1.16.1"
.\"
.\" unbound-host.1 -- unbound DNS lookup utility
.\"
diff --git a/doc/unbound.8.in b/doc/unbound.8.in
index e3492724c95d..bc768c6a151b 100644
--- a/doc/unbound.8.in
+++ b/doc/unbound.8.in
@@ -1,4 +1,4 @@
-.TH "unbound" "8" "Jun 2, 2022" "NLnet Labs" "unbound 1.16.0"
+.TH "unbound" "8" "Jul 11, 2022" "NLnet Labs" "unbound 1.16.1"
.\"
.\" unbound.8 -- unbound manual
.\"
@@ -9,7 +9,7 @@
.\"
.SH "NAME"
.B unbound
-\- Unbound DNS validating resolver 1.16.0.
+\- Unbound DNS validating resolver 1.16.1.
.SH "SYNOPSIS"
.B unbound
.RB [ \-h ]
@@ -75,7 +75,7 @@ concurrently.
.TP
.B \-v
Increase verbosity. If given multiple times, more information is logged.
-This is in addition to the verbosity (if any) from the config file.
+This is added to the verbosity (if any) from the config file.
.TP
.B \-V
Show the version number and build options, and exit.
diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in
index 3c891aa59e28..1157a2d1975f 100644
--- a/doc/unbound.conf.5.in
+++ b/doc/unbound.conf.5.in
@@ -1,4 +1,4 @@
-.TH "unbound.conf" "5" "Jun 2, 2022" "NLnet Labs" "unbound 1.16.0"
+.TH "unbound.conf" "5" "Jul 11, 2022" "NLnet Labs" "unbound 1.16.1"
.\"
.\" unbound.conf.5 -- unbound.conf manual
.\"
@@ -2067,8 +2067,8 @@ With allow\-notify you can specify additional sources of notifies.
When notified, the server attempts to first probe and then zone transfer.
If the notify is from a primary, it first attempts that primary. Otherwise
other primaries are attempted. If there are no primaries, but only urls, the
-file is downloaded when notified. The primaries from primary: statements are
-allowed notify by default.
+file is downloaded when notified. The primaries from primary: and url:
+statements are allowed notify by default.
.TP
.B fallback\-enabled: \fI<yes or no>
Default no. If enabled, Unbound falls back to querying the internet as
@@ -2682,8 +2682,8 @@ With allow\-notify you can specify additional sources of notifies.
When notified, the server attempts to first probe and then zone transfer.
If the notify is from a primary, it first attempts that primary. Otherwise
other primaries are attempted. If there are no primaries, but only urls, the
-file is downloaded when notified. The primaries from primary: statements are
-allowed notify by default.
+file is downloaded when notified. The primaries from primary: and url:
+statements are allowed notify by default.
.TP
.B zonefile: \fI<filename>
The filename where the zone is stored. If not given then no zonefile is used.
diff --git a/edns-subnet/subnetmod.c b/edns-subnet/subnetmod.c
index 25190b040d45..75446113b742 100644
--- a/edns-subnet/subnetmod.c
+++ b/edns-subnet/subnetmod.c
@@ -93,13 +93,14 @@ subnet_new_qstate(struct module_qstate *qstate, int id)
qstate->minfo[id] = sq;
memset(sq, 0, sizeof(*sq));
sq->started_no_cache_store = qstate->no_cache_store;
+ sq->started_no_cache_lookup = qstate->no_cache_lookup;
return 1;
}
/** Add ecs struct to edns list, after parsing it to wire format. */
void
subnet_ecs_opt_list_append(struct ecs_data* ecs, struct edns_option** list,
- struct module_qstate *qstate)
+ struct module_qstate *qstate, struct regional *region)
{
size_t sn_octs, sn_octs_remainder;
sldns_buffer* buf = qstate->env->scratch_buffer;
@@ -131,7 +132,7 @@ subnet_ecs_opt_list_append(struct ecs_data* ecs, struct edns_option** list,
edns_opt_list_append(list,
qstate->env->cfg->client_subnet_opcode,
sn_octs + sn_octs_remainder + 4,
- sldns_buffer_begin(buf), qstate->region);
+ sldns_buffer_begin(buf), region);
}
}
@@ -139,7 +140,7 @@ int ecs_whitelist_check(struct query_info* qinfo,
uint16_t ATTR_UNUSED(flags), struct module_qstate* qstate,
struct sockaddr_storage* addr, socklen_t addrlen,
uint8_t* ATTR_UNUSED(zone), size_t ATTR_UNUSED(zonelen),
- struct regional* ATTR_UNUSED(region), int id, void* ATTR_UNUSED(cbargs))
+ struct regional *region, int id, void* ATTR_UNUSED(cbargs))
{
struct subnet_qstate *sq;
struct subnet_env *sn_env;
@@ -165,7 +166,7 @@ int ecs_whitelist_check(struct query_info* qinfo,
if(!edns_opt_list_find(qstate->edns_opts_back_out,
qstate->env->cfg->client_subnet_opcode)) {
subnet_ecs_opt_list_append(&sq->ecs_server_out,
- &qstate->edns_opts_back_out, qstate);
+ &qstate->edns_opts_back_out, qstate, region);
}
sq->subnet_sent = 1;
}
@@ -331,9 +332,11 @@ update_cache(struct module_qstate *qstate, int id)
struct ecs_data *edns = &sq->ecs_client_in;
size_t i;
- /* We already calculated hash upon lookup */
- hashvalue_type h = qstate->minfo[id] ?
- ((struct subnet_qstate*)qstate->minfo[id])->qinfo_hash :
+ /* We already calculated hash upon lookup (lookup_and_reply) if we were
+ * allowed to look in the ECS cache */
+ hashvalue_type h = qstate->minfo[id] &&
+ ((struct subnet_qstate*)qstate->minfo[id])->qinfo_hash_calculated?
+ ((struct subnet_qstate*)qstate->minfo[id])->qinfo_hash :
query_info_hash(&qstate->qinfo, qstate->query_flags);
/* Step 1, general qinfo lookup */
struct lruhash_entry *lru_entry = slabhash_lookup(subnet_msg_cache, h,
@@ -416,7 +419,10 @@ lookup_and_reply(struct module_qstate *qstate, int id, struct subnet_qstate *sq)
memset(&sq->ecs_client_out, 0, sizeof(sq->ecs_client_out));
- if (sq) sq->qinfo_hash = h; /* Might be useful on cache miss */
+ if (sq) {
+ sq->qinfo_hash = h; /* Might be useful on cache miss */
+ sq->qinfo_hash_calculated = 1;
+ }
e = slabhash_lookup(sne->subnet_msg_cache, h, &qstate->qinfo, 1);
if (!e) return 0; /* qinfo not in cache */
data = e->data;
@@ -758,18 +764,21 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
return;
}
- lock_rw_wrlock(&sne->biglock);
- if (lookup_and_reply(qstate, id, sq)) {
- sne->num_msg_cache++;
- lock_rw_unlock(&sne->biglock);
- verbose(VERB_QUERY, "subnetcache: answered from cache");
- qstate->ext_state[id] = module_finished;
+ if(!sq->started_no_cache_lookup && !qstate->blacklist) {
+ lock_rw_wrlock(&sne->biglock);
+ if(lookup_and_reply(qstate, id, sq)) {
+ sne->num_msg_cache++;
+ lock_rw_unlock(&sne->biglock);
+ verbose(VERB_QUERY, "subnetcache: answered from cache");
+ qstate->ext_state[id] = module_finished;
- subnet_ecs_opt_list_append(&sq->ecs_client_out,
- &qstate->edns_opts_front_out, qstate);
- return;
+ subnet_ecs_opt_list_append(&sq->ecs_client_out,
+ &qstate->edns_opts_front_out, qstate,
+ qstate->region);
+ return;
+ }
+ lock_rw_unlock(&sne->biglock);
}
- lock_rw_unlock(&sne->biglock);
sq->ecs_server_out.subnet_addr_fam =
sq->ecs_client_in.subnet_addr_fam;
@@ -812,9 +821,11 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
if(qstate->ext_state[id] == module_finished &&
qstate->return_msg) {
subnet_ecs_opt_list_append(&sq->ecs_client_out,
- &qstate->edns_opts_front_out, qstate);
+ &qstate->edns_opts_front_out, qstate,
+ qstate->region);
}
qstate->no_cache_store = sq->started_no_cache_store;
+ qstate->no_cache_lookup = sq->started_no_cache_lookup;
return;
}
if(sq && outbound) {
diff --git a/edns-subnet/subnetmod.h b/edns-subnet/subnetmod.h
index c877692b46b1..f0bcaad33e15 100644
--- a/edns-subnet/subnetmod.h
+++ b/edns-subnet/subnetmod.h
@@ -76,6 +76,7 @@ struct subnet_msg_cache_data {
struct subnet_qstate {
/** We need the hash for both cache lookup and insert */
hashvalue_type qinfo_hash;
+ int qinfo_hash_calculated;
/** ecs_data for client communication */
struct ecs_data ecs_client_in;
struct ecs_data ecs_client_out;
@@ -92,6 +93,8 @@ struct subnet_qstate {
uint8_t max_scope;
/** has the subnet module been started with no_cache_store? */
int started_no_cache_store;
+ /** has the subnet module been started with no_cache_lookup? */
+ int started_no_cache_lookup;
};
void subnet_data_delete(void* d, void* ATTR_UNUSED(arg));
@@ -145,7 +148,7 @@ void subnet_markdel(void* key);
/** Add ecs struct to edns list, after parsing it to wire format. */
void subnet_ecs_opt_list_append(struct ecs_data* ecs, struct edns_option** list,
- struct module_qstate *qstate);
+ struct module_qstate *qstate, struct regional *region);
/** Create ecs_data from the sockaddr_storage information. */
void subnet_option_from_ss(struct sockaddr_storage *ss, struct ecs_data* ecs,
diff --git a/iterator/iter_delegpt.c b/iterator/iter_delegpt.c
index 80148e810890..4bffa1b3a7d5 100644
--- a/iterator/iter_delegpt.c
+++ b/iterator/iter_delegpt.c
@@ -185,6 +185,10 @@ delegpt_add_target(struct delegpt* dp, struct regional* region,
else ns->got4 = 1;
if(ns->got4 && ns->got6)
ns->resolved = 1;
+ } else {
+ if(addr_is_ip6(addr, addrlen))
+ ns->done_pside6 = 1;
+ else ns->done_pside4 = 1;
}
log_assert(ns->port>0);
return delegpt_add_addr(dp, region, addr, addrlen, bogus, lame,
@@ -338,13 +342,16 @@ delegpt_count_targets(struct delegpt* dp)
}
size_t
-delegpt_count_missing_targets(struct delegpt* dp)
+delegpt_count_missing_targets(struct delegpt* dp, int* alllame)
{
struct delegpt_ns* ns;
- size_t n = 0;
- for(ns = dp->nslist; ns; ns = ns->next)
- if(!ns->resolved)
- n++;
+ size_t n = 0, nlame = 0;
+ for(ns = dp->nslist; ns; ns = ns->next) {
+ if(ns->resolved) continue;
+ n++;
+ if(ns->lame) nlame++;
+ }
+ if(alllame && n == nlame) *alllame = 1;
return n;
}
@@ -694,6 +701,10 @@ int delegpt_add_target_mlc(struct delegpt* dp, uint8_t* name, size_t namelen,
else ns->got4 = 1;
if(ns->got4 && ns->got6)
ns->resolved = 1;
+ } else {
+ if(addr_is_ip6(addr, addrlen))
+ ns->done_pside6 = 1;
+ else ns->done_pside4 = 1;
}
log_assert(ns->port>0);
return delegpt_add_addr_mlc(dp, addr, addrlen, bogus, lame,
diff --git a/iterator/iter_delegpt.h b/iterator/iter_delegpt.h
index 998b98cd803e..62c8edc51225 100644
--- a/iterator/iter_delegpt.h
+++ b/iterator/iter_delegpt.h
@@ -330,9 +330,10 @@ void delegpt_add_unused_targets(struct delegpt* dp);
/**
* Count number of missing targets. These are ns names with no resolved flag.
* @param dp: delegation point.
+ * @param alllame: if set, check if all the missing targets are lame.
* @return number of missing targets (or 0).
*/
-size_t delegpt_count_missing_targets(struct delegpt* dp);
+size_t delegpt_count_missing_targets(struct delegpt* dp, int* alllame);
/** count total number of targets in dp */
size_t delegpt_count_targets(struct delegpt* dp);
diff --git a/iterator/iter_utils.c b/iterator/iter_utils.c
index f3bea46d6c95..6d159157a995 100644
--- a/iterator/iter_utils.c
+++ b/iterator/iter_utils.c
@@ -367,6 +367,7 @@ iter_filter_order(struct iter_env* iter_env, struct module_env* env,
struct sock_list* blacklist, time_t prefetch)
{
int got_num = 0, low_rtt = 0, swap_to_front, rtt_band = RTT_BAND, nth;
+ int alllame = 0;
size_t num_results;
struct delegpt_addr* a, *n, *prev=NULL;
@@ -376,7 +377,10 @@ iter_filter_order(struct iter_env* iter_env, struct module_env* env,
if(got_num == 0)
return 0;
if(low_rtt >= USEFUL_SERVER_TOP_TIMEOUT &&
- (delegpt_count_missing_targets(dp) > 0 || open_target > 0)) {
+ /* If all missing (or not fully resolved) targets are lame,
+ * then use the remaining lame address. */
+ ((delegpt_count_missing_targets(dp, &alllame) > 0 && !alllame) ||
+ open_target > 0)) {
verbose(VERB_ALGO, "Bad choices, trying to get more choice");
return 0; /* we want more choice. The best choice is a bad one.
return 0 to force the caller to fetch more */
diff --git a/iterator/iterator.c b/iterator/iterator.c
index 3cfb286f4491..727631d6cf8e 100644
--- a/iterator/iterator.c
+++ b/iterator/iterator.c
@@ -253,8 +253,9 @@ error_supers(struct module_qstate* qstate, int id, struct module_qstate* super)
delegpt_mark_neg(dpns, qstate->qinfo.qtype);
dpns->resolved = 1; /* mark as failed */
if((dpns->got4 == 2 || !ie->supports_ipv4) &&
- (dpns->got6 == 2 || !ie->supports_ipv6))
+ (dpns->got6 == 2 || !ie->supports_ipv6)) {
target_count_increase_nx(super_iq, 1);
+ }
}
if(qstate->qinfo.qtype == LDNS_RR_TYPE_NS) {
/* prime failed to get delegation */
@@ -678,15 +679,20 @@ is_caps_whitelisted(struct iter_env* ie, struct iter_qstate* iq)
iq->qchase.qclass) != NULL;
}
-/** create target count structure for this query */
+/**
+ * Create target count structure for this query. This is always explicitly
+ * created for the parent query.
+ */
static void
target_count_create(struct iter_qstate* iq)
{
if(!iq->target_count) {
- iq->target_count = (int*)calloc(3, sizeof(int));
+ iq->target_count = (int*)calloc(TARGET_COUNT_MAX, sizeof(int));
/* if calloc fails we simply do not track this number */
- if(iq->target_count)
- iq->target_count[0] = 1;
+ if(iq->target_count) {
+ iq->target_count[TARGET_COUNT_REF] = 1;
+ iq->nxns_dp = (uint8_t**)calloc(1, sizeof(uint8_t*));
+ }
}
}
@@ -695,7 +701,7 @@ target_count_increase(struct iter_qstate* iq, int num)
{
target_count_create(iq);
if(iq->target_count)
- iq->target_count[1] += num;
+ iq->target_count[TARGET_COUNT_QUERIES] += num;
iq->dp_target_count++;
}
@@ -704,7 +710,7 @@ target_count_increase_nx(struct iter_qstate* iq, int num)
{
target_count_create(iq);
if(iq->target_count)
- iq->target_count[2] += num;
+ iq->target_count[TARGET_COUNT_NX] += num;
}
/**
@@ -799,8 +805,10 @@ generate_sub_request(uint8_t* qname, size_t qnamelen, uint16_t qtype,
subiq->num_target_queries = 0;
target_count_create(iq);
subiq->target_count = iq->target_count;
- if(iq->target_count)
- iq->target_count[0] ++; /* extra reference */
+ if(iq->target_count) {
+ iq->target_count[TARGET_COUNT_REF] ++; /* extra reference */
+ subiq->nxns_dp = iq->nxns_dp;
+ }
subiq->dp_target_count = 0;
subiq->num_current_queries = 0;
subiq->depth = iq->depth+1;
@@ -1832,7 +1840,7 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq,
int toget = 0;
iter_mark_cycle_targets(qstate, iq->dp);
- missing = (int)delegpt_count_missing_targets(iq->dp);
+ missing = (int)delegpt_count_missing_targets(iq->dp, NULL);
log_assert(maxtargets != 0); /* that would not be useful */
/* Generate target requests. Basically, any missing targets
@@ -1851,11 +1859,12 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq,
if(iq->depth == ie->max_dependency_depth)
return 0;
if(iq->depth > 0 && iq->target_count &&
- iq->target_count[1] > MAX_TARGET_COUNT) {
+ iq->target_count[TARGET_COUNT_QUERIES] > MAX_TARGET_COUNT) {
char s[LDNS_MAX_DOMAINLEN+1];
dname_str(qstate->qinfo.qname, s);
verbose(VERB_QUERY, "request %s has exceeded the maximum "
- "number of glue fetches %d", s, iq->target_count[1]);
+ "number of glue fetches %d", s,
+ iq->target_count[TARGET_COUNT_QUERIES]);
return 0;
}
if(iq->dp_target_count > MAX_DP_TARGET_COUNT) {
@@ -1883,7 +1892,9 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq,
continue;
}
- if(ie->supports_ipv6 && !ns->got6) {
+ if(ie->supports_ipv6 &&
+ ((ns->lame && !ns->done_pside6) ||
+ (!ns->lame && !ns->got6))) {
/* Send the AAAA request. */
if(!generate_target_query(qstate, iq, id,
ns->name, ns->namelen,
@@ -1896,7 +1907,9 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq,
query_count++;
}
/* Send the A request. */
- if(ie->supports_ipv4 && !ns->got4) {
+ if(ie->supports_ipv4 &&
+ ((ns->lame && !ns->done_pside4) ||
+ (!ns->lame && !ns->got4))) {
if(!generate_target_query(qstate, iq, id,
ns->name, ns->namelen,
LDNS_RR_TYPE_A, iq->qchase.qclass)) {
@@ -2006,7 +2019,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
return next_state(iq, QUERYTARGETS_STATE);
}
/* query for an extra name added by the parent-NS record */
- if(delegpt_count_missing_targets(iq->dp) > 0) {
+ if(delegpt_count_missing_targets(iq->dp, NULL) > 0) {
int qs = 0;
verbose(VERB_ALGO, "try parent-side target name");
if(!query_for_targets(qstate, iq, ie, id, 1, &qs)) {
@@ -2027,11 +2040,12 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
if(iq->depth > 0 && iq->target_count &&
- iq->target_count[1] > MAX_TARGET_COUNT) {
+ iq->target_count[TARGET_COUNT_QUERIES] > MAX_TARGET_COUNT) {
char s[LDNS_MAX_DOMAINLEN+1];
dname_str(qstate->qinfo.qname, s);
verbose(VERB_QUERY, "request %s has exceeded the maximum "
- "number of glue fetches %d", s, iq->target_count[1]);
+ "number of glue fetches %d", s,
+ iq->target_count[TARGET_COUNT_QUERIES]);
errinf(qstate, "exceeded the maximum number of glue fetches");
return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
@@ -2158,6 +2172,32 @@ processDSNSFind(struct module_qstate* qstate, struct iter_qstate* iq, int id)
return 0;
}
+
+/**
+ * Check if we wait responses for sent queries and update the iterator's
+ * external state.
+ */
+static void
+check_waiting_queries(struct iter_qstate* iq, struct module_qstate* qstate,
+ int id)
+{
+ if(iq->num_target_queries>0 && iq->num_current_queries>0) {
+ verbose(VERB_ALGO, "waiting for %d targets to "
+ "resolve or %d outstanding queries to "
+ "respond", iq->num_target_queries,
+ iq->num_current_queries);
+ qstate->ext_state[id] = module_wait_reply;
+ } else if(iq->num_target_queries>0) {
+ verbose(VERB_ALGO, "waiting for %d targets to "
+ "resolve", iq->num_target_queries);
+ qstate->ext_state[id] = module_wait_subquery;
+ } else {
+ verbose(VERB_ALGO, "waiting for %d "
+ "outstanding queries to respond",
+ iq->num_current_queries);
+ qstate->ext_state[id] = module_wait_reply;
+ }
+}
/**
* This is the request event state where the request will be sent to one of
@@ -2211,12 +2251,91 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
errinf(qstate, "exceeded the maximum number of sends");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
- if(iq->target_count && iq->target_count[2] > MAX_TARGET_NX) {
- verbose(VERB_QUERY, "request has exceeded the maximum "
- " number of nxdomain nameserver lookups with %d",
- iq->target_count[2]);
- errinf(qstate, "exceeded the maximum nameserver nxdomains");
- return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+
+ /* Check if we reached MAX_TARGET_NX limit without a fallback activation. */
+ if(iq->target_count && !*iq->nxns_dp &&
+ iq->target_count[TARGET_COUNT_NX] > MAX_TARGET_NX) {
+ struct delegpt_ns* ns;
+ /* If we can wait for resolution, do so. */
+ if(iq->num_target_queries>0 || iq->num_current_queries>0) {
+ check_waiting_queries(iq, qstate, id);
+ return 0;
+ }
+ verbose(VERB_ALGO, "request has exceeded the maximum "
+ "number of nxdomain nameserver lookups (%d) with %d",
+ MAX_TARGET_NX, iq->target_count[TARGET_COUNT_NX]);
+ /* Check for dp because we require one below */
+ if(!iq->dp) {
+ verbose(VERB_QUERY, "Failed to get a delegation, "
+ "giving up");
+ errinf(qstate, "failed to get a delegation (eg. prime "
+ "failure)");
+ return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+ }
+ /* We reached the limit but we already have parent side
+ * information; stop resolution */
+ if(iq->dp->has_parent_side_NS) {
+ verbose(VERB_ALGO, "parent-side information is "
+ "already present for the delegation point, no "
+ "fallback possible");
+ errinf(qstate, "exceeded the maximum nameserver nxdomains");
+ return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+ }
+ verbose(VERB_ALGO, "initiating parent-side fallback for "
+ "nxdomain nameserver lookups");
+ /* Mark all the current NSes as resolved to allow for parent
+ * fallback */
+ for(ns=iq->dp->nslist; ns; ns=ns->next) {
+ ns->resolved = 1;
+ }
+ /* Note the delegation point that triggered the NXNS fallback;
+ * no reason for shared queries to keep trying there.
+ * This also marks the fallback activation. */
+ *iq->nxns_dp = malloc(iq->dp->namelen);
+ if(!*iq->nxns_dp) {
+ verbose(VERB_ALGO, "out of memory while initiating "
+ "fallback");
+ errinf(qstate, "exceeded the maximum nameserver "
+ "nxdomains (malloc)");
+ return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+ }
+ memcpy(*iq->nxns_dp, iq->dp->name, iq->dp->namelen);
+ } else if(iq->target_count && *iq->nxns_dp) {
+ /* Handle the NXNS fallback case. */
+ /* If we can wait for resolution, do so. */
+ if(iq->num_target_queries>0 || iq->num_current_queries>0) {
+ check_waiting_queries(iq, qstate, id);
+ return 0;
+ }
+ /* Check for dp because we require one below */
+ if(!iq->dp) {
+ verbose(VERB_QUERY, "Failed to get a delegation, "
+ "giving up");
+ errinf(qstate, "failed to get a delegation (eg. prime "
+ "failure)");
+ return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+ }
+
+ if(iq->target_count[TARGET_COUNT_NX] > MAX_TARGET_NX_FALLBACK) {
+ verbose(VERB_ALGO, "request has exceeded the maximum "
+ "number of fallback nxdomain nameserver "
+ "lookups (%d) with %d", MAX_TARGET_NX_FALLBACK,
+ iq->target_count[TARGET_COUNT_NX]);
+ errinf(qstate, "exceeded the maximum nameserver nxdomains");
+ return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+ }
+
+ if(!iq->dp->has_parent_side_NS) {
+ struct delegpt_ns* ns;
+ if(!dname_canonical_compare(*iq->nxns_dp, iq->dp->name)) {
+ verbose(VERB_ALGO, "this delegation point "
+ "initiated the fallback, marking the "
+ "nslist as resolved");
+ for(ns=iq->dp->nslist; ns; ns=ns->next) {
+ ns->resolved = 1;
+ }
+ }
+ }
}
/* Make sure we have a delegation point, otherwise priming failed
@@ -2434,7 +2553,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
* that servfail is cached, which is not good as opportunism goes. */
if(iq->depth < ie->max_dependency_depth
&& iq->num_target_queries == 0
- && (!iq->target_count || iq->target_count[2]==0)
+ && (!iq->target_count || iq->target_count[TARGET_COUNT_NX]==0)
&& iq->sent_count < TARGET_FETCH_STOP) {
tf_policy = ie->target_fetch_policy[iq->depth];
}
@@ -2523,9 +2642,9 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
}
/* Select the next usable target, filtering out unsuitable targets. */
- target = iter_server_selection(ie, qstate->env, iq->dp,
+ target = iter_server_selection(ie, qstate->env, iq->dp,
iq->dp->name, iq->dp->namelen, iq->qchase.qtype,
- &iq->dnssec_lame_query, &iq->chase_to_rd,
+ &iq->dnssec_lame_query, &iq->chase_to_rd,
iq->num_target_queries, qstate->blacklist,
qstate->prefetch_leeway);
@@ -2544,7 +2663,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
/* If there is nothing to wait for, then we need
* to distinguish between generating (a) new target
* query, or failing. */
- if(delegpt_count_missing_targets(iq->dp) > 0) {
+ if(delegpt_count_missing_targets(iq->dp, NULL) > 0) {
int qs = 0;
verbose(VERB_ALGO, "querying for next "
"missing target");
@@ -2556,7 +2675,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
LDNS_RCODE_SERVFAIL);
}
if(qs == 0 &&
- delegpt_count_missing_targets(iq->dp) == 0){
+ delegpt_count_missing_targets(iq->dp, NULL) == 0){
/* it looked like there were missing
* targets, but they did not turn up.
* Try the bad choices again (if any),
@@ -2595,23 +2714,8 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
/* otherwise, we have no current targets, so submerge
* until one of the target or direct queries return. */
- if(iq->num_target_queries>0 && iq->num_current_queries>0) {
- verbose(VERB_ALGO, "no current targets -- waiting "
- "for %d targets to resolve or %d outstanding"
- " queries to respond", iq->num_target_queries,
- iq->num_current_queries);
- qstate->ext_state[id] = module_wait_reply;
- } else if(iq->num_target_queries>0) {
- verbose(VERB_ALGO, "no current targets -- waiting "
- "for %d targets to resolve.",
- iq->num_target_queries);
- qstate->ext_state[id] = module_wait_subquery;
- } else {
- verbose(VERB_ALGO, "no current targets -- waiting "
- "for %d outstanding queries to respond.",
- iq->num_current_queries);
- qstate->ext_state[id] = module_wait_reply;
- }
+ verbose(VERB_ALGO, "no current targets");
+ check_waiting_queries(iq, qstate, id);
/* undo qname minimise step because we'll get back here
* to do it again */
if(qout_orig && iq->minimise_count > 0) {
@@ -3383,8 +3487,11 @@ processTargetResponse(struct module_qstate* qstate, int id,
delegpt_mark_neg(dpns, qstate->qinfo.qtype);
dpns->resolved = 1; /* fail the target */
if((dpns->got4 == 2 || !ie->supports_ipv4) &&
- (dpns->got6 == 2 || !ie->supports_ipv6))
+ (dpns->got6 == 2 || !ie->supports_ipv6) &&
+ /* do not count cached answers */
+ (qstate->reply_origin && qstate->reply_origin->len != 0)) {
target_count_increase_nx(foriq, 1);
+ }
}
}
@@ -4002,8 +4109,11 @@ iter_clear(struct module_qstate* qstate, int id)
iq = (struct iter_qstate*)qstate->minfo[id];
if(iq) {
outbound_list_clear(&iq->outlist);
- if(iq->target_count && --iq->target_count[0] == 0)
+ if(iq->target_count && --iq->target_count[TARGET_COUNT_REF] == 0) {
free(iq->target_count);
+ if(*iq->nxns_dp) free(*iq->nxns_dp);
+ free(iq->nxns_dp);
+ }
iq->num_current_queries = 0;
}
qstate->minfo[id] = NULL;
diff --git a/iterator/iterator.h b/iterator/iterator.h
index 8b840528d9d9..62f4768ea01d 100644
--- a/iterator/iterator.h
+++ b/iterator/iterator.h
@@ -60,6 +60,9 @@ struct rbtree_type;
/** max number of nxdomains allowed for target lookups for a query and
* its subqueries */
#define MAX_TARGET_NX 5
+/** max number of nxdomains allowed for target lookups for a query and
+ * its subqueries when fallback has kicked in */
+#define MAX_TARGET_NX_FALLBACK (MAX_TARGET_NX*2)
/** max number of query restarts. Determines max number of CNAME chain. */
#define MAX_RESTART_COUNT 11
/** max number of referrals. Makes sure resolver does not run away */
@@ -218,6 +221,21 @@ enum iter_state {
};
/**
+ * Shared counters for queries.
+ */
+enum target_count_variables {
+ /** Reference count for the shared iter_qstate->target_count. */
+ TARGET_COUNT_REF = 0,
+ /** Number of target queries spawned for the query and subqueries. */
+ TARGET_COUNT_QUERIES,
+ /** Number of nxdomain responses encountered. */
+ TARGET_COUNT_NX,
+
+ /** This should stay last here, it is used for the allocation */
+ TARGET_COUNT_MAX,
+};
+
+/**
* Per query state for the iterator module.
*/
struct iter_qstate {
@@ -310,15 +328,20 @@ struct iter_qstate {
/** number of queries fired off */
int sent_count;
- /** number of target queries spawned in [1], for this query and its
- * subqueries, the malloced-array is shared, [0] refcount.
- * in [2] the number of nxdomains is counted. */
+ /** malloced-array shared with this query and its subqueries. It keeps
+ * track of the defined enum target_count_variables counters. */
int* target_count;
/** number of target lookups per delegation point. Reset to 0 after
* receiving referral answer. Not shared with subqueries. */
int dp_target_count;
+ /** Delegation point that triggered the NXNS fallback; shared with
+ * this query and its subqueries, count-referenced by the reference
+ * counter in target_count.
+ * This also marks the fallback activation. */
+ uint8_t** nxns_dp;
+
/** if true, already tested for ratelimiting and passed the test */
int ratelimit_ok;
diff --git a/libunbound/unbound.h b/libunbound/unbound.h
index ee8558759065..c779d183e385 100644
--- a/libunbound/unbound.h
+++ b/libunbound/unbound.h
@@ -725,6 +725,8 @@ struct ub_server_stats {
long long qtcp;
/** number of outgoing queries over TCP */
long long qtcp_outgoing;
+ /** number of outgoing queries over UDP */
+ long long qudp_outgoing;
/** number of queries over (DNS over) TLS */
long long qtls;
/** number of queries over (DNS over) HTTPS */
diff --git a/services/authzone.c b/services/authzone.c
index 02fb621a22ff..5f2b7154a946 100644
--- a/services/authzone.c
+++ b/services/authzone.c
@@ -3967,7 +3967,7 @@ probe_copy_masters_for_allow_notify(struct auth_xfer* xfr)
struct auth_master* list = NULL, *last = NULL;
struct auth_master* p;
/* build up new list with copies */
- for(p = xfr->task_probe->masters; p; p=p->next) {
+ for(p = xfr->task_transfer->masters; p; p=p->next) {
struct auth_master* m = auth_master_copy(p);
if(!m) {
auth_free_masters(list);
@@ -5512,6 +5512,8 @@ xfr_transfer_init_fetch(struct auth_xfer* xfr, struct module_env* env)
addr_to_str(&addr, addrlen, as, sizeof(as));
verbose(VERB_ALGO, "auth zone %s transfer next HTTP fetch from %s started", zname, as);
}
+ /* Create or refresh the list of allow_notify addrs */
+ probe_copy_masters_for_allow_notify(xfr);
return 1;
}
diff --git a/services/mesh.c b/services/mesh.c
index fbaa966bdd05..c40eb50dc55c 100644
--- a/services/mesh.c
+++ b/services/mesh.c
@@ -811,7 +811,8 @@ static void mesh_schedule_prefetch_subnet(struct mesh_area* mesh,
log_err("prefetch_subnet subnet_option_from_ss: invalid data");
return;
}
- subnet_ecs_opt_list_append(&ecs, &s->s.edns_opts_front_in, &s->s);
+ subnet_ecs_opt_list_append(&ecs, &s->s.edns_opts_front_in,
+ &s->s, s->s.region);
if(!s->s.edns_opts_front_in) {
log_err("prefetch_subnet subnet_ecs_opt_list_append: out of memory");
return;
diff --git a/services/outside_network.c b/services/outside_network.c
index ec37a4a80d71..3f479a3a36fe 100644
--- a/services/outside_network.c
+++ b/services/outside_network.c
@@ -271,7 +271,7 @@ outnet_get_tcp_fd(struct sockaddr_storage* addr, socklen_t addrlen, int tcp_mss,
int s;
int af;
char* err;
-#ifdef SO_REUSEADDR
+#if defined(SO_REUSEADDR) || defined(IP_BIND_ADDRESS_NO_PORT)
int on = 1;
#endif
#ifdef INET6
@@ -317,7 +317,13 @@ outnet_get_tcp_fd(struct sockaddr_storage* addr, socklen_t addrlen, int tcp_mss,
" setsockopt(TCP_MAXSEG) unsupported");
#endif /* defined(IPPROTO_TCP) && defined(TCP_MAXSEG) */
}
-
+#ifdef IP_BIND_ADDRESS_NO_PORT
+ if(setsockopt(s, IPPROTO_IP, IP_BIND_ADDRESS_NO_PORT, (void*)&on,
+ (socklen_t)sizeof(on)) < 0) {
+ verbose(VERB_ALGO, "outgoing tcp:"
+ " setsockopt(.. IP_BIND_ADDRESS_NO_PORT ..) failed");
+ }
+#endif /* IP_BIND_ADDRESS_NO_PORT */
return s;
}
@@ -1608,6 +1614,7 @@ outside_network_create(struct comm_base *base, size_t bufsize,
outnet->tcp_reuse_timeout= tcp_reuse_timeout;
outnet->tcp_auth_query_timeout = tcp_auth_query_timeout;
outnet->num_tcp_outgoing = 0;
+ outnet->num_udp_outgoing = 0;
outnet->infra = infra;
outnet->rnd = rnd;
outnet->sslctx = sslctx;
@@ -2142,6 +2149,7 @@ randomize_and_send_udp(struct pending* pend, sldns_buffer* packet, int timeout)
portcomm_loweruse(outnet, pend->pc);
return 0;
}
+ outnet->num_udp_outgoing++;
/* system calls to set timeout after sending UDP to make roundtrip
smaller. */
diff --git a/services/outside_network.h b/services/outside_network.h
index 4c5b96f83424..c383b8f09e24 100644
--- a/services/outside_network.h
+++ b/services/outside_network.h
@@ -113,6 +113,8 @@ struct outside_network {
/** if we perform udp-connect, connect() for UDP socket to mitigate
* ICMP side channel leakage */
int udp_connect;
+ /** number of udp packets sent. */
+ size_t num_udp_outgoing;
/** array of outgoing IP4 interfaces */
struct port_if* ip4_ifs;
diff --git a/sldns/parse.c b/sldns/parse.c
index 491c8f51bf10..8ea084661db6 100644
--- a/sldns/parse.c
+++ b/sldns/parse.c
@@ -34,7 +34,7 @@ sldns_fget_token_l(FILE *f, char *token, const char *delim, size_t limit, int *l
{
int c, prev_c;
int p; /* 0 -> no parentheses seen, >0 nr of ( seen */
- int com, quoted;
+ int com, quoted, only_blank;
char *t;
size_t i;
const char *d;
@@ -53,6 +53,7 @@ sldns_fget_token_l(FILE *f, char *token, const char *delim, size_t limit, int *l
com = 0;
quoted = 0;
prev_c = 0;
+ only_blank = 1; /* Assume we got only <blank> until now */
t = token;
if (del[0] == '"') {
quoted = 1;
@@ -101,6 +102,22 @@ sldns_fget_token_l(FILE *f, char *token, const char *delim, size_t limit, int *l
if (line_nr) {
*line_nr = *line_nr + 1;
}
+ if (only_blank && i > 0) {
+ /* Got only <blank> so far. Reset and try
+ * again with the next line.
+ */
+ i = 0;
+ t = token;
+ }
+ if (p == 0) {
+ /* If p != 0 then the next line is a continuation. So
+ * we assume that the next line starts with a blank only
+ * if it is actually a new line.
+ */
+ only_blank = 1; /* Assume next line starts with
+ * <blank>.
+ */
+ }
if (p == 0 && i > 0) {
goto tokenread;
} else {
@@ -131,12 +148,29 @@ sldns_fget_token_l(FILE *f, char *token, const char *delim, size_t limit, int *l
/* check if we hit the delim */
for (d = del; *d; d++) {
- if (c == *d && i > 0 && prev_c != '\\' && p == 0) {
- if (c == '\n' && line_nr) {
- *line_nr = *line_nr + 1;
- }
- goto tokenread;
+ if (c == *d)
+ break;
+ }
+
+ if (c == *d && i > 0 && prev_c != '\\' && p == 0) {
+ if (c == '\n' && line_nr) {
+ *line_nr = *line_nr + 1;
}
+ if (only_blank) {
+ /* Got only <blank> so far. Reset and
+ * try again with the next line.
+ */
+ i = 0;
+ t = token;
+ only_blank = 1;
+ prev_c = c;
+ continue;
+ }
+ goto tokenread;
+ }
+ if (c != ' ' && c != '\t') {
+ /* Found something that is not <blank> */
+ only_blank= 0;
}
if (c != '\0' && c != '\n') {
i++;
@@ -149,8 +183,13 @@ sldns_fget_token_l(FILE *f, char *token, const char *delim, size_t limit, int *l
if (c != '\0' && c != '\n') {
*t++ = c;
}
- if (c == '\n' && line_nr) {
- *line_nr = *line_nr + 1;
+ if (c == '\n') {
+ if (line_nr) {
+ *line_nr = *line_nr + 1;
+ }
+ only_blank = 1; /* Assume next line starts with
+ * <blank>.
+ */
}
if (c == '\\' && prev_c == '\\')
prev_c = 0;
diff --git a/smallapp/unbound-control.c b/smallapp/unbound-control.c
index 89543e45de7c..d473702c441e 100644
--- a/smallapp/unbound-control.c
+++ b/smallapp/unbound-control.c
@@ -354,6 +354,7 @@ static void print_extended(struct ub_stats_info* s)
/* transport */
PR_UL("num.query.tcp", s->svr.qtcp);
PR_UL("num.query.tcpout", s->svr.qtcp_outgoing);
+ PR_UL("num.query.udpout", s->svr.qudp_outgoing);
PR_UL("num.query.tls", s->svr.qtls);
PR_UL("num.query.tls_resume", s->svr.qtls_resume);
PR_UL("num.query.ipv6", s->svr.qipv6);
@@ -486,6 +487,7 @@ static void print_stats_shm(const char* cfgfile, int quiet)
config_delete(cfg);
#else
(void)cfgfile;
+ (void)quiet;
#endif /* HAVE_SHMGET */
}
diff --git a/testcode/dohclient.c b/testcode/dohclient.c
index d35f5a82c662..82e522f90d51 100644
--- a/testcode/dohclient.c
+++ b/testcode/dohclient.c
@@ -551,7 +551,7 @@ run(struct http2_session* h2_session, int port, int no_tls, int count, char** q)
if(ctx) {
SSL_CTX_free(ctx);
}
- close(fd);
+ sock_close(fd);
}
/** getopt global, in case header files fail to declare it. */
diff --git a/testcode/mini_tdir.sh b/testcode/mini_tdir.sh
index 0457a95e7094..6bbece8d9368 100755
--- a/testcode/mini_tdir.sh
+++ b/testcode/mini_tdir.sh
@@ -5,6 +5,10 @@ if test "$1" = "-a"; then
shift
shift
fi
+
+# This will keep the temporary directory around and return 1 when the test failed.
+DEBUG=0
+
quiet=0
if test "$1" = "-q"; then
quiet=1
@@ -184,11 +188,18 @@ echo "DateRunEnd: "`date "+%s" 2>/dev/null` >> $result
mv $result ..
cd ..
-rm -rf $dir
-# compat for windows where deletion may not succeed initially (files locked
-# by processes that still have to exit).
-if test $? -eq 1; then
- echo "minitdir waiting for processes to terminate"
- sleep 2 # some time to exit, and try again
+if test $DEBUG -eq 0; then
rm -rf $dir
+ # compat for windows where deletion may not succeed initially (files locked
+ # by processes that still have to exit).
+ if test $? -eq 1; then
+ echo "minitdir waiting for processes to terminate"
+ sleep 2 # some time to exit, and try again
+ rm -rf $dir
+ fi
+else
+ if test $success == "no"; then
+ exit 1
+ fi
+ exit 0
fi
diff --git a/testcode/unitmain.c b/testcode/unitmain.c
index 16aa88450210..b6dac5507faf 100644
--- a/testcode/unitmain.c
+++ b/testcode/unitmain.c
@@ -918,7 +918,7 @@ main(int argc, char* argv[])
checklock_stop();
printf("%d checks ok.\n", testcount);
#ifdef HAVE_SSL
-# if defined(USE_GOST) && defined(HAVE_LDNS_KEY_EVP_UNLOAD_GOST)
+# if defined(USE_GOST)
sldns_key_EVP_unload_gost();
# endif
# ifdef HAVE_OPENSSL_CONFIG
diff --git a/testdata/auth_https.tdir/auth_https.test b/testdata/auth_https.tdir/auth_https.test
index cff93544b14a..c2471b7d6439 100644
--- a/testdata/auth_https.tdir/auth_https.test
+++ b/testdata/auth_https.tdir/auth_https.test
@@ -5,6 +5,7 @@
[ -f .tpkg.var.test ] && source .tpkg.var.test
PRE="../.."
+. ../common.sh
# do the test
echo "> dig www.example.com."
dig @localhost -p $UNBOUND_PORT www.example.com. | tee outfile
@@ -48,4 +49,15 @@ else
exit 1
fi
+# Test that notify is allowed from the webserver address
+get_ldns_notify
+echo "> ldns-notify -z example.com"
+$LDNS_NOTIFY -p $UNBOUND_PORT -z example.com 127.0.0.1 | tee outfile
+if grep "rcode: REFUSED" outfile; then
+ echo "Not OK"
+ exit 1
+else
+ echo "OK"
+fi
+
exit 0
diff --git a/testdata/blanks_cached_zone.tdir/blanks.example.com.zone b/testdata/blanks_cached_zone.tdir/blanks.example.com.zone
new file mode 100644
index 000000000000..f5eba1fbb9ea
--- /dev/null
+++ b/testdata/blanks_cached_zone.tdir/blanks.example.com.zone
@@ -0,0 +1,23 @@
+; Test if the zone parser accepts various blank lines
+@ IN SOA ns1.example.com dnsmaster.example.com. (
+ 1 ; Serial
+ 7200 ; Refresh 2 hours
+ 3600 ; Retry 1 hour
+ 2419200 ; expire - 4 weeks
+ 3600 ; Minimum 1 hour
+)
+ 7200 IN NS ns1
+ns1 IN A 192.0.2.1
+ IN AAAA 2001:dbb::1
+; completely blank line
+
+; line with one space
+
+; line with one tab
+
+; line with spaces followed by comment
+ ; test comment
+; line with tabs followed by comment
+ ; test comment
+; Final line with spaces, tabs and comment
+ ; test comment
diff --git a/testdata/blanks_cached_zone.tdir/blanks_cached_zone.conf b/testdata/blanks_cached_zone.tdir/blanks_cached_zone.conf
new file mode 100644
index 000000000000..b0eb4aa9ad06
--- /dev/null
+++ b/testdata/blanks_cached_zone.tdir/blanks_cached_zone.conf
@@ -0,0 +1,20 @@
+server:
+ verbosity: 7
+ # num-threads: 1
+ interface: 127.0.0.1
+ port: @PORT@
+ use-syslog: no
+ directory: ""
+ pidfile: "unbound.pid"
+ chroot: ""
+ username: ""
+ do-not-query-localhost: no
+ use-caps-for-id: yes
+ module-config: "respip validator iterator"
+
+auth-zone:
+ name: blanks.example.com
+ zonefile: "blanks.example.com.zone"
+ url: "https://127.0.0.1:@TOPORT@/blanks.example.com.zone"
+ for-upstream: yes
+ for-downstream: yes
diff --git a/testdata/blanks_cached_zone.tdir/blanks_cached_zone.dsc b/testdata/blanks_cached_zone.tdir/blanks_cached_zone.dsc
new file mode 100644
index 000000000000..1686aa4f3855
--- /dev/null
+++ b/testdata/blanks_cached_zone.tdir/blanks_cached_zone.dsc
@@ -0,0 +1,16 @@
+BaseName: blanks_cached_zone
+Version: 1.0
+Description: Check if a cached zone with blank line can be loaded
+CreationDate: Wed 08 Jun 2022 11:16:25 AM CEST
+Maintainer: Philip Homburg
+Category:
+Component:
+CmdDepends:
+Depends:
+Help:
+Pre: blanks_cached_zone.pre
+Post: blanks_cached_zone.post
+Test: blanks_cached_zone.test
+AuxFiles:
+Passed:
+Failure:
diff --git a/testdata/blanks_cached_zone.tdir/blanks_cached_zone.post b/testdata/blanks_cached_zone.tdir/blanks_cached_zone.post
new file mode 100644
index 000000000000..c79a887471d8
--- /dev/null
+++ b/testdata/blanks_cached_zone.tdir/blanks_cached_zone.post
@@ -0,0 +1,10 @@
+# #-- blanks_cached_zone.post --#
+# source the master var file when it's there
+[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
+# source the test var file when it's there
+[ -f .tpkg.var.test ] && source .tpkg.var.test
+#
+# do your teardown here
+PRE="../.."
+. ../common.sh
+kill_pid $UNBOUND_PID
diff --git a/testdata/blanks_cached_zone.tdir/blanks_cached_zone.pre b/testdata/blanks_cached_zone.tdir/blanks_cached_zone.pre
new file mode 100644
index 000000000000..f347a66cccbd
--- /dev/null
+++ b/testdata/blanks_cached_zone.tdir/blanks_cached_zone.pre
@@ -0,0 +1,23 @@
+# #-- blanks_cached_zone.pre--#
+# source the master var file when it's there
+[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
+# use .tpkg.var.test for in test variable passing
+[ -f .tpkg.var.test ] && source .tpkg.var.test
+
+PRE="../.."
+. ../common.sh
+get_random_port 2
+UNBOUND_PORT=$RND_PORT
+UNUSED_PORT=$(($RND_PORT + 1))
+echo "UNBOUND_PORT=$UNBOUND_PORT" >> .tpkg.var.test
+
+# make config file
+sed -e 's/@PORT\@/'$UNBOUND_PORT'/' -e 's/@TOPORT\@/'$UNUSED_PORT'/' < blanks_cached_zone.conf > ub.conf
+# start unbound in the background
+$PRE/unbound -d -c ub.conf >unbound.log 2>&1 &
+UNBOUND_PID=$!
+echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test
+
+cat .tpkg.var.test
+wait_unbound_up unbound.log
+
diff --git a/testdata/blanks_cached_zone.tdir/blanks_cached_zone.test b/testdata/blanks_cached_zone.tdir/blanks_cached_zone.test
new file mode 100644
index 000000000000..c405950c3794
--- /dev/null
+++ b/testdata/blanks_cached_zone.tdir/blanks_cached_zone.test
@@ -0,0 +1,51 @@
+# #-- blanks_cached_zone.test --#
+# source the master var file when it's there
+[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
+# use .tpkg.var.test for in test variable passing
+[ -f .tpkg.var.test ] && source .tpkg.var.test
+
+PRE="../.."
+# do the test
+TARGET=ns1.blanks.example.com.
+echo "> dig $TARGET"
+dig @localhost -p $UNBOUND_PORT $TARGET | tee outfile
+if grep SERVFAIL outfile; then
+ echo "> try again"
+ dig @localhost -p $UNBOUND_PORT $TARGET | tee outfile
+fi
+if grep SERVFAIL outfile; then
+ echo "> try again"
+ sleep 1
+ dig @localhost -p $UNBOUND_PORT $TARGET | tee outfile
+fi
+if grep SERVFAIL outfile; then
+ echo "> try again"
+ sleep 1
+ dig @localhost -p $UNBOUND_PORT $TARGET | tee outfile
+fi
+if grep SERVFAIL outfile; then
+ echo "> try again"
+ sleep 1
+ dig @localhost -p $UNBOUND_PORT $TARGET | tee outfile
+fi
+if grep SERVFAIL outfile; then
+ echo "> try again"
+ sleep 10
+ dig @localhost -p $UNBOUND_PORT $TARGET | tee outfile
+fi
+if grep SERVFAIL outfile; then
+ echo "> try again"
+ sleep 10
+ dig @localhost -p $UNBOUND_PORT $TARGET | tee outfile
+fi
+echo "> cat logfiles"
+cat unbound.log
+echo "> check answer"
+if grep "192.0.2.1" outfile; then
+ echo "OK"
+else
+ echo "Not OK"
+ exit 1
+fi
+
+exit 0
diff --git a/testdata/blanks_https.tdir/127.0.0.1/blanks.example.com.zone b/testdata/blanks_https.tdir/127.0.0.1/blanks.example.com.zone
new file mode 100644
index 000000000000..f5eba1fbb9ea
--- /dev/null
+++ b/testdata/blanks_https.tdir/127.0.0.1/blanks.example.com.zone
@@ -0,0 +1,23 @@
+; Test if the zone parser accepts various blank lines
+@ IN SOA ns1.example.com dnsmaster.example.com. (
+ 1 ; Serial
+ 7200 ; Refresh 2 hours
+ 3600 ; Retry 1 hour
+ 2419200 ; expire - 4 weeks
+ 3600 ; Minimum 1 hour
+)
+ 7200 IN NS ns1
+ns1 IN A 192.0.2.1
+ IN AAAA 2001:dbb::1
+; completely blank line
+
+; line with one space
+
+; line with one tab
+
+; line with spaces followed by comment
+ ; test comment
+; line with tabs followed by comment
+ ; test comment
+; Final line with spaces, tabs and comment
+ ; test comment
diff --git a/testdata/blanks_https.tdir/blanks_https.conf b/testdata/blanks_https.tdir/blanks_https.conf
new file mode 100644
index 000000000000..836353356de6
--- /dev/null
+++ b/testdata/blanks_https.tdir/blanks_https.conf
@@ -0,0 +1,18 @@
+server:
+ verbosity: 7
+ # num-threads: 1
+ interface: 127.0.0.1
+ port: @PORT@
+ use-syslog: no
+ directory: ""
+ pidfile: "unbound.pid"
+ chroot: ""
+ username: ""
+ do-not-query-localhost: no
+ use-caps-for-id: yes
+auth-zone:
+ name: "blanks.example.com"
+ for-upstream: yes
+ for-downstream: yes
+ url: "https://127.0.0.1:@TOPORT@/blanks.example.com.zone"
+
diff --git a/testdata/blanks_https.tdir/blanks_https.dsc b/testdata/blanks_https.tdir/blanks_https.dsc
new file mode 100644
index 000000000000..eb3d4d6d8fe0
--- /dev/null
+++ b/testdata/blanks_https.tdir/blanks_https.dsc
@@ -0,0 +1,16 @@
+BaseName: blanks_https
+Version: 1.0
+Description: Fetch a zone with blank lines over https
+CreationDate: Tue 14 Jun 2022 04:43:21 PM CEST
+Maintainer: Philip Homburg
+Category:
+Component:
+CmdDepends:
+Depends:
+Help:
+Pre: blanks_https.pre
+Post: blanks_https.post
+Test: blanks_https.test
+AuxFiles:
+Passed:
+Failure:
diff --git a/testdata/blanks_https.tdir/blanks_https.post b/testdata/blanks_https.tdir/blanks_https.post
new file mode 100644
index 000000000000..631150c024ce
--- /dev/null
+++ b/testdata/blanks_https.tdir/blanks_https.post
@@ -0,0 +1,11 @@
+# #-- blanks_https.post --#
+# source the master var file when it's there
+[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
+# source the test var file when it's there
+[ -f .tpkg.var.test ] && source .tpkg.var.test
+#
+# do your teardown here
+PRE="../.."
+. ../common.sh
+kill_pid $UNBOUND_PID
+kill_pid $PETAL_PID
diff --git a/testdata/blanks_https.tdir/blanks_https.pre b/testdata/blanks_https.tdir/blanks_https.pre
new file mode 100644
index 000000000000..956ac05202f0
--- /dev/null
+++ b/testdata/blanks_https.tdir/blanks_https.pre
@@ -0,0 +1,34 @@
+# #-- blanks_https.pre--#
+# source the master var file when it's there
+[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
+# use .tpkg.var.test for in test variable passing
+[ -f .tpkg.var.test ] && source .tpkg.var.test
+
+PRE="../.."
+. ../common.sh
+get_random_port 2
+UNBOUND_PORT=$RND_PORT
+PETAL_PORT=$(($RND_PORT + 1))
+echo "UNBOUND_PORT=$UNBOUND_PORT" >> .tpkg.var.test
+echo "PETAL_PORT=$PETAL_PORT" >> .tpkg.var.test
+
+get_make
+(cd $PRE; $MAKE petal)
+
+# start https daemon
+$PRE/petal -v -a "127.0.0.1" -p $PETAL_PORT >petal.log 2>&1 &
+PETAL_PID=$!
+echo "PETAL_PID=$PETAL_PID" >> .tpkg.var.test
+cat .tpkg.var.test
+wait_petal_up petal.log
+
+# make config file
+sed -e 's/@PORT\@/'$UNBOUND_PORT'/' -e 's/@TOPORT\@/'$PETAL_PORT'/' < blanks_https.conf > ub.conf
+# start unbound in the background
+$PRE/unbound -d -c ub.conf >unbound.log 2>&1 &
+UNBOUND_PID=$!
+echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test
+
+cat .tpkg.var.test
+wait_unbound_up unbound.log
+
diff --git a/testdata/blanks_https.tdir/blanks_https.test b/testdata/blanks_https.tdir/blanks_https.test
new file mode 100644
index 000000000000..2c4c5dc7a473
--- /dev/null
+++ b/testdata/blanks_https.tdir/blanks_https.test
@@ -0,0 +1,52 @@
+# #-- blanks_https.test --#
+# source the master var file when it's there
+[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
+# use .tpkg.var.test for in test variable passing
+[ -f .tpkg.var.test ] && source .tpkg.var.test
+
+PRE="../.."
+# do the test
+TARGET=ns1.blanks.example.com.
+echo "> dig $TARGET"
+dig @localhost -p $UNBOUND_PORT $TARGET | tee outfile
+if grep SERVFAIL outfile; then
+ echo "> try again"
+ dig @localhost -p $UNBOUND_PORT $TARGET | tee outfile
+fi
+if grep SERVFAIL outfile; then
+ echo "> try again"
+ sleep 1
+ dig @localhost -p $UNBOUND_PORT $TARGET | tee outfile
+fi
+if grep SERVFAIL outfile; then
+ echo "> try again"
+ sleep 1
+ dig @localhost -p $UNBOUND_PORT $TARGET | tee outfile
+fi
+if grep SERVFAIL outfile; then
+ echo "> try again"
+ sleep 1
+ dig @localhost -p $UNBOUND_PORT $TARGET | tee outfile
+fi
+if grep SERVFAIL outfile; then
+ echo "> try again"
+ sleep 10
+ dig @localhost -p $UNBOUND_PORT $TARGET | tee outfile
+fi
+if grep SERVFAIL outfile; then
+ echo "> try again"
+ sleep 10
+ dig @localhost -p $UNBOUND_PORT $TARGET | tee outfile
+fi
+echo "> cat logfiles"
+cat petal.log
+cat unbound.log
+echo "> check answer"
+if grep "192.0.2.1" outfile; then
+ echo "OK"
+else
+ echo "Not OK"
+ exit 1
+fi
+
+exit 0
diff --git a/testdata/blanks_https.tdir/petal.key b/testdata/blanks_https.tdir/petal.key
new file mode 100644
index 000000000000..6614e498fcd2
--- /dev/null
+++ b/testdata/blanks_https.tdir/petal.key
@@ -0,0 +1,21 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIDfQIBAAKBwQC1xQ/Kca6zszZbcCtdOTIH2Uy2gOy/DfabMUU7TmNPm0dVE0NJ
+RuN+Rm304SonpwghfP2/ULZNnuDgpG03/32yI7k/VzG6iA4hiF7tT/KAAWC/+2l1
+QCsawCV2bSrFK0VhcZr7ALqXd8vkDaQ867K029ypjOQtAJ85qdO3mERy7TGtdUcu
+O6hLeVet419YeQ2F8cfNxn63d7bOzNGLPW5xwaCd3UcgD+Ib0k4xfFvbinvPQUeU
+J/i4YDWexFYSL+ECAwEAAQKBwCLXXQl+9O+5AEhSnd1Go1Jh0pSA7eBJOuXQcebG
+Rb7ykp+6C4G2NtDziwwPRNdI6wQQQ0sym18RfyVQHydGr78/nbiIbB3HCn5e92Mh
+mefzW6ow9Kvm2txLzGKA1lvoyRbNm81jnG/eygi3u7Nqd5PNv+4dHj2RkTlmxOeh
+qnDMVP5md8uZPv6lYNnrnIzvLCR5vnPNdVwn89AqzI85IcDZdy0R9ZX4NBbsDgAU
+6ig6uXuRXvSGiyJ/OUXSrnogaQJhAOjvkHUhVZQkPOxO90TNH4j0GdKKtbSWxIdz
+lKfuJeBAEqs0TL+C6vbS81Xw3W1alyDdUBk3rJMOBqW6Ryq5HNL+j5H+Jfsh7fvc
+Yle+5wHGci0P9zCFZCrY8It7n9XFIwJhAMfEi6oJa2G8waPJ1bQhxka82Tf9pnKM
+XCn/1BBOFjVIx5F842cpA+zp5a62GENTGYPQTTRBB/2/ZwnW5aIkrlg54AtmbqBZ
+Oh+2kJdJQD/tfoVmc5soUE2ScTHadK5RKwJhAN4w9kjkXS+MSZjX0kIMsBIBVkhh
+C+aREjJqa9ir7/Ey7RvmLXdYuCxtGLRXp7/R8+rjcK49Tx6O+IRJZe042mfhbq3C
+EhS1Tr86f4xXix9EXlDhs9bSxrOgcAN9Dv/opQJhAK7eBcPaav0rVfYh/8emqQHS
+3fJ9Pu6WnzbEksWTFS2ff9KDGCx9YspIFJ5TF/oXDAaumGZdZrlgirm6O1kr8tGY
+F97i04PZl1+bWAaWQH+1TUNI43m2WFUPE7coG2tb8QJgcddDg9VlXliZqgcETZfJ
+kJmYETxrcSn3ao6v116N8yxhEgUgjkmsCTiFgx36iDVnXwK6PIt+sIu8MC7eYNa3
+berrv/M21K0LRn20IWRxvUobG070weHCAgkko7fTWgr2
+-----END RSA PRIVATE KEY-----
diff --git a/testdata/blanks_https.tdir/petal.pem b/testdata/blanks_https.tdir/petal.pem
new file mode 100644
index 000000000000..19c8b895ba86
--- /dev/null
+++ b/testdata/blanks_https.tdir/petal.pem
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICFzCCAUACCQDO660L5y5LGDANBgkqhkiG9w0BAQUFADAQMQ4wDAYDVQQDEwVw
+ZXRhbDAeFw0xMDA5MzAxMzQzMDFaFw0zMDA2MTcxMzQzMDFaMBAxDjAMBgNVBAMT
+BXBldGFsMIHfMA0GCSqGSIb3DQEBAQUAA4HNADCByQKBwQC1xQ/Kca6zszZbcCtd
+OTIH2Uy2gOy/DfabMUU7TmNPm0dVE0NJRuN+Rm304SonpwghfP2/ULZNnuDgpG03
+/32yI7k/VzG6iA4hiF7tT/KAAWC/+2l1QCsawCV2bSrFK0VhcZr7ALqXd8vkDaQ8
+67K029ypjOQtAJ85qdO3mERy7TGtdUcuO6hLeVet419YeQ2F8cfNxn63d7bOzNGL
+PW5xwaCd3UcgD+Ib0k4xfFvbinvPQUeUJ/i4YDWexFYSL+ECAwEAATANBgkqhkiG
+9w0BAQUFAAOBwQBBkX9KDP2RXbg+xPmdJ4P6CwvA5x1LZwC++ydVx4NlvT0pWicD
+ZUnXjcWAJlkeOuUBAqFG7WHTrXpUUAjmdqFVq2yFjteUYBdrFz0RDB2jM9feeKYO
+mTgxdZyT9a6humxCxt5VfgT02axLjm/2AqCyFPMbf4PASoJDln01AEuZLZ8Xl2gV
+bYHMnHTGoD1Hu6FNEzRgkMC6XT8X3YjHvzQhpc/qL5wEfEsinQGdX4twsuWbf8xd
+q7miNnkO8vd0maw=
+-----END CERTIFICATE-----
diff --git a/testdata/common.sh b/testdata/common.sh
index f6d72c2f046a..280f5dac4cec 100644
--- a/testdata/common.sh
+++ b/testdata/common.sh
@@ -14,6 +14,7 @@
# info x : print info
# test_tool_avail x : see if program in path and complain, exit if not.
# get_ldns_testns : set LDNS_TESTNS to executable ldns-testns
+# get_ldns_notify : set LDNS_NOTIFY to executable ldns-notify
# get_make : set MAKE to gmake or make tool.
# get_gcc : set cc or gcc in CC
# get_pcat : set PCAT, PCAT_DIFF and PCAT_PRINT executables.
@@ -62,6 +63,15 @@ get_ldns_testns () {
fi
}
+# get ldns-notify tool in LDNS_NOTIFY variable.
+get_ldns_notify () {
+ if test -x "`which ldns-notify 2>&1`"; then
+ LDNS_NOTIFY=ldns-notify
+ else
+ LDNS_NOTIFY=/home/wouter/bin/ldns-notify
+ fi
+}
+
# get make tool in MAKE variable, gmake is used if present.
get_make () {
if test -x "`which gmake 2>&1`"; then
diff --git a/testdata/iter_nxns_cached.rpl b/testdata/iter_nxns_cached.rpl
new file mode 100644
index 000000000000..7671df6636cc
--- /dev/null
+++ b/testdata/iter_nxns_cached.rpl
@@ -0,0 +1,384 @@
+; Check that cached NXDOMAIN replies for nameservers do not count towards the
+; MAX_TARGET_NX limit.
+
+server:
+ module-config: "iterator"
+ trust-anchor-signaling: no
+ target-fetch-policy: "0 0 0 0 0"
+ verbosity: 3
+ access-control: 127.0.0.1 allow_snoop
+ do-not-query-localhost: no
+ qname-minimisation: no
+ minimal-responses: no
+ rrset-roundrobin: no
+stub-zone:
+ name: "example.com"
+ stub-addr: 127.0.0.2
+stub-zone:
+ name: "nameservers.com"
+ stub-addr: 127.0.0.3
+CONFIG_END
+
+SCENARIO_BEGIN Test that the NXNS countermeasure is not triggered for cached NXDOMAIN
+
+RANGE_BEGIN 0 100
+ ADDRESS 127.0.0.1
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ b.a.example.com. IN A
+ SECTION ANSWER
+ b.a.example.com. IN A 127.0.0.0
+ ENTRY_END
+RANGE_END
+
+RANGE_BEGIN 31 100
+ ADDRESS 127.0.0.3
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ ns1.nameservers.com. IN A
+ SECTION ANSWER
+ ns1.nameservers.com. IN A 127.0.0.1
+ ENTRY_END
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ ns2.nameservers.com. IN A
+ SECTION ANSWER
+ ns2.nameservers.com. IN A 127.0.0.1
+ ENTRY_END
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ ns3.nameservers.com. IN A
+ SECTION ANSWER
+ ns3.nameservers.com. IN A 127.0.0.1
+ ENTRY_END
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ ns4.nameservers.com. IN A
+ SECTION ANSWER
+ ns4.nameservers.com. IN A 127.0.0.1
+ ENTRY_END
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ ns5.nameservers.com. IN A
+ SECTION ANSWER
+ ns5.nameservers.com. IN A 127.0.0.1
+ ENTRY_END
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ ns6.nameservers.com. IN A
+ SECTION ANSWER
+ ns6.nameservers.com. IN A 127.0.0.1
+ ENTRY_END
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ ns7.nameservers.com. IN A
+ SECTION ANSWER
+ ns7.nameservers.com. IN A 127.0.0.1
+ ENTRY_END
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ ns8.nameservers.com. IN A
+ SECTION ANSWER
+ ns8.nameservers.com. IN A 127.0.0.1
+ ENTRY_END
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ ns9.nameservers.com. IN A
+ SECTION ANSWER
+ ns9.nameservers.com. IN A 127.0.0.1
+ ENTRY_END
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ ns10.nameservers.com. IN A
+ SECTION ANSWER
+ ns10.nameservers.com. IN A 127.0.0.1
+ ENTRY_END
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ ns11.nameservers.com. IN A
+ SECTION ANSWER
+ ns11.nameservers.com. IN A 127.0.0.1
+ ENTRY_END
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ ns12.nameservers.com. IN A
+ SECTION ANSWER
+ ns12.nameservers.com. IN A 127.0.0.1
+ ENTRY_END
+
+ ; Reply no-data to AAAA queries
+ ENTRY_BEGIN
+ MATCH opcode subdomain
+ ADJUST copy_id copy_query
+ REPLY QR NOERROR
+ SECTION QUESTION
+ nameservers.com. IN A
+ ENTRY_END
+RANGE_END
+
+; Query for a domain
+STEP 0 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+a.example.com. IN A
+ENTRY_END
+
+; Answer with delegation
+STEP 1 REPLY
+ENTRY_BEGIN
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+a.example.com. IN A
+SECTION AUTHORITY
+a.example.com. IN NS ns1.nameservers.com.
+a.example.com. IN NS ns2.nameservers.com.
+a.example.com. IN NS ns3.nameservers.com.
+a.example.com. IN NS ns4.nameservers.com.
+a.example.com. IN NS ns5.nameservers.com.
+a.example.com. IN NS ns6.nameservers.com.
+a.example.com. IN NS ns7.nameservers.com.
+a.example.com. IN NS ns8.nameservers.com.
+a.example.com. IN NS ns9.nameservers.com.
+a.example.com. IN NS ns10.nameservers.com.
+a.example.com. IN NS ns11.nameservers.com.
+a.example.com. IN NS ns12.nameservers.com.
+ENTRY_END
+
+; Reply NXDOMAIN to MAX_TARGET_NX queries(6) x2 (A+AAAA)
+STEP 2 REPLY
+ENTRY_BEGIN
+ADJUST copy_id copy_query
+REPLY QR NXDOMAIN
+SECTION QUESTION
+a.query. IN A
+SECTION AUTHORITY
+example.com. IN SOA ns.example.com email.example.com 1 2 3 4 60
+ENTRY_END
+STEP 3 REPLY
+ENTRY_BEGIN
+ADJUST copy_id copy_query
+REPLY QR NXDOMAIN
+SECTION QUESTION
+a.query. IN A
+SECTION AUTHORITY
+example.com. IN SOA ns.ns email.email 1 2 3 4 60
+ENTRY_END
+STEP 4 REPLY
+ENTRY_BEGIN
+ADJUST copy_id copy_query
+REPLY QR NXDOMAIN
+SECTION QUESTION
+a.query. IN A
+ENTRY_END
+STEP 5 REPLY
+ENTRY_BEGIN
+ADJUST copy_id copy_query
+REPLY QR NXDOMAIN
+SECTION QUESTION
+a.query. IN A
+ENTRY_END
+STEP 6 REPLY
+ENTRY_BEGIN
+ADJUST copy_id copy_query
+REPLY QR NXDOMAIN
+SECTION QUESTION
+a.query. IN A
+ENTRY_END
+STEP 7 REPLY
+ENTRY_BEGIN
+ADJUST copy_id copy_query
+REPLY QR NXDOMAIN
+SECTION QUESTION
+a.query. IN A
+ENTRY_END
+STEP 8 REPLY
+ENTRY_BEGIN
+ADJUST copy_id copy_query
+REPLY QR NXDOMAIN
+SECTION QUESTION
+a.query. IN A
+ENTRY_END
+STEP 9 REPLY
+ENTRY_BEGIN
+ADJUST copy_id copy_query
+REPLY QR NXDOMAIN
+SECTION QUESTION
+a.query. IN A
+ENTRY_END
+STEP 10 REPLY
+ENTRY_BEGIN
+ADJUST copy_id copy_query
+REPLY QR NXDOMAIN
+SECTION QUESTION
+a.query. IN A
+ENTRY_END
+STEP 11 REPLY
+ENTRY_BEGIN
+ADJUST copy_id copy_query
+REPLY QR NXDOMAIN
+SECTION QUESTION
+a.query. IN A
+ENTRY_END
+STEP 12 REPLY
+ENTRY_BEGIN
+ADJUST copy_id copy_query
+REPLY QR NXDOMAIN
+SECTION QUESTION
+a.query. IN A
+ENTRY_END
+STEP 13 REPLY
+ENTRY_BEGIN
+ADJUST copy_id copy_query
+REPLY QR NXDOMAIN
+SECTION QUESTION
+a.query. IN A
+ENTRY_END
+
+; We should receive SERVFAIL because MAX_TARGET_NX was reached
+STEP 14 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA SERVFAIL
+SECTION QUESTION
+a.example.com. IN A
+ENTRY_END
+
+; Query for another domain in the same delegation
+STEP 20 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+b.a.example.com. IN A
+ENTRY_END
+
+; We still have 6 NSes that Unbound didn't try to resolve
+; Reply with NXDOMAIN for 5 of them
+STEP 21 REPLY
+ENTRY_BEGIN
+ADJUST copy_id copy_query
+REPLY QR NXDOMAIN
+SECTION QUESTION
+a.query. IN A
+ENTRY_END
+STEP 22 REPLY
+ENTRY_BEGIN
+ADJUST copy_id copy_query
+REPLY QR NXDOMAIN
+SECTION QUESTION
+a.query. IN A
+ENTRY_END
+STEP 23 REPLY
+ENTRY_BEGIN
+ADJUST copy_id copy_query
+REPLY QR NXDOMAIN
+SECTION QUESTION
+a.query. IN A
+ENTRY_END
+STEP 24 REPLY
+ENTRY_BEGIN
+ADJUST copy_id copy_query
+REPLY QR NXDOMAIN
+SECTION QUESTION
+a.query. IN A
+ENTRY_END
+STEP 25 REPLY
+ENTRY_BEGIN
+ADJUST copy_id copy_query
+REPLY QR NXDOMAIN
+SECTION QUESTION
+a.query. IN A
+ENTRY_END
+STEP 26 REPLY
+ENTRY_BEGIN
+ADJUST copy_id copy_query
+REPLY QR NXDOMAIN
+SECTION QUESTION
+a.query. IN A
+ENTRY_END
+STEP 27 REPLY
+ENTRY_BEGIN
+ADJUST copy_id copy_query
+REPLY QR NXDOMAIN
+SECTION QUESTION
+a.query. IN A
+ENTRY_END
+STEP 28 REPLY
+ENTRY_BEGIN
+ADJUST copy_id copy_query
+REPLY QR NXDOMAIN
+SECTION QUESTION
+a.query. IN A
+ENTRY_END
+STEP 29 REPLY
+ENTRY_BEGIN
+ADJUST copy_id copy_query
+REPLY QR NXDOMAIN
+SECTION QUESTION
+a.query. IN A
+ENTRY_END
+STEP 30 REPLY
+ENTRY_BEGIN
+ADJUST copy_id copy_query
+REPLY QR NXDOMAIN
+SECTION QUESTION
+a.query. IN A
+ENTRY_END
+
+; Unbound will reach the upstream and get the answer for the final NS
+; which has the answer for the client query.
+
+STEP 40 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+b.a.example.com. IN A
+SECTION ANSWER
+b.a.example.com. IN A 127.0.0.0
+ENTRY_END
+
+; Allow for possible pending NS query (AAAA) to get answered
+STEP 41 TRAFFIC
+
+SCENARIO_END
diff --git a/testdata/iter_nxns_fallback.rpl b/testdata/iter_nxns_fallback.rpl
new file mode 100644
index 000000000000..324068604be0
--- /dev/null
+++ b/testdata/iter_nxns_fallback.rpl
@@ -0,0 +1,380 @@
+; Check if fallback to the parent side works when MAX_TARGET_NX is reached.
+
+server:
+ module-config: "iterator"
+ trust-anchor-signaling: no
+ target-fetch-policy: "0 0 0 0 0"
+ verbosity: 3
+ access-control: 127.0.0.1 allow_snoop
+ qname-minimisation: no
+ minimal-responses: no
+ rrset-roundrobin: no
+
+stub-zone:
+ name: "."
+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test the NXNS fallback
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 100
+ ADDRESS 193.0.14.129
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ . IN NS
+ SECTION ANSWER
+ . IN NS K.ROOT-SERVERS.NET.
+ SECTION ADDITIONAL
+ K.ROOT-SERVERS.NET. IN A 193.0.14.129
+ ENTRY_END
+
+ ENTRY_BEGIN
+ MATCH opcode qtype subdomain
+ ADJUST copy_id copy_query
+ REPLY QR NOERROR
+ SECTION QUESTION
+ example.com. IN A
+ SECTION AUTHORITY
+ com. IN NS a.gtld-servers.net.
+ SECTION ADDITIONAL
+ a.gtld-servers.net. IN A 192.5.6.30
+ ENTRY_END
+
+ ENTRY_BEGIN
+ MATCH opcode subdomain
+ ADJUST copy_id copy_query
+ REPLY QR NOERROR
+ SECTION QUESTION
+ nonexistant.com. IN A
+ SECTION AUTHORITY
+ com. IN NS a.gtld-servers.net.
+ SECTION ADDITIONAL
+ a.gtld-servers.net. IN A 192.5.6.30
+ ENTRY_END
+RANGE_END
+
+; a.gtld-servers.net.
+RANGE_BEGIN 0 100
+ ADDRESS 192.5.6.30
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ com. IN NS
+ SECTION ANSWER
+ com. IN NS a.gtld-servers.net.
+ SECTION ADDITIONAL
+ a.gtld-servers.net. IN A 192.5.6.30
+ ENTRY_END
+
+ ENTRY_BEGIN
+ MATCH opcode qtype subdomain
+ ADJUST copy_id copy_query
+ REPLY QR NOERROR
+ SECTION QUESTION
+ example.com. IN A
+ SECTION AUTHORITY
+ example.com. IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. 10 IN A 1.2.3.4
+ ENTRY_END
+
+ ENTRY_BEGIN
+ MATCH opcode subdomain
+ ADJUST copy_id copy_query
+ REPLY QR NOERROR
+ SECTION QUESTION
+ nonexistant.com. IN A
+ SECTION AUTHORITY
+ nonexistant.com. IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. 10 IN A 1.2.3.4
+ ENTRY_END
+RANGE_END
+
+; ns.example.com.
+RANGE_BEGIN 0 100
+ ADDRESS 1.2.3.4
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ example.com. IN NS
+ SECTION ANSWER
+ example.com. IN NS ns1.nonexistant.com.
+ example.com. IN NS ns2.nonexistant.com.
+ example.com. IN NS ns3.nonexistant.com.
+ example.com. IN NS ns4.nonexistant.com.
+ example.com. IN NS ns5.nonexistant.com.
+ example.com. IN NS ns6.nonexistant.com.
+ example.com. IN NS ns7.nonexistant.com.
+ example.com. IN NS ns8.nonexistant.com.
+ example.com. IN NS ns9.nonexistant.com.
+ example.com. IN NS ns10.nonexistant.com.
+ example.com. IN NS ns11.nonexistant.com.
+ example.com. IN NS ns12.nonexistant.com.
+ ENTRY_END
+
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ ns.example.com. IN A
+ SECTION ANSWER
+ ns.example.com. 10 IN A 1.2.3.4
+ ENTRY_END
+
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ ns.example.com. IN AAAA
+ ENTRY_END
+
+ ENTRY_BEGIN
+ MATCH opcode subdomain
+ ADJUST copy_id copy_query
+ REPLY QR NXDOMAIN
+ SECTION QUESTION
+ nonexistant.com. IN A
+ ENTRY_END
+
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ a.example.com. IN A
+ SECTION ANSWER
+ a.example.com. IN A 10.20.30.40
+ SECTION AUTHORITY
+ example.com. IN NS ns1.nonexistant.com.
+ example.com. IN NS ns2.nonexistant.com.
+ example.com. IN NS ns3.nonexistant.com.
+ example.com. IN NS ns4.nonexistant.com.
+ example.com. IN NS ns5.nonexistant.com.
+ example.com. IN NS ns6.nonexistant.com.
+ example.com. IN NS ns7.nonexistant.com.
+ example.com. IN NS ns8.nonexistant.com.
+ example.com. IN NS ns9.nonexistant.com.
+ example.com. IN NS ns10.nonexistant.com.
+ example.com. IN NS ns11.nonexistant.com.
+ example.com. IN NS ns12.nonexistant.com.
+ ENTRY_END
+
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ b.example.com. IN A
+ SECTION ANSWER
+ b.example.com. IN A 10.20.30.40
+ SECTION AUTHORITY
+ example.com. IN NS ns1.nonexistant.com.
+ example.com. IN NS ns2.nonexistant.com.
+ example.com. IN NS ns3.nonexistant.com.
+ example.com. IN NS ns4.nonexistant.com.
+ example.com. IN NS ns5.nonexistant.com.
+ example.com. IN NS ns6.nonexistant.com.
+ example.com. IN NS ns7.nonexistant.com.
+ example.com. IN NS ns8.nonexistant.com.
+ example.com. IN NS ns9.nonexistant.com.
+ example.com. IN NS ns10.nonexistant.com.
+ example.com. IN NS ns11.nonexistant.com.
+ example.com. IN NS ns12.nonexistant.com.
+ ENTRY_END
+
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ c.example.com. IN A
+ SECTION ANSWER
+ c.example.com. IN A 10.20.30.40
+ SECTION AUTHORITY
+ example.com. IN NS ns1.nonexistant.com.
+ example.com. IN NS ns2.nonexistant.com.
+ example.com. IN NS ns3.nonexistant.com.
+ example.com. IN NS ns4.nonexistant.com.
+ example.com. IN NS ns5.nonexistant.com.
+ example.com. IN NS ns6.nonexistant.com.
+ example.com. IN NS ns7.nonexistant.com.
+ example.com. IN NS ns8.nonexistant.com.
+ example.com. IN NS ns9.nonexistant.com.
+ example.com. IN NS ns10.nonexistant.com.
+ example.com. IN NS ns11.nonexistant.com.
+ example.com. IN NS ns12.nonexistant.com.
+ ENTRY_END
+
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ d.example.com. IN A
+ SECTION ANSWER
+ d.example.com. IN A 10.20.30.40
+ SECTION AUTHORITY
+ example.com. IN NS ns1.nonexistant.com.
+ example.com. IN NS ns2.nonexistant.com.
+ example.com. IN NS ns3.nonexistant.com.
+ example.com. IN NS ns4.nonexistant.com.
+ example.com. IN NS ns5.nonexistant.com.
+ example.com. IN NS ns6.nonexistant.com.
+ example.com. IN NS ns7.nonexistant.com.
+ example.com. IN NS ns8.nonexistant.com.
+ example.com. IN NS ns9.nonexistant.com.
+ example.com. IN NS ns10.nonexistant.com.
+ example.com. IN NS ns11.nonexistant.com.
+ example.com. IN NS ns12.nonexistant.com.
+ ENTRY_END
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+a.example.com. IN A
+ENTRY_END
+
+; This was resolved by asking the parent side nameservers
+STEP 2 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+a.example.com. IN A
+SECTION ANSWER
+a.example.com. IN A 10.20.30.40
+SECTION AUTHORITY
+example.com. IN NS ns1.nonexistant.com.
+example.com. IN NS ns2.nonexistant.com.
+example.com. IN NS ns3.nonexistant.com.
+example.com. IN NS ns4.nonexistant.com.
+example.com. IN NS ns5.nonexistant.com.
+example.com. IN NS ns6.nonexistant.com.
+example.com. IN NS ns7.nonexistant.com.
+example.com. IN NS ns8.nonexistant.com.
+example.com. IN NS ns9.nonexistant.com.
+example.com. IN NS ns10.nonexistant.com.
+example.com. IN NS ns11.nonexistant.com.
+example.com. IN NS ns12.nonexistant.com.
+ENTRY_END
+
+; The child side nameservers are now known to Unbound
+
+; Query again, the child server nameservers will be asked now
+STEP 3 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+b.example.com. IN A
+ENTRY_END
+
+; This was resolved by falling back to the parent side nameservers
+STEP 4 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+b.example.com. IN A
+SECTION ANSWER
+b.example.com. IN A 10.20.30.40
+SECTION AUTHORITY
+example.com. IN NS ns1.nonexistant.com.
+example.com. IN NS ns2.nonexistant.com.
+example.com. IN NS ns3.nonexistant.com.
+example.com. IN NS ns4.nonexistant.com.
+example.com. IN NS ns5.nonexistant.com.
+example.com. IN NS ns6.nonexistant.com.
+example.com. IN NS ns7.nonexistant.com.
+example.com. IN NS ns8.nonexistant.com.
+example.com. IN NS ns9.nonexistant.com.
+example.com. IN NS ns10.nonexistant.com.
+example.com. IN NS ns11.nonexistant.com.
+example.com. IN NS ns12.nonexistant.com.
+ENTRY_END
+
+; Query a third time, this will get the cached NXDOMAINs (no NX counter for
+; those) and will go to the parent as a last resort. This query will test that
+; we will not have resolution for the lame(parent side) addresses that could
+; raise the NX counter because of no address addition to the delegation point
+; (the same addresses are already there).
+STEP 5 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+c.example.com. IN A
+ENTRY_END
+
+; This was resolved by going back to the parent side nameservers (child side
+; was exhausted from cache and queries < MAX_TARGET_NX).
+STEP 6 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+c.example.com. IN A
+SECTION ANSWER
+c.example.com. IN A 10.20.30.40
+SECTION AUTHORITY
+example.com. IN NS ns1.nonexistant.com.
+example.com. IN NS ns2.nonexistant.com.
+example.com. IN NS ns3.nonexistant.com.
+example.com. IN NS ns4.nonexistant.com.
+example.com. IN NS ns5.nonexistant.com.
+example.com. IN NS ns6.nonexistant.com.
+example.com. IN NS ns7.nonexistant.com.
+example.com. IN NS ns8.nonexistant.com.
+example.com. IN NS ns9.nonexistant.com.
+example.com. IN NS ns10.nonexistant.com.
+example.com. IN NS ns11.nonexistant.com.
+example.com. IN NS ns12.nonexistant.com.
+ENTRY_END
+
+; Allow for the nameserver glue to expire
+STEP 10 TIME_PASSES ELAPSE 11
+
+; Query again for the parent side fallback
+STEP 11 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+d.example.com. IN A
+ENTRY_END
+
+; This was resolved by falling back to the parent side nameservers
+STEP 12 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+d.example.com. IN A
+SECTION ANSWER
+d.example.com. IN A 10.20.30.40
+SECTION AUTHORITY
+example.com. IN NS ns1.nonexistant.com.
+example.com. IN NS ns2.nonexistant.com.
+example.com. IN NS ns3.nonexistant.com.
+example.com. IN NS ns4.nonexistant.com.
+example.com. IN NS ns5.nonexistant.com.
+example.com. IN NS ns6.nonexistant.com.
+example.com. IN NS ns7.nonexistant.com.
+example.com. IN NS ns8.nonexistant.com.
+example.com. IN NS ns9.nonexistant.com.
+example.com. IN NS ns10.nonexistant.com.
+example.com. IN NS ns11.nonexistant.com.
+example.com. IN NS ns12.nonexistant.com.
+ENTRY_END
+
+SCENARIO_END
diff --git a/testdata/iter_nxns_parentside.rpl b/testdata/iter_nxns_parentside.rpl
new file mode 100644
index 000000000000..94a5a6f1bf52
--- /dev/null
+++ b/testdata/iter_nxns_parentside.rpl
@@ -0,0 +1,118 @@
+; Check if the NXNS fallback to the parent side does not mess with normal
+; parent side resolution. Parent side resolution should SERVFAIL when reaching
+; the MAX_TARGET_NX limit.
+
+server:
+ module-config: "iterator"
+ trust-anchor-signaling: no
+ target-fetch-policy: "0 0 0 0 0"
+ verbosity: 3
+ access-control: 127.0.0.1 allow_snoop
+ qname-minimisation: no
+ minimal-responses: no
+ rrset-roundrobin: no
+
+stub-zone:
+ name: "."
+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test that the NXNS fallback does not mess with parent side resolution
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 100
+ ADDRESS 193.0.14.129
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ . IN NS
+ SECTION ANSWER
+ . IN NS K.ROOT-SERVERS.NET.
+ SECTION ADDITIONAL
+ K.ROOT-SERVERS.NET. IN A 193.0.14.129
+ ENTRY_END
+
+ ENTRY_BEGIN
+ MATCH opcode qtype subdomain
+ ADJUST copy_id copy_query
+ REPLY QR NOERROR
+ SECTION QUESTION
+ example.com. IN A
+ SECTION AUTHORITY
+ com. IN NS a.gtld-servers.net.
+ SECTION ADDITIONAL
+ a.gtld-servers.net. IN A 192.5.6.30
+ ENTRY_END
+
+ ENTRY_BEGIN
+ MATCH opcode subdomain
+ ADJUST copy_id copy_query
+ REPLY QR NOERROR
+ SECTION QUESTION
+ nonexistant.com. IN A
+ SECTION AUTHORITY
+ com. IN NS a.gtld-servers.net.
+ SECTION ADDITIONAL
+ a.gtld-servers.net. IN A 192.5.6.30
+ ENTRY_END
+RANGE_END
+
+; a.gtld-servers.net.
+RANGE_BEGIN 0 100
+ ADDRESS 192.5.6.30
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ com. IN NS
+ SECTION ANSWER
+ com. IN NS a.gtld-servers.net.
+ SECTION ADDITIONAL
+ a.gtld-servers.net. IN A 192.5.6.30
+ ENTRY_END
+
+ ENTRY_BEGIN
+ MATCH opcode qtype subdomain
+ ADJUST copy_id copy_query
+ REPLY QR NOERROR
+ SECTION QUESTION
+ example.com. IN A
+ SECTION AUTHORITY
+ example.com. IN NS ns1.nonexistant.com.
+ example.com. IN NS ns2.nonexistant.com.
+ example.com. IN NS ns3.nonexistant.com.
+ example.com. IN NS ns4.nonexistant.com.
+ example.com. IN NS ns5.nonexistant.com.
+ example.com. IN NS ns6.nonexistant.com.
+ example.com. IN NS ns7.nonexistant.com.
+ example.com. IN NS ns8.nonexistant.com.
+ ENTRY_END
+
+ ENTRY_BEGIN
+ MATCH opcode subdomain
+ ADJUST copy_id copy_query
+ REPLY QR NXDOMAIN
+ SECTION QUESTION
+ nonexistant.com. IN A
+ ENTRY_END
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+a.example.com. IN A
+ENTRY_END
+
+STEP 2 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA SERVFAIL
+SECTION QUESTION
+a.example.com. IN A
+ENTRY_END
+
+SCENARIO_END
diff --git a/testdata/serve_expired_client_timeout_no_prefetch.rpl b/testdata/serve_expired_client_timeout_no_prefetch.rpl
new file mode 100644
index 000000000000..aed397d9e9ae
--- /dev/null
+++ b/testdata/serve_expired_client_timeout_no_prefetch.rpl
@@ -0,0 +1,110 @@
+; config options
+server:
+ module-config: "validator iterator"
+ qname-minimisation: "no"
+ minimal-responses: no
+ serve-expired: yes
+ serve-expired-client-timeout: 1
+ serve-expired-reply-ttl: 123
+ ede: yes
+ ede-serve-expired: yes
+
+stub-zone:
+ name: "example.com"
+ stub-addr: 1.2.3.4
+CONFIG_END
+
+SCENARIO_BEGIN Test that no prefetch is triggered for 0TTL records with serve-expired and client-timeout enabled
+; Scenario overview:
+; - query for example.com. IN A
+; - check that we get an answer for example.com. IN A with the correct TTL
+; - query again right at the 0TTL cached entry
+; - check that we get the cached answer with no prefetching triggered
+
+; ns.example.com.
+RANGE_BEGIN 0 100
+ ADDRESS 1.2.3.4
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ example.com. IN NS
+ SECTION ANSWER
+ example.com. IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. IN A 1.2.3.4
+ ENTRY_END
+RANGE_END
+
+; ns.example.com.
+RANGE_BEGIN 0 10
+ ADDRESS 1.2.3.4
+ ; response to A query
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ example.com. IN A
+ SECTION ANSWER
+ example.com. 10 IN A 5.6.7.8
+ SECTION AUTHORITY
+ example.com. IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. IN A 1.2.3.4
+ ENTRY_END
+RANGE_END
+
+; Query with RD flag
+STEP 0 QUERY
+ENTRY_BEGIN
+ REPLY RD
+ SECTION QUESTION
+ example.com. IN A
+ENTRY_END
+
+; Check that we got the correct answer (should be cached)
+STEP 1 CHECK_ANSWER
+ENTRY_BEGIN
+ MATCH all ttl
+ REPLY QR RD RA NOERROR
+ SECTION QUESTION
+ example.com. IN A
+ SECTION ANSWER
+ example.com. 10 IN A 5.6.7.8
+ SECTION AUTHORITY
+ example.com. IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+; Wait for the TTL to expire and produce a 0 TTL cached record.
+STEP 10 TIME_PASSES ELAPSE 10
+
+; Query again
+STEP 20 QUERY
+ENTRY_BEGIN
+ REPLY RD DO
+ SECTION QUESTION
+ example.com. IN A
+ENTRY_END
+
+; This should come from the cache with no prefetch triggered (earlier bug).
+STEP 21 CHECK_ANSWER
+ENTRY_BEGIN
+ MATCH all ttl
+ REPLY QR RD RA DO NOERROR
+ SECTION QUESTION
+ example.com. IN A
+ SECTION ANSWER
+ example.com. 0 IN A 5.6.7.8
+ SECTION AUTHORITY
+ example.com. 3590 IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. 3590 IN A 1.2.3.4
+ENTRY_END
+
+; If a prefetch triggers the test will fail with 'messages pending'.
+
+SCENARIO_END
diff --git a/testdata/stat_values.tdir/stat_values.conf b/testdata/stat_values.tdir/stat_values.conf
index 5cec691f589d..dc4553920635 100644
--- a/testdata/stat_values.tdir/stat_values.conf
+++ b/testdata/stat_values.tdir/stat_values.conf
@@ -1,5 +1,5 @@
server:
- verbosity: 2
+ verbosity: 5
num-threads: 1
interface: 127.0.0.1
port: @PORT@
@@ -11,6 +11,9 @@ server:
do-not-query-localhost: no
extended-statistics: yes
identity: "stat_values"
+ outbound-msg-retry: 0
+ root-key-sentinel: no
+ trust-anchor-signaling: no
local-zone: local.zone static
local-data: "www.local.zone A 192.0.2.1"
diff --git a/testdata/stat_values.tdir/stat_values.test b/testdata/stat_values.tdir/stat_values.test
index 1cd19fa2b43e..ef86a0471d60 100644
--- a/testdata/stat_values.tdir/stat_values.test
+++ b/testdata/stat_values.tdir/stat_values.test
@@ -186,6 +186,7 @@ num.query.opcode.QUERY=1
num.query.flags.RD=1
num.query.flags.AD=1
num.query.edns.present=1
+num.query.udpout=1
msg.cache.count=1
rrset.cache.count=1
infra.cache.count=1
@@ -223,6 +224,7 @@ num.query.flags.AD=1
num.query.flags.RD=1
num.query.opcode.QUERY=1
num.query.type.A=1
+num.query.udpout=1
msg.cache.count=1
rrset.cache.count=1
infra.cache.count=1"
@@ -249,6 +251,7 @@ num.query.opcode.QUERY=1
num.query.flags.RD=1
num.query.flags.AD=1
num.query.edns.present=1
+num.query.udpout=1
msg.cache.count=2
rrset.cache.count=2
infra.cache.count=2
@@ -263,6 +266,7 @@ if grep "192.0.2.1" outfile; then
else
end 1
fi
+sleep 1 # make sure the outgoing UDP (and the edns1xx0 retry) are accounted for.
check_stats "\
total.num.queries=1
total.num.expired=1
@@ -274,6 +278,7 @@ num.query.flags.AD=1
num.query.flags.RD=1
num.query.opcode.QUERY=1
num.query.type.A=1
+num.query.udpout=2
total.num.cachemiss=1
msg.cache.count=2
rrset.cache.count=2
@@ -327,6 +332,7 @@ num.query.edns.present=1
num.query.flags.RD=1
num.query.opcode.QUERY=1
num.query.type.A=1
+num.query.udpout=1
total.num.queries=1
total.num.recursivereplies=1
msg.cache.count=3
diff --git a/testdata/subnet_prefetch.crpl b/testdata/subnet_prefetch.crpl
index 7083aba6a563..04922f2bbe48 100644
--- a/testdata/subnet_prefetch.crpl
+++ b/testdata/subnet_prefetch.crpl
@@ -12,7 +12,6 @@ server:
access-control: 127.0.0.1 allow_snoop
qname-minimisation: no
minimal-responses: no
- serve-expired: yes
prefetch: yes
stub-zone:
@@ -20,7 +19,7 @@ stub-zone:
stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
CONFIG_END
-SCENARIO_BEGIN Test prefetch option for global cache
+SCENARIO_BEGIN Test prefetch option for global cache with ECS enabled
; K.ROOT-SERVERS.NET.
RANGE_BEGIN 0 100
@@ -34,9 +33,6 @@ RANGE_BEGIN 0 100
SECTION ANSWER
. IN NS K.ROOT-SERVERS.NET.
SECTION ADDITIONAL
- HEX_EDNSDATA_BEGIN
- ;; we expect to receive empty
- HEX_EDNSDATA_END
K.ROOT-SERVERS.NET. IN A 193.0.14.129
ENTRY_END
@@ -65,9 +61,6 @@ RANGE_BEGIN 0 100
SECTION ANSWER
com. IN NS a.gtld-servers.net.
SECTION ADDITIONAL
- HEX_EDNSDATA_BEGIN
- ;; we expect to receive empty
- HEX_EDNSDATA_END
a.gtld-servers.net. IN A 192.5.6.30
ENTRY_END
@@ -96,9 +89,6 @@ RANGE_BEGIN 0 10
SECTION ANSWER
example.com. IN NS ns.example.com.
SECTION ADDITIONAL
- HEX_EDNSDATA_BEGIN
- ;; we expect to receive empty
- HEX_EDNSDATA_END
ns.example.com. IN A 1.2.3.4
ENTRY_END
@@ -130,9 +120,6 @@ RANGE_BEGIN 11 100
SECTION ANSWER
example.com. IN NS ns.example.com.
SECTION ADDITIONAL
- HEX_EDNSDATA_BEGIN
- ;; we expect to receive empty
- HEX_EDNSDATA_END
ns.example.com. IN A 1.2.3.4
ENTRY_END
@@ -144,7 +131,7 @@ RANGE_BEGIN 11 100
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
- www.example.com. IN A 10.20.30.40
+ www.example.com. 10 IN A 10.20.30.40
SECTION AUTHORITY
example.com. IN NS ns.example.com.
SECTION ADDITIONAL
@@ -167,23 +154,23 @@ SECTION QUESTION
www.example.com. IN A
ENTRY_END
-; This answer should be in the global cache
+; This answer should be in the global cache (because no ECS from upstream)
STEP 2 CHECK_ANSWER
ENTRY_BEGIN
MATCH all
REPLY QR RD RA NOERROR
SECTION QUESTION
-www.example.com. IN A
+www.example.com. IN A
SECTION ANSWER
-www.example.com. IN A 10.20.30.40
+www.example.com. IN A 10.20.30.40
SECTION AUTHORITY
-example.com. IN NS ns.example.com.
+example.com. IN NS ns.example.com.
SECTION ADDITIONAL
-ns.example.com. IN A 1.2.3.4
+ns.example.com. IN A 1.2.3.4
ENTRY_END
; Try to trigger a prefetch
-STEP 3 TIME_PASSES ELAPSE 11
+STEP 3 TIME_PASSES ELAPSE 9
STEP 11 QUERY
ENTRY_BEGIN
@@ -192,24 +179,46 @@ SECTION QUESTION
www.example.com. IN A
ENTRY_END
-; This expired record came from the cache and a prefetch is triggered
+; This record came from the global cache and a prefetch was triggered
STEP 12 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl
REPLY QR RD RA NOERROR
SECTION QUESTION
-www.example.com. IN A
+www.example.com. IN A
SECTION ANSWER
-www.example.com. 30 IN A 10.20.30.40
+www.example.com. 1 IN A 10.20.30.40
SECTION AUTHORITY
-example.com. 3589 IN NS ns.example.com.
+example.com. 3591 IN NS ns.example.com.
SECTION ADDITIONAL
-ns.example.com. 3589 IN A 1.2.3.4
+ns.example.com. 3591 IN A 1.2.3.4
+ENTRY_END
+
+; Allow time to pass so that the global cache record is expired
+STEP 13 TIME_PASSES ELAPSE 2
+
+; Query again to verify that the record was prefetched and stored in the ECS
+; cache (because the server replied with ECS this time)
+STEP 14 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
ENTRY_END
-; Allow upstream to reply to the prefetch query.
-; It can only be answered if correct ECS was derived from the client's IP.
-; Otherwise the test will fail with "messages pending".
-STEP 13 TRAFFIC
+; This record came from the ECS cache
+STEP 15 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 8 IN A 10.20.30.40
+SECTION AUTHORITY
+example.com. 3598 IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. 3598 IN A 1.2.3.4
+ENTRY_END
SCENARIO_END
diff --git a/testdata/subnet_prefetch_with_client_ecs.crpl b/testdata/subnet_prefetch_with_client_ecs.crpl
index b0410255e85d..ddc832c475de 100644
--- a/testdata/subnet_prefetch_with_client_ecs.crpl
+++ b/testdata/subnet_prefetch_with_client_ecs.crpl
@@ -12,7 +12,6 @@ server:
access-control: 127.0.0.1 allow_snoop
qname-minimisation: no
minimal-responses: no
- serve-expired: yes
prefetch: yes
stub-zone:
@@ -20,7 +19,7 @@ stub-zone:
stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
CONFIG_END
-SCENARIO_BEGIN Test prefetch option for global cache
+SCENARIO_BEGIN Test prefetch option for global cache with ECS enabled and ECS client
; K.ROOT-SERVERS.NET.
RANGE_BEGIN 0 100
@@ -34,9 +33,6 @@ RANGE_BEGIN 0 100
SECTION ANSWER
. IN NS K.ROOT-SERVERS.NET.
SECTION ADDITIONAL
- HEX_EDNSDATA_BEGIN
- ;; we expect to receive empty
- HEX_EDNSDATA_END
K.ROOT-SERVERS.NET. IN A 193.0.14.129
ENTRY_END
@@ -65,9 +61,6 @@ RANGE_BEGIN 0 100
SECTION ANSWER
com. IN NS a.gtld-servers.net.
SECTION ADDITIONAL
- HEX_EDNSDATA_BEGIN
- ;; we expect to receive empty
- HEX_EDNSDATA_END
a.gtld-servers.net. IN A 192.5.6.30
ENTRY_END
@@ -96,9 +89,6 @@ RANGE_BEGIN 0 10
SECTION ANSWER
example.com. IN NS ns.example.com.
SECTION ADDITIONAL
- HEX_EDNSDATA_BEGIN
- ;; we expect to receive empty
- HEX_EDNSDATA_END
ns.example.com. IN A 1.2.3.4
ENTRY_END
@@ -130,9 +120,6 @@ RANGE_BEGIN 11 100
SECTION ANSWER
example.com. IN NS ns.example.com.
SECTION ADDITIONAL
- HEX_EDNSDATA_BEGIN
- ;; we expect to receive empty
- HEX_EDNSDATA_END
ns.example.com. IN A 1.2.3.4
ENTRY_END
@@ -144,7 +131,7 @@ RANGE_BEGIN 11 100
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
- www.example.com. IN A 10.20.30.40
+ www.example.com. 10 IN A 10.20.30.40
SECTION AUTHORITY
example.com. IN NS ns.example.com.
SECTION ADDITIONAL
@@ -173,17 +160,17 @@ ENTRY_BEGIN
MATCH all
REPLY QR RD RA NOERROR
SECTION QUESTION
-www.example.com. IN A
+www.example.com. IN A
SECTION ANSWER
-www.example.com. IN A 10.20.30.40
+www.example.com. IN A 10.20.30.40
SECTION AUTHORITY
-example.com. IN NS ns.example.com.
+example.com. IN NS ns.example.com.
SECTION ADDITIONAL
-ns.example.com. IN A 1.2.3.4
+ns.example.com. IN A 1.2.3.4
ENTRY_END
; Try to trigger a prefetch
-STEP 3 TIME_PASSES ELAPSE 11
+STEP 3 TIME_PASSES ELAPSE 9
STEP 11 QUERY
ENTRY_BEGIN
@@ -192,30 +179,63 @@ SECTION QUESTION
www.example.com. IN A
SECTION ADDITIONAL
HEX_EDNSDATA_BEGIN
- 00 08 00 05 ; OPC, optlen
- 00 01 08 00 ; ip4, source 8, scope 0
- 7f ; 127.0.0.0/8
+ 00 08 00 05 ; OPC, optlen
+ 00 01 08 00 ; ip4, source 8, scope 0
+ 7f ; 127.0.0.0/8
HEX_EDNSDATA_END
ENTRY_END
-; This expired record came from the cache and a prefetch is triggered
+; This record came from the global cache and a prefetch was triggered
STEP 12 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl
REPLY QR RD RA DO NOERROR
SECTION QUESTION
-www.example.com. IN A
+www.example.com. IN A
SECTION ANSWER
-www.example.com. 30 IN A 10.20.30.40
+www.example.com. 1 IN A 10.20.30.40
SECTION AUTHORITY
-example.com. 3589 IN NS ns.example.com.
+example.com. 3591 IN NS ns.example.com.
SECTION ADDITIONAL
-ns.example.com. 3589 IN A 1.2.3.4
+ns.example.com. 3591 IN A 1.2.3.4
ENTRY_END
-; Allow upstream to reply to the prefetch query.
-; It can only be answered if correct ECS was derived from the client's IP.
-; Otherwise the test will fail with "messages pending".
-STEP 13 TRAFFIC
+; Allow time to pass so that the global cache record is expired
+STEP 13 TIME_PASSES ELAPSE 2
+
+; Query again to verify that the record was prefetched and stored in the ECS
+; cache (because the server replied with ECS this time)
+STEP 14 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+www.example.com. IN A
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ 00 08 00 05 ; OPC, optlen
+ 00 01 08 00 ; ip4, source 8, scope 0
+ 7f ; 127.0.0.0/8
+HEX_EDNSDATA_END
+ENTRY_END
+
+; This record came from the ECS cache
+STEP 15 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl
+REPLY QR RD RA DO NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 8 IN A 10.20.30.40
+SECTION AUTHORITY
+example.com. 3598 IN NS ns.example.com.
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ 00 08 00 05 ; OPC, optlen
+ 00 01 08 08 ; ip4, source 8, scope 0
+ 7f ; 127.0.0.0/8
+HEX_EDNSDATA_END
+ns.example.com. 3598 IN A 1.2.3.4
+ENTRY_END
SCENARIO_END
diff --git a/util/iana_ports.inc b/util/iana_ports.inc
index c7662dc62fe3..ae2986c822e5 100644
--- a/util/iana_ports.inc
+++ b/util/iana_ports.inc
@@ -2917,6 +2917,7 @@
3297,
3298,
3299,
+3301,
3302,
3303,
3304,
@@ -4342,6 +4343,13 @@
5859,
5863,
5900,
+5903,
+5904,
+5905,
+5906,
+5907,
+5908,
+5909,
5910,
5911,
5912,
@@ -4553,6 +4561,7 @@
6965,
6966,
6969,
+6980,
6997,
6998,
6999,
diff --git a/util/net_help.c b/util/net_help.c
index 114920e3f905..8153dbdd1818 100644
--- a/util/net_help.c
+++ b/util/net_help.c
@@ -1162,10 +1162,11 @@ add_WIN_cacerts_to_openssl_store(SSL_CTX* tls_ctx)
(const unsigned char **)&pTargetCert->pbCertEncoded,
pTargetCert->cbCertEncoded);
if (!cert1) {
+ unsigned long error = ERR_get_error();
/* return error if a cert fails */
verbose(VERB_ALGO, "%s %d:%s",
"Unable to parse certificate in memory",
- (int)ERR_get_error(), ERR_error_string(ERR_get_error(), NULL));
+ (int)error, ERR_error_string(error, NULL));
return 0;
}
else {
@@ -1176,10 +1177,11 @@ add_WIN_cacerts_to_openssl_store(SSL_CTX* tls_ctx)
/* Ignore error X509_R_CERT_ALREADY_IN_HASH_TABLE which means the
* certificate is already in the store. */
if(ERR_GET_LIB(error) != ERR_LIB_X509 ||
- ERR_GET_REASON(error) != X509_R_CERT_ALREADY_IN_HASH_TABLE) {
+ ERR_GET_REASON(error) != X509_R_CERT_ALREADY_IN_HASH_TABLE) {
+ error = ERR_get_error();
verbose(VERB_ALGO, "%s %d:%s\n",
- "Error adding certificate", (int)ERR_get_error(),
- ERR_error_string(ERR_get_error(), NULL));
+ "Error adding certificate", (int)error,
+ ERR_error_string(error, NULL));
X509_free(cert1);
return 0;
}
diff --git a/validator/val_secalgo.c b/validator/val_secalgo.c
index 7abf66f01d2a..78651674991e 100644
--- a/validator/val_secalgo.c
+++ b/validator/val_secalgo.c
@@ -97,6 +97,23 @@ log_crypto_error(const char* str, unsigned long e)
log_err("%s crypto %s", str, buf);
}
+/**
+ * Output a libcrypto openssl error to the logfile as a debug message.
+ * @param level: debug level to use in verbose() call
+ * @param str: string to add to it.
+ * @param e: the error to output, error number from ERR_get_error().
+ */
+static void
+log_crypto_verbose(enum verbosity_value level, const char* str, unsigned long e)
+{
+ char buf[128];
+ /* or use ERR_error_string if ERR_error_string_n is not avail TODO */
+ ERR_error_string_n(e, buf, sizeof(buf));
+ /* buf now contains */
+ /* error:[error code]:[library name]:[function name]:[reason string] */
+ verbose(level, "%s crypto %s", str, buf);
+}
+
/* return size of digest if supported, or 0 otherwise */
size_t
nsec3_hash_algo_size_supported(int id)
@@ -215,6 +232,10 @@ ds_digest_size_supported(int algo)
switch(algo) {
case LDNS_SHA1:
#if defined(HAVE_EVP_SHA1) && defined(USE_SHA1)
+#ifdef HAVE_EVP_DEFAULT_PROPERTIES_IS_FIPS_ENABLED
+ if (EVP_default_properties_is_fips_enabled(NULL))
+ return 0;
+#endif
return SHA_DIGEST_LENGTH;
#else
if(fake_sha1) return 20;
@@ -325,7 +346,11 @@ dnskey_algo_id_is_supported(int id)
case LDNS_RSASHA1:
case LDNS_RSASHA1_NSEC3:
#ifdef USE_SHA1
+#ifdef HAVE_EVP_DEFAULT_PROPERTIES_IS_FIPS_ENABLED
+ return !EVP_default_properties_is_fips_enabled(NULL);
+#else
return 1;
+#endif
#else
if(fake_sha1) return 1;
return 0;
@@ -341,15 +366,22 @@ dnskey_algo_id_is_supported(int id)
case LDNS_ECDSAP256SHA256:
case LDNS_ECDSAP384SHA384:
#endif
+#if (defined(HAVE_EVP_SHA256) && defined(USE_SHA2)) || (defined(HAVE_EVP_SHA512) && defined(USE_SHA2)) || defined(USE_ECDSA)
+ return 1;
+#endif
#ifdef USE_ED25519
case LDNS_ED25519:
#endif
#ifdef USE_ED448
case LDNS_ED448:
#endif
-#if (defined(HAVE_EVP_SHA256) && defined(USE_SHA2)) || (defined(HAVE_EVP_SHA512) && defined(USE_SHA2)) || defined(USE_ECDSA) || defined(USE_ED25519) || defined(USE_ED448)
+#if defined(USE_ED25519) || defined(USE_ED448)
+#ifdef HAVE_EVP_DEFAULT_PROPERTIES_IS_FIPS_ENABLED
+ return !EVP_default_properties_is_fips_enabled(NULL);
+#else
return 1;
#endif
+#endif
#ifdef USE_GOST
case LDNS_ECC_GOST:
@@ -652,6 +684,36 @@ setup_key_digest(int algo, EVP_PKEY** evp_key, const EVP_MD** digest_type,
return 1;
}
+static void
+digest_ctx_free(EVP_MD_CTX* ctx, EVP_PKEY *evp_key,
+ unsigned char* sigblock, int dofree, int docrypto_free)
+{
+#ifdef HAVE_EVP_MD_CTX_NEW
+ EVP_MD_CTX_destroy(ctx);
+#else
+ EVP_MD_CTX_cleanup(ctx);
+ free(ctx);
+#endif
+ EVP_PKEY_free(evp_key);
+ if(dofree) free(sigblock);
+ else if(docrypto_free) OPENSSL_free(sigblock);
+}
+
+static enum sec_status
+digest_error_status(const char *str)
+{
+ unsigned long e = ERR_get_error();
+#ifdef EVP_R_INVALID_DIGEST
+ if (ERR_GET_LIB(e) == ERR_LIB_EVP &&
+ ERR_GET_REASON(e) == EVP_R_INVALID_DIGEST) {
+ log_crypto_verbose(VERB_ALGO, str, e);
+ return sec_status_indeterminate;
+ }
+#endif
+ log_crypto_verbose(VERB_QUERY, str, e);
+ return sec_status_unchecked;
+}
+
/**
* Check a canonical sig+rrset and signature against a dnskey
* @param buf: buffer with data to verify, the first rrsig part and the
@@ -663,10 +725,11 @@ setup_key_digest(int algo, EVP_PKEY** evp_key, const EVP_MD** digest_type,
* @param keylen: length of keydata.
* @param reason: bogus reason in more detail.
* @return secure if verification succeeded, bogus on crypto failure,
- * unchecked on format errors and alloc failures.
+ * unchecked on format errors and alloc failures, indeterminate
+ * if digest is not supported by the crypto library (openssl3+ only).
*/
enum sec_status
-verify_canonrrset(sldns_buffer* buf, int algo, unsigned char* sigblock,
+verify_canonrrset(sldns_buffer* buf, int algo, unsigned char* sigblock,
unsigned int sigblock_len, unsigned char* key, unsigned int keylen,
char** reason)
{
@@ -735,62 +798,36 @@ verify_canonrrset(sldns_buffer* buf, int algo, unsigned char* sigblock,
}
#ifndef HAVE_EVP_DIGESTVERIFY
if(EVP_DigestInit(ctx, digest_type) == 0) {
- verbose(VERB_QUERY, "verify: EVP_DigestInit failed");
-#ifdef HAVE_EVP_MD_CTX_NEW
- EVP_MD_CTX_destroy(ctx);
-#else
- EVP_MD_CTX_cleanup(ctx);
- free(ctx);
-#endif
- EVP_PKEY_free(evp_key);
- if(dofree) free(sigblock);
- else if(docrypto_free) OPENSSL_free(sigblock);
- return sec_status_unchecked;
+ enum sec_status sec;
+ sec = digest_error_status("verify: EVP_DigestInit failed");
+ digest_ctx_free(ctx, evp_key, sigblock,
+ dofree, docrypto_free);
+ return sec;
}
if(EVP_DigestUpdate(ctx, (unsigned char*)sldns_buffer_begin(buf),
(unsigned int)sldns_buffer_limit(buf)) == 0) {
- verbose(VERB_QUERY, "verify: EVP_DigestUpdate failed");
-#ifdef HAVE_EVP_MD_CTX_NEW
- EVP_MD_CTX_destroy(ctx);
-#else
- EVP_MD_CTX_cleanup(ctx);
- free(ctx);
-#endif
- EVP_PKEY_free(evp_key);
- if(dofree) free(sigblock);
- else if(docrypto_free) OPENSSL_free(sigblock);
+ log_crypto_verbose(VERB_QUERY, "verify: EVP_DigestUpdate failed",
+ ERR_get_error());
+ digest_ctx_free(ctx, evp_key, sigblock,
+ dofree, docrypto_free);
return sec_status_unchecked;
}
res = EVP_VerifyFinal(ctx, sigblock, sigblock_len, evp_key);
#else /* HAVE_EVP_DIGESTVERIFY */
if(EVP_DigestVerifyInit(ctx, NULL, digest_type, NULL, evp_key) == 0) {
- verbose(VERB_QUERY, "verify: EVP_DigestVerifyInit failed");
-#ifdef HAVE_EVP_MD_CTX_NEW
- EVP_MD_CTX_destroy(ctx);
-#else
- EVP_MD_CTX_cleanup(ctx);
- free(ctx);
-#endif
- EVP_PKEY_free(evp_key);
- if(dofree) free(sigblock);
- else if(docrypto_free) OPENSSL_free(sigblock);
- return sec_status_unchecked;
+ enum sec_status sec;
+ sec = digest_error_status("verify: EVP_DigestVerifyInit failed");
+ digest_ctx_free(ctx, evp_key, sigblock,
+ dofree, docrypto_free);
+ return sec;
}
res = EVP_DigestVerify(ctx, sigblock, sigblock_len,
(unsigned char*)sldns_buffer_begin(buf),
sldns_buffer_limit(buf));
#endif
-#ifdef HAVE_EVP_MD_CTX_NEW
- EVP_MD_CTX_destroy(ctx);
-#else
- EVP_MD_CTX_cleanup(ctx);
- free(ctx);
-#endif
- EVP_PKEY_free(evp_key);
-
- if(dofree) free(sigblock);
- else if(docrypto_free) OPENSSL_free(sigblock);
+ digest_ctx_free(ctx, evp_key, sigblock,
+ dofree, docrypto_free);
if(res == 1) {
return sec_status_secure;
diff --git a/validator/val_sigcrypt.c b/validator/val_sigcrypt.c
index d5f16b11f819..5ab21e20e735 100644
--- a/validator/val_sigcrypt.c
+++ b/validator/val_sigcrypt.c
@@ -513,25 +513,96 @@ size_t algo_needs_num_missing(struct algo_needs* n)
int algo_needs_missing(struct algo_needs* n)
{
- int i;
- /* first check if a needed algo was bogus - report that */
- for(i=0; i<ALGO_NEEDS_MAX; i++)
+ int i, miss = -1;
+ /* check if a needed algo was bogus - report that;
+ * check the first missing algo - report that;
+ * or return 0 */
+ for(i=0; i<ALGO_NEEDS_MAX; i++) {
if(n->needs[i] == 2)
return 0;
- /* now check which algo is missing */
- for(i=0; i<ALGO_NEEDS_MAX; i++)
- if(n->needs[i] == 1)
- return i;
+ if(n->needs[i] == 1 && miss == -1)
+ miss = i;
+ }
+ if(miss != -1) return miss;
return 0;
}
+/**
+ * verify rrset, with dnskey rrset, for a specific rrsig in rrset
+ * @param env: module environment, scratch space is used.
+ * @param ve: validator environment, date settings.
+ * @param now: current time for validation (can be overridden).
+ * @param rrset: to be validated.
+ * @param dnskey: DNSKEY rrset, keyset to try.
+ * @param sig_idx: which signature to try to validate.
+ * @param sortree: reused sorted order. Stored in region. Pass NULL at start,
+ * and for a new rrset.
+ * @param reason: if bogus, a string returned, fixed or alloced in scratch.
+ * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure.
+ * @param section: section of packet where this rrset comes from.
+ * @param qstate: qstate with region.
+ * @return secure if any key signs *this* signature. bogus if no key signs it,
+ * unchecked on error, or indeterminate if all keys are not supported by
+ * the crypto library (openssl3+ only).
+ */
static enum sec_status
dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve,
time_t now, struct ub_packed_rrset_key* rrset,
struct ub_packed_rrset_key* dnskey, size_t sig_idx,
struct rbtree_type** sortree,
char** reason, sldns_ede_code *reason_bogus,
- sldns_pkt_section section, struct module_qstate* qstate);
+ sldns_pkt_section section, struct module_qstate* qstate)
+{
+ /* find matching keys and check them */
+ enum sec_status sec = sec_status_bogus;
+ uint16_t tag = rrset_get_sig_keytag(rrset, sig_idx);
+ int algo = rrset_get_sig_algo(rrset, sig_idx);
+ size_t i, num = rrset_get_count(dnskey);
+ size_t numchecked = 0;
+ size_t numindeterminate = 0;
+ int buf_canon = 0;
+ verbose(VERB_ALGO, "verify sig %d %d", (int)tag, algo);
+ if(!dnskey_algo_id_is_supported(algo)) {
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_UNSUPPORTED_DNSKEY_ALG;
+ verbose(VERB_QUERY, "verify sig: unknown algorithm");
+ return sec_status_insecure;
+ }
+
+ for(i=0; i<num; i++) {
+ /* see if key matches keytag and algo */
+ if(algo != dnskey_get_algo(dnskey, i) ||
+ tag != dnskey_calc_keytag(dnskey, i))
+ continue;
+ numchecked ++;
+
+ /* see if key verifies */
+ sec = dnskey_verify_rrset_sig(env->scratch,
+ env->scratch_buffer, ve, now, rrset, dnskey, i,
+ sig_idx, sortree, &buf_canon, reason, reason_bogus,
+ section, qstate);
+ if(sec == sec_status_secure)
+ return sec;
+ else if(sec == sec_status_indeterminate)
+ numindeterminate ++;
+ }
+ if(numchecked == 0) {
+ *reason = "signatures from unknown keys";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_DNSKEY_MISSING;
+ verbose(VERB_QUERY, "verify: could not find appropriate key");
+ return sec_status_bogus;
+ }
+ if(numindeterminate == numchecked) {
+ *reason = "unsupported algorithm by crypto library";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_UNSUPPORTED_DNSKEY_ALG;
+ verbose(VERB_ALGO, "verify sig: unsupported algorithm by "
+ "crypto library");
+ return sec_status_indeterminate;
+ }
+ return sec_status_bogus;
+}
enum sec_status
dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve,
@@ -607,14 +678,14 @@ void algo_needs_reason(struct module_env* env, int alg, char** reason, char* s)
*reason = s;
}
-enum sec_status
+enum sec_status
dnskey_verify_rrset(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
size_t dnskey_idx, char** reason, sldns_ede_code *reason_bogus,
sldns_pkt_section section, struct module_qstate* qstate)
{
enum sec_status sec;
- size_t i, num, numchecked = 0;
+ size_t i, num, numchecked = 0, numindeterminate = 0;
rbtree_type* sortree = NULL;
int buf_canon = 0;
uint16_t tag = dnskey_calc_keytag(dnskey, dnskey_idx);
@@ -642,56 +713,21 @@ dnskey_verify_rrset(struct module_env* env, struct val_env* ve,
if(sec == sec_status_secure)
return sec;
numchecked ++;
+ if(sec == sec_status_indeterminate)
+ numindeterminate ++;
}
verbose(VERB_ALGO, "rrset failed to verify: all signatures are bogus");
- if(!numchecked) *reason = "signature missing";
- return sec_status_bogus;
-}
-
-static enum sec_status
-dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve,
- time_t now, struct ub_packed_rrset_key* rrset,
- struct ub_packed_rrset_key* dnskey, size_t sig_idx,
- struct rbtree_type** sortree,
- char** reason, sldns_ede_code *reason_bogus,
- sldns_pkt_section section, struct module_qstate* qstate)
-{
- /* find matching keys and check them */
- enum sec_status sec = sec_status_bogus;
- uint16_t tag = rrset_get_sig_keytag(rrset, sig_idx);
- int algo = rrset_get_sig_algo(rrset, sig_idx);
- size_t i, num = rrset_get_count(dnskey);
- size_t numchecked = 0;
- int buf_canon = 0;
- verbose(VERB_ALGO, "verify sig %d %d", (int)tag, algo);
- if(!dnskey_algo_id_is_supported(algo)) {
+ if(!numchecked) {
+ *reason = "signature missing";
if(reason_bogus)
- *reason_bogus = LDNS_EDE_UNSUPPORTED_DNSKEY_ALG;
- verbose(VERB_QUERY, "verify sig: unknown algorithm");
- return sec_status_insecure;
- }
-
- for(i=0; i<num; i++) {
- /* see if key matches keytag and algo */
- if(algo != dnskey_get_algo(dnskey, i) ||
- tag != dnskey_calc_keytag(dnskey, i))
- continue;
- numchecked ++;
-
- /* see if key verifies */
- sec = dnskey_verify_rrset_sig(env->scratch,
- env->scratch_buffer, ve, now, rrset, dnskey, i,
- sig_idx, sortree, &buf_canon, reason, reason_bogus,
- section, qstate);
- if(sec == sec_status_secure)
- return sec;
- }
- if(numchecked == 0) {
- *reason = "signatures from unknown keys";
+ *reason_bogus = LDNS_EDE_RRSIGS_MISSING;
+ } else if(numchecked == numindeterminate) {
+ verbose(VERB_ALGO, "rrset failed to verify due to algorithm "
+ "refusal by cryptolib");
if(reason_bogus)
- *reason_bogus = LDNS_EDE_DNSKEY_MISSING;
- verbose(VERB_QUERY, "verify: could not find appropriate key");
- return sec_status_bogus;
+ *reason_bogus = LDNS_EDE_UNSUPPORTED_DNSKEY_ALG;
+ *reason = "algorithm refused by cryptolib";
+ return sec_status_indeterminate;
}
return sec_status_bogus;
}
diff --git a/validator/val_utils.c b/validator/val_utils.c
index 18a7c9c2e95d..18c963d863f1 100644
--- a/validator/val_utils.c
+++ b/validator/val_utils.c
@@ -458,7 +458,7 @@ verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve,
}
/* If it didn't validate with the DNSKEY, try the next one! */
}
- if(numsizesupp != 0) {
+ if(numsizesupp != 0 || sec == sec_status_indeterminate) {
/* there is a working DS, but that DNSKEY is not supported */
return sec_status_insecure;
}