diff options
author | Cy Schubert <cy@FreeBSD.org> | 2022-07-13 13:02:01 +0000 |
---|---|---|
committer | Cy Schubert <cy@FreeBSD.org> | 2022-07-13 13:02:01 +0000 |
commit | d57351465531b38689892ec862de2725b52842dd (patch) | |
tree | 7363167508f031fdb90b9091c07a065f3903325d | |
parent | 5f9f82264b91e041df7cba2406625146e7268ce4 (diff) |
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; } |