merge updates
authorsthen <sthen@openbsd.org>
Mon, 29 Aug 2022 16:04:59 +0000 (16:04 +0000)
committersthen <sthen@openbsd.org>
Mon, 29 Aug 2022 16:04:59 +0000 (16:04 +0000)
56 files changed:
usr.sbin/unbound/Makefile.in
usr.sbin/unbound/cachedb/cachedb.c
usr.sbin/unbound/config.h.in
usr.sbin/unbound/configure
usr.sbin/unbound/configure.ac
usr.sbin/unbound/daemon/cachedump.c
usr.sbin/unbound/daemon/daemon.c
usr.sbin/unbound/daemon/remote.c
usr.sbin/unbound/daemon/stats.c
usr.sbin/unbound/daemon/worker.c
usr.sbin/unbound/dns64/dns64.c
usr.sbin/unbound/doc/Changelog
usr.sbin/unbound/doc/README
usr.sbin/unbound/doc/example.conf.in
usr.sbin/unbound/doc/libunbound.3.in
usr.sbin/unbound/doc/unbound-anchor.8.in
usr.sbin/unbound/doc/unbound-checkconf.8.in
usr.sbin/unbound/doc/unbound-control.8.in
usr.sbin/unbound/doc/unbound-host.1.in
usr.sbin/unbound/doc/unbound.8.in
usr.sbin/unbound/doc/unbound.conf.5.in
usr.sbin/unbound/edns-subnet/subnetmod.c
usr.sbin/unbound/edns-subnet/subnetmod.h
usr.sbin/unbound/ipsecmod/ipsecmod.c
usr.sbin/unbound/iterator/iter_delegpt.c
usr.sbin/unbound/iterator/iter_delegpt.h
usr.sbin/unbound/iterator/iter_utils.c
usr.sbin/unbound/iterator/iter_utils.h
usr.sbin/unbound/iterator/iterator.c
usr.sbin/unbound/iterator/iterator.h
usr.sbin/unbound/libunbound/unbound.h
usr.sbin/unbound/services/authzone.c
usr.sbin/unbound/services/cache/dns.c
usr.sbin/unbound/services/cache/dns.h
usr.sbin/unbound/services/cache/infra.c
usr.sbin/unbound/services/listen_dnsport.c
usr.sbin/unbound/services/mesh.c
usr.sbin/unbound/services/outside_network.c
usr.sbin/unbound/services/outside_network.h
usr.sbin/unbound/sldns/parse.c
usr.sbin/unbound/sldns/rrdef.c
usr.sbin/unbound/sldns/wire2str.c
usr.sbin/unbound/smallapp/unbound-control.c
usr.sbin/unbound/util/config_file.c
usr.sbin/unbound/util/config_file.h
usr.sbin/unbound/util/configlexer.lex
usr.sbin/unbound/util/configparser.y
usr.sbin/unbound/util/data/msgreply.c
usr.sbin/unbound/util/iana_ports.inc
usr.sbin/unbound/util/module.h
usr.sbin/unbound/util/net_help.c
usr.sbin/unbound/util/rtt.h
usr.sbin/unbound/validator/val_secalgo.c
usr.sbin/unbound/validator/val_sigcrypt.c
usr.sbin/unbound/validator/val_utils.c
usr.sbin/unbound/validator/validator.c

index 464d20d..cf907eb 100644 (file)
@@ -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
index 725bc6c..b07743d 100644 (file)
@@ -662,7 +662,7 @@ cachedb_intcache_store(struct module_qstate* qstate)
                return;
        (void)dns_cache_store(qstate->env, &qstate->qinfo,
                qstate->return_msg->rep, 0, qstate->prefetch_leeway, 0,
-               qstate->region, store_flags);
+               qstate->region, store_flags, qstate->qstarttime);
 }
 
 /**
index ef1dd2c..07d47a8 100644 (file)
 /* 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
 
index 2a84807..e5df3e3 100644 (file)
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.71 for unbound 1.16.0.
+# Generated by GNU Autoconf 2.71 for unbound 1.16.2.
 #
 # Report bugs to <unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues>.
 #
@@ -622,8 +622,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.2'
+PACKAGE_STRING='unbound 1.16.2'
 PACKAGE_BUGREPORT='unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues'
 PACKAGE_URL=''
 
@@ -1503,7 +1503,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.2 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1569,7 +1569,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.2:";;
    esac
   cat <<\_ACEOF
 
@@ -1812,7 +1812,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.2
 generated by GNU Autoconf 2.71
 
 Copyright (C) 2021 Free Software Foundation, Inc.
@@ -2469,7 +2469,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.2, which was
 generated by GNU Autoconf 2.71.  Invocation command line was
 
   $ $0$ac_configure_args_raw
@@ -3233,11 +3233,11 @@ UNBOUND_VERSION_MAJOR=1
 
 UNBOUND_VERSION_MINOR=16
 
-UNBOUND_VERSION_MICRO=0
+UNBOUND_VERSION_MICRO=2
 
 
 LIBUNBOUND_CURRENT=9
-LIBUNBOUND_REVISION=16
+LIBUNBOUND_REVISION=18
 LIBUNBOUND_AGE=1
 # 1.0.0 had 0:12:0
 # 1.0.1 had 0:13:0
@@ -3321,6 +3321,8 @@ 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
+# 1.16.2 had 9:18:1
 
 #   Current  -- the number of the binary API that we're implementing
 #   Revision -- which iteration of the implementation of the binary
@@ -19991,6 +19993,12 @@ if test "x$ac_cv_func_FIPS_mode" = xyes
 then :
   printf "%s\n" "#define HAVE_FIPS_MODE 1" >>confdefs.h
 
+fi
+ac_fn_c_check_func "$LINENO" "EVP_default_properties_is_fips_enabled" "ac_cv_func_EVP_default_properties_is_fips_enabled"
+if test "x$ac_cv_func_EVP_default_properties_is_fips_enabled" = xyes
+then :
+  printf "%s\n" "#define HAVE_EVP_DEFAULT_PROPERTIES_IS_FIPS_ENABLED 1" >>confdefs.h
+
 fi
 ac_fn_c_check_func "$LINENO" "EVP_MD_CTX_new" "ac_cv_func_EVP_MD_CTX_new"
 if test "x$ac_cv_func_EVP_MD_CTX_new" = xyes
@@ -21526,7 +21534,46 @@ if test x_$enable_static_exe = x_yes; then
                else
                        LIBS="$LIBS -lgdi32"
                fi
-               LIBS="$LIBS -lz"
+               { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for compress in -lz" >&5
+printf %s "checking for compress in -lz... " >&6; }
+if test ${ac_cv_lib_z_compress+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  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.  */
+char compress ();
+int
+main (void)
+{
+return compress ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+  ac_cv_lib_z_compress=yes
+else $as_nop
+  ac_cv_lib_z_compress=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_z_compress" >&5
+printf "%s\n" "$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
@@ -21547,7 +21594,46 @@ if test x_$enable_fully_static = x_yes; then
                else
                        LIBS="$LIBS -lgdi32"
                fi
-               LIBS="$LIBS -lz"
+               { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for compress in -lz" >&5
+printf %s "checking for compress in -lz... " >&6; }
+if test ${ac_cv_lib_z_compress+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  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.  */
+char compress ();
+int
+main (void)
+{
+return compress ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+  ac_cv_lib_z_compress=yes
+else $as_nop
+  ac_cv_lib_z_compress=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_z_compress" >&5
+printf "%s\n" "$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
@@ -23692,7 +23778,7 @@ printf "%s\n" "#define MAXSYSLOGMSGLEN 10240" >>confdefs.h
 
 
 
-version=1.16.0
+version=1.16.2
 
 date=`date +'%b %e, %Y'`
 
@@ -24204,7 +24290,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.2, which was
 generated by GNU Autoconf 2.71.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -24272,7 +24358,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config='$ac_cs_config_escaped'
 ac_cs_version="\\
-unbound config.status 1.16.0
+unbound config.status 1.16.2
 configured by $0, generated by GNU Autoconf 2.71,
   with options \\"\$ac_cs_config\\"
 
index a54f2c0..8dabd62 100644 (file)
@@ -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],[2])
 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=18
 LIBUNBOUND_AGE=1
 # 1.0.0 had 0:12:0
 # 1.0.1 had 0:13:0
@@ -102,6 +102,8 @@ 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
+# 1.16.2 had 9:18:1
 
 #   Current  -- the number of the binary API that we're implementing
 #   Revision -- which iteration of the implementation of the binary
@@ -907,7 +909,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"
@@ -1500,7 +1502,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
@@ -1517,7 +1519,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
index b929f90..baf8008 100644 (file)
@@ -679,7 +679,8 @@ load_msg(RES* ssl, sldns_buffer* buf, struct worker* worker)
        if(!go_on) 
                return 1; /* skip this one, not all references satisfied */
 
-       if(!dns_cache_store(&worker->env, &qinf, &rep, 0, 0, 0, NULL, flags)) {
+       if(!dns_cache_store(&worker->env, &qinf, &rep, 0, 0, 0, NULL, flags,
+               *worker->env.now)) {
                log_warn("error out of memory");
                return 0;
        }
@@ -850,7 +851,7 @@ int print_deleg_lookup(RES* ssl, struct worker* worker, uint8_t* nm,
        while(1) {
                dp = dns_cache_find_delegation(&worker->env, nm, nmlen, 
                        qinfo.qtype, qinfo.qclass, region, &msg, 
-                       *worker->env.now);
+                       *worker->env.now, 0, NULL, 0);
                if(!dp) {
                        return ssl_printf(ssl, "no delegation from "
                                "cache; goes to configured roots\n");
index 0e3923b..4ed5318 100644 (file)
@@ -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
index 675ef43..ec7a4d5 100644 (file)
@@ -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", 
index d08f18d..57c4282 100644 (file)
@@ -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;
index bf8c5d6..010c4dc 100644 (file)
@@ -459,7 +459,7 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo,
 
        dp = dns_cache_find_delegation(&worker->env, qinfo->qname, 
                qinfo->qname_len, qinfo->qtype, qinfo->qclass,
-               worker->scratchpad, &msg, timenow);
+               worker->scratchpad, &msg, timenow, 0, NULL, 0);
        if(!dp) { /* no delegation, need to reprime */
                return 0;
        }
@@ -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)
index d01b436..4b98b60 100644 (file)
@@ -652,7 +652,7 @@ handle_event_moddone(struct module_qstate* qstate, int id)
        if ( (!iq || !iq->started_no_cache_store) &&
                qstate->return_msg && qstate->return_msg->rep &&
                !dns_cache_store(qstate->env, &qstate->qinfo, qstate->return_msg->rep,
-               0, 0, 0, NULL, qstate->query_flags))
+               0, 0, 0, NULL, qstate->query_flags, qstate->qstarttime))
                log_err("out of memory");
 
        /* do nothing */
@@ -991,7 +991,7 @@ dns64_inform_super(struct module_qstate* qstate, int id,
        /* Store the generated response in cache. */
        if ( (!super_dq || !super_dq->started_no_cache_store) &&
                !dns_cache_store(super->env, &super->qinfo, super->return_msg->rep,
-               0, 0, 0, NULL, super->query_flags))
+               0, 0, 0, NULL, super->query_flags, qstate->qstarttime))
                log_err("out of memory");
 }
 
index 53a1371..9d3523a 100644 (file)
@@ -1,9 +1,115 @@
 7 February 2022: Wouter
        - Fix that TCP interface does not use TLS when TLS is also configured.
 
+1 August 2022: Wouter
+       - Fix the novel ghost domain issues CVE-2022-30698 and CVE-2022-30699.
+       - Tests for ghost domain fixes.
+
+19 July 2022: George
+       - Update documentation for 'outbound-msg-retry:'.
+
+19 July 2022: Wouter
+       - Merge #718: Introduce infra-cache-max-rtt option to config max
+         retransmit timeout.
+
+15 July 2022: Wouter
+       - Merge PR 714: Avoid treat normal hosts as unresponsive servers.
+         And fixup the lock code.
+       - iana portlist update.
+
+12 July 2022: George
+       - For windows crosscompile, fix setting the IPV6_MTU socket option
+         equivalent (IPV6_USER_MTU); allows cross compiling with latest
+         cross-compiler versions.
+
+12 July 2022: Wouter
+       - Fix dname count in sldns parse type descriptor for SVCB and HTTPS.
+
+11 July 2022: Wouter
+       - Fix verbose EDE error printout.
+
+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. This became 1.16.1 on 11 July 2022.
+         The code repo continues with version 1.16.2 under development.
+
+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
index ea93afd..a6377d8 100644 (file)
@@ -1,4 +1,4 @@
-README for Unbound 1.16.0
+README for Unbound 1.16.2
 Copyright 2007 NLnet Labs
 http://unbound.net
 
index ad4d08c..ff0ebd7 100644 (file)
@@ -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.2.
 #
 # this is a comment.
 
@@ -168,7 +168,8 @@ server:
        # perform connect for UDP sockets to mitigate ICMP side channel.
        # udp-connect: yes
 
-       # The number of retries when a non-positive response is received.
+       # The number of retries, per upstream nameserver in a delegation, when
+       # a throwaway response (also timeouts) is received.
        # outbound-msg-retry: 5
 
        # msec for waiting for an unknown server to reply.  Increase if you
@@ -202,6 +203,9 @@ server:
        # minimum wait time for responses, increase if uplink is long. In msec.
        # infra-cache-min-rtt: 50
 
+       # maximum wait time for responses. In msec.
+       # infra-cache-max-rtt: 120000
+
        # enable to make server probe down hosts more frequently.
        # infra-keep-probing: no
 
@@ -1045,8 +1049,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
index b1be90c..543e628 100644 (file)
@@ -1,4 +1,4 @@
-.TH "libunbound" "3" "Jun  2, 2022" "NLnet Labs" "unbound 1.16.0"
+.TH "libunbound" "3" "Aug  1, 2022" "NLnet Labs" "unbound 1.16.2"
 .\"
 .\" 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.2 functions.
 .SH "SYNOPSIS"
 .B #include <unbound.h>
 .LP
index 4da37b1..7fc3168 100644 (file)
@@ -1,4 +1,4 @@
-.TH "unbound-anchor" "8" "Jun  2, 2022" "NLnet Labs" "unbound 1.16.0"
+.TH "unbound-anchor" "8" "Aug  1, 2022" "NLnet Labs" "unbound 1.16.2"
 .\"
 .\" unbound-anchor.8 -- unbound anchor maintenance utility manual
 .\"
index 4c607a2..628f841 100644 (file)
@@ -1,4 +1,4 @@
-.TH "unbound-checkconf" "8" "Jun  2, 2022" "NLnet Labs" "unbound 1.16.0"
+.TH "unbound-checkconf" "8" "Aug  1, 2022" "NLnet Labs" "unbound 1.16.2"
 .\"
 .\" unbound-checkconf.8 -- unbound configuration checker manual
 .\"
index 3ef1d65..d18a407 100644 (file)
@@ -1,4 +1,4 @@
-.TH "unbound-control" "8" "Jun  2, 2022" "NLnet Labs" "unbound 1.16.0"
+.TH "unbound-control" "8" "Aug  1, 2022" "NLnet Labs" "unbound 1.16.2"
 .\"
 .\" 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.
index a30d1df..d3b701f 100644 (file)
@@ -1,4 +1,4 @@
-.TH "unbound\-host" "1" "Jun  2, 2022" "NLnet Labs" "unbound 1.16.0"
+.TH "unbound\-host" "1" "Aug  1, 2022" "NLnet Labs" "unbound 1.16.2"
 .\"
 .\" unbound-host.1 -- unbound DNS lookup utility
 .\"
index e349272..73b9e4b 100644 (file)
@@ -1,4 +1,4 @@
-.TH "unbound" "8" "Jun  2, 2022" "NLnet Labs" "unbound 1.16.0"
+.TH "unbound" "8" "Aug  1, 2022" "NLnet Labs" "unbound 1.16.2"
 .\"
 .\" 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.2.
 .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.
index ca2a20e..5642bdc 100644 (file)
@@ -1,4 +1,4 @@
-.TH "unbound.conf" "5" "Jun  2, 2022" "NLnet Labs" "unbound 1.16.0"
+.TH "unbound.conf" "5" "Aug  1, 2022" "NLnet Labs" "unbound 1.16.2"
 .\"
 .\" unbound.conf.5 -- unbound.conf manual
 .\"
@@ -359,6 +359,10 @@ Lower limit for dynamic retransmit timeout calculation in infrastructure
 cache. Default is 50 milliseconds. Increase this value if using forwarders
 needing more time to do recursive name resolution.
 .TP
+.B infra\-cache\-max\-rtt: \fI<msec>
+Upper limit for dynamic retransmit timeout calculation in infrastructure
+cache. Default is 2 minutes.
+.TP
 .B infra\-keep\-probing: \fI<yes or no>
 If enabled the server keeps probing hosts that are down, in the one probe
 at a time regime.  Default is no.  Hosts that are down, eg. they did
@@ -1713,9 +1717,12 @@ set ip\-ratelimit to a suspicious rate to aggressively limit unusually high
 traffic.  Default is off.
 .TP 5
 .B outbound\-msg\-retry: \fI<number>
-The number of retries Unbound will do in case of a non positive response is
-received. If a forward nameserver is used, this is the number of retries per
-forward nameserver in case of throwaway response.
+The number of retries, per upstream nameserver in a delegation, that Unbound
+will attempt in case a throwaway response is received.
+No response (timeout) contributes to the retry counter.
+If a forward/stub zone is used, this is the number of retries per nameserver in
+the zone.
+Default is 5.
 .TP 5
 .B fast\-server\-permil: \fI<number>
 Specify how many times out of 1000 to pick from the set of fastest servers.
@@ -2022,8 +2029,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
@@ -2637,8 +2644,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.
index 25190b0..7544611 100644 (file)
@@ -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) {
index c877692..f0bcaad 100644 (file)
@@ -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,
index 577f711..19549d4 100644 (file)
@@ -456,7 +456,7 @@ ipsecmod_handle_query(struct module_qstate* qstate,
        /* Store A/AAAA in cache. */
        if(!dns_cache_store(qstate->env, &qstate->qinfo,
                qstate->return_msg->rep, 0, qstate->prefetch_leeway,
-               0, qstate->region, qstate->query_flags)) {
+               0, qstate->region, qstate->query_flags, qstate->qstarttime)) {
                log_err("ipsecmod: out of memory caching record");
        }
        qstate->ext_state[id] = module_finished;
index 80148e8..4bffa1b 100644 (file)
@@ -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,
index 998b98c..62c8edc 100644 (file)
@@ -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);
index f3bea46..3e13e59 100644 (file)
@@ -70,8 +70,6 @@
 
 /** time when nameserver glue is said to be 'recent' */
 #define SUSPICION_RECENT_EXPIRY 86400
-/** penalty to validation failed blacklisted IPs */
-#define BLACKLIST_PENALTY (USEFUL_SERVER_TOP_TIMEOUT*4)
 
 /** fillup fetch policy array */
 static void
@@ -367,6 +365,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 +375,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 */
@@ -657,10 +659,10 @@ dns_copy_msg(struct dns_msg* from, struct regional* region)
 void
 iter_dns_store(struct module_env* env, struct query_info* msgqinf,
        struct reply_info* msgrep, int is_referral, time_t leeway, int pside,
-       struct regional* region, uint16_t flags)
+       struct regional* region, uint16_t flags, time_t qstarttime)
 {
        if(!dns_cache_store(env, msgqinf, msgrep, is_referral, leeway,
-               pside, region, flags))
+               pside, region, flags, qstarttime))
                log_err("out of memory: cannot store data in cache");
 }
 
index c0e5181..8583fde 100644 (file)
@@ -132,6 +132,7 @@ struct dns_msg* dns_copy_msg(struct dns_msg* from, struct regional* regional);
  *     can be prefetch-updates.
  * @param region: to copy modified (cache is better) rrs back to.
  * @param flags: with BIT_CD for dns64 AAAA translated queries.
+ * @param qstarttime: time of query start.
  * return void, because we are not interested in alloc errors,
  *     the iterator and validator can operate on the results in their
  *     scratch space (the qstate.region) and are not dependent on the cache.
@@ -140,7 +141,7 @@ struct dns_msg* dns_copy_msg(struct dns_msg* from, struct regional* regional);
  */
 void iter_dns_store(struct module_env* env, struct query_info* qinf,
        struct reply_info* rep, int is_referral, time_t leeway, int pside,
-       struct regional* region, uint16_t flags);
+       struct regional* region, uint16_t flags, time_t qstarttime);
 
 /**
  * Select randomly with n/m probability.
index 3cfb286..25e5cfe 100644 (file)
 
 /* in msec */
 int UNKNOWN_SERVER_NICENESS = 376;
+/* in msec */
+int USEFUL_SERVER_TOP_TIMEOUT = 120000;
+/* Equals USEFUL_SERVER_TOP_TIMEOUT*4 */
+int BLACKLIST_PENALTY = (120000*4);
 
 static void target_count_increase_nx(struct iter_qstate* iq, int num);
 
@@ -253,8 +257,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 */
@@ -370,7 +375,7 @@ error_response_cache(struct module_qstate* qstate, int id, int rcode)
                err.security = sec_status_indeterminate;
                verbose(VERB_ALGO, "store error response in message cache");
                iter_dns_store(qstate->env, &qstate->qinfo, &err, 0, 0, 0, NULL,
-                       qstate->query_flags);
+                       qstate->query_flags, qstate->qstarttime);
        }
        return error_response(qstate, id, rcode);
 }
@@ -678,15 +683,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 +705,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 +714,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 +809,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;
@@ -1477,7 +1489,8 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
                     iq->dp = dns_cache_find_delegation(qstate->env, delname, 
                        delnamelen, iq->qchase.qtype, iq->qchase.qclass, 
                        qstate->region, &iq->deleg_msg,
-                       *qstate->env->now+qstate->prefetch_leeway);
+                       *qstate->env->now+qstate->prefetch_leeway, 1,
+                       dpname, dpnamelen);
                else iq->dp = NULL;
 
                /* If the cache has returned nothing, then we have a 
@@ -1769,7 +1782,8 @@ generate_parentside_target_query(struct module_qstate* qstate,
                        subiq->dp = dns_cache_find_delegation(qstate->env, 
                                name, namelen, qtype, qclass, subq->region,
                                &subiq->deleg_msg,
-                               *qstate->env->now+subq->prefetch_leeway); 
+                               *qstate->env->now+subq->prefetch_leeway,
+                               1, NULL, 0);
                        /* if no dp, then it's from root, refetch unneeded */
                        if(subiq->dp) { 
                                subiq->dnssec_expected = iter_indicates_dnssec(
@@ -1832,7 +1846,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 +1865,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 +1898,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 +1913,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 +2025,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 +2046,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 +2178,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 +2257,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 +2559,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 +2648,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 +2669,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 +2681,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 +2720,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) {
@@ -2839,7 +2949,8 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
                                iq->qchase.qtype != iq->response->qinfo.qtype,
                                qstate->prefetch_leeway,
                                iq->dp&&iq->dp->has_parent_side_NS,
-                               qstate->region, qstate->query_flags);
+                               qstate->region, qstate->query_flags,
+                               qstate->qstarttime);
                /* close down outstanding requests to be discarded */
                outbound_list_clear(&iq->outlist);
                iq->num_current_queries = 0;
@@ -2928,7 +3039,8 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
                        /* Store the referral under the current query */
                        /* no prefetch-leeway, since its not the answer */
                        iter_dns_store(qstate->env, &iq->response->qinfo,
-                               iq->response->rep, 1, 0, 0, NULL, 0);
+                               iq->response->rep, 1, 0, 0, NULL, 0,
+                               qstate->qstarttime);
                        if(iq->store_parent_NS)
                                iter_store_parentside_NS(qstate->env, 
                                        iq->response->rep);
@@ -3042,7 +3154,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
                        iter_dns_store(qstate->env, &iq->response->qinfo,
                                iq->response->rep, 1, qstate->prefetch_leeway,
                                iq->dp&&iq->dp->has_parent_side_NS, NULL,
-                               qstate->query_flags);
+                               qstate->query_flags, qstate->qstarttime);
                /* set the current request's qname to the new value. */
                iq->qchase.qname = sname;
                iq->qchase.qname_len = snamelen;
@@ -3383,8 +3495,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);
+               }
        }
 }
 
@@ -3645,7 +3760,8 @@ processFinished(struct module_qstate* qstate, struct iter_qstate* iq,
                        iter_dns_store(qstate->env, &qstate->qinfo, 
                                iq->response->rep, 0, qstate->prefetch_leeway,
                                iq->dp&&iq->dp->has_parent_side_NS,
-                               qstate->region, qstate->query_flags);
+                               qstate->region, qstate->query_flags,
+                               qstate->qstarttime);
                }
        }
        qstate->return_rcode = LDNS_RCODE_NOERROR;
@@ -4002,8 +4118,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;
index 8b84052..b71b7fe 100644 (file)
@@ -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 */
@@ -91,15 +94,17 @@ struct rbtree_type;
 extern int UNKNOWN_SERVER_NICENESS;
 /** maximum timeout before a host is deemed unsuitable, in msec. 
  * After host_ttl this will be timed out and the host will be tried again. 
- * Equals RTT_MAX_TIMEOUT
- */
-#define USEFUL_SERVER_TOP_TIMEOUT      120000
+ * Equals RTT_MAX_TIMEOUT, and thus when RTT_MAX_TIMEOUT is overwritten by
+ * config infra_cache_max_rtt, it will be overwritten as well. */
+extern int USEFUL_SERVER_TOP_TIMEOUT;
+/** penalty to validation failed blacklisted IPs
+ * Equals USEFUL_SERVER_TOP_TIMEOUT*4, and thus when RTT_MAX_TIMEOUT is
+ * overwritten by config infra_cache_max_rtt, it will be overwritten as well. */
+extern int BLACKLIST_PENALTY;
 /** RTT band, within this amount from the best, servers are chosen randomly.
  * Chosen so that the UNKNOWN_SERVER_NICENESS falls within the band of a 
  * fast server, this causes server exploration as a side benefit. msec. */
 #define RTT_BAND 400
-/** Start value for blacklisting a host, 2*USEFUL_SERVER_TOP_TIMEOUT in sec */
-#define INFRA_BACKOFF_INITIAL 240
 
 /**
  * Global state for the iterator. 
@@ -217,6 +222,21 @@ enum iter_state {
        FINISHED_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.
  */
@@ -310,15 +330,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;
 
index ee85587..c779d18 100644 (file)
@@ -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 */
index 02fb621..b9e0b11 100644 (file)
@@ -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;
        }
 
@@ -8187,7 +8189,6 @@ auth_zone_verify_zonemd_key_with_ds(struct auth_zone* z,
        keystorage->rk.type = htons(LDNS_RR_TYPE_DNSKEY);
        keystorage->rk.rrset_class = htons(z->dclass);
        auth_zone_log(z->name, VERB_QUERY, "zonemd: verify zone DNSKEY with DS");
-       // @TODO add EDE here? we currently just pass NULL
        sec = val_verify_DNSKEY_with_DS(env, ve, keystorage, ds, sigalg,
                why_bogus, NULL, NULL);
        regional_free_all(env->scratch);
index f6c1145..6bca8d8 100644 (file)
  *     in a prefetch situation to be updated (without becoming sticky).
  * @param qrep: update rrsets here if cache is better
  * @param region: for qrep allocs.
+ * @param qstarttime: time when delegations were looked up, this is perhaps
+ *     earlier than the time in now. The time is used to determine if RRsets
+ *     of type NS have expired, so that they can only be updated using
+ *     lookups of delegation points that did not use them, since they had
+ *     expired then.
  */
 static void
 store_rrsets(struct module_env* env, struct reply_info* rep, time_t now,
        time_t leeway, int pside, struct reply_info* qrep,
-       struct regional* region)
+       struct regional* region, time_t qstarttime)
 {
        size_t i;
        /* see if rrset already exists in cache, if not insert it. */
@@ -81,8 +86,8 @@ store_rrsets(struct module_env* env, struct reply_info* rep, time_t now,
                rep->ref[i].id = rep->rrsets[i]->id;
                /* update ref if it was in the cache */
                switch(rrset_cache_update(env->rrset_cache, &rep->ref[i],
-                               env->alloc, now + ((ntohs(rep->ref[i].key->rk.type)==
-                               LDNS_RR_TYPE_NS && !pside)?0:leeway))) {
+                               env->alloc, ((ntohs(rep->ref[i].key->rk.type)==
+                               LDNS_RR_TYPE_NS && !pside)?qstarttime:now + leeway))) {
                case 0: /* ref unchanged, item inserted */
                        break;
                case 2: /* ref updated, cache is superior */
@@ -155,7 +160,8 @@ msg_del_servfail(struct module_env* env, struct query_info* qinfo,
 void 
 dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
        hashvalue_type hash, struct reply_info* rep, time_t leeway, int pside,
-       struct reply_info* qrep, uint32_t flags, struct regional* region)
+       struct reply_info* qrep, uint32_t flags, struct regional* region,
+       time_t qstarttime)
 {
        struct msgreply_entry* e;
        time_t ttl = rep->ttl;
@@ -170,7 +176,8 @@ dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
        /* there was a reply_info_sortref(rep) here but it seems to be
         * unnecessary, because the cache gets locked per rrset. */
        reply_info_set_ttls(rep, *env->now);
-       store_rrsets(env, rep, *env->now, leeway, pside, qrep, region);
+       store_rrsets(env, rep, *env->now, leeway, pside, qrep, region,
+               qstarttime);
        if(ttl == 0 && !(flags & DNSCACHE_STORE_ZEROTTL)) {
                /* we do not store the message, but we did store the RRs,
                 * which could be useful for delegation information */
@@ -194,10 +201,51 @@ dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
        slabhash_insert(env->msg_cache, hash, &e->entry, rep, env->alloc);
 }
 
+/** see if an rrset is expired above the qname, return upper qname. */
+static int
+rrset_expired_above(struct module_env* env, uint8_t** qname, size_t* qnamelen,
+       uint16_t searchtype, uint16_t qclass, time_t now, uint8_t* expiretop,
+       size_t expiretoplen)
+{
+       struct ub_packed_rrset_key *rrset;
+       uint8_t lablen;
+
+       while(*qnamelen > 0) {
+               /* look one label higher */
+               lablen = **qname;
+               *qname += lablen + 1;
+               *qnamelen -= lablen + 1;
+               if(*qnamelen <= 0)
+                       break;
+
+               /* looks up with a time of 0, to see expired entries */
+               if((rrset = rrset_cache_lookup(env->rrset_cache, *qname,
+                       *qnamelen, searchtype, qclass, 0, 0, 0))) {
+                       struct packed_rrset_data* data =
+                               (struct packed_rrset_data*)rrset->entry.data;
+                       if(now > data->ttl) {
+                               /* it is expired, this is not wanted */
+                               lock_rw_unlock(&rrset->entry.lock);
+                               log_nametypeclass(VERB_ALGO, "this rrset is expired", *qname, searchtype, qclass);
+                               return 1;
+                       }
+                       /* it is not expired, continue looking */
+                       lock_rw_unlock(&rrset->entry.lock);
+               }
+
+               /* do not look above the expiretop. */
+               if(expiretop && *qnamelen == expiretoplen &&
+                       query_dname_compare(*qname, expiretop)==0)
+                       break;
+       }
+       return 0;
+}
+
 /** find closest NS or DNAME and returns the rrset (locked) */
 static struct ub_packed_rrset_key*
 find_closest_of_type(struct module_env* env, uint8_t* qname, size_t qnamelen, 
-       uint16_t qclass, time_t now, uint16_t searchtype, int stripfront)
+       uint16_t qclass, time_t now, uint16_t searchtype, int stripfront,
+       int noexpiredabove, uint8_t* expiretop, size_t expiretoplen)
 {
        struct ub_packed_rrset_key *rrset;
        uint8_t lablen;
@@ -212,8 +260,40 @@ find_closest_of_type(struct module_env* env, uint8_t* qname, size_t qnamelen,
        /* snip off front part of qname until the type is found */
        while(qnamelen > 0) {
                if((rrset = rrset_cache_lookup(env->rrset_cache, qname, 
-                       qnamelen, searchtype, qclass, 0, now, 0)))
-                       return rrset;
+                       qnamelen, searchtype, qclass, 0, now, 0))) {
+                       uint8_t* origqname = qname;
+                       size_t origqnamelen = qnamelen;
+                       if(!noexpiredabove)
+                               return rrset;
+                       /* if expiretop set, do not look above it, but
+                        * qname is equal, so the just found result is also
+                        * the nonexpired above part. */
+                       if(expiretop && qnamelen == expiretoplen &&
+                               query_dname_compare(qname, expiretop)==0)
+                               return rrset;
+                       /* check for expiry, but we have to let go of the rrset
+                        * for the lock ordering */
+                       lock_rw_unlock(&rrset->entry.lock);
+                       /* the expired_above function always takes off one
+                        * label (if qnamelen>0) and returns the final qname
+                        * where it searched, so we can continue from there
+                        * turning the O N*N search into O N. */
+                       if(!rrset_expired_above(env, &qname, &qnamelen,
+                               searchtype, qclass, now, expiretop,
+                               expiretoplen)) {
+                               /* we want to return rrset, but it may be
+                                * gone from cache, if so, just loop like
+                                * it was not in the cache in the first place.
+                                */
+                               if((rrset = rrset_cache_lookup(env->
+                                       rrset_cache, origqname, origqnamelen,
+                                       searchtype, qclass, 0, now, 0))) {
+                                       return rrset;
+                               }
+                       }
+                       log_nametypeclass(VERB_ALGO, "ignoring rrset because expired rrsets exist above it", origqname, searchtype, qclass);
+                       continue;
+               }
 
                /* snip off front label */
                lablen = *qname;
@@ -462,7 +542,8 @@ dns_msg_ansadd(struct dns_msg* msg, struct regional* region,
 struct delegpt* 
 dns_cache_find_delegation(struct module_env* env, uint8_t* qname, 
        size_t qnamelen, uint16_t qtype, uint16_t qclass, 
-       struct regional* region, struct dns_msg** msg, time_t now)
+       struct regional* region, struct dns_msg** msg, time_t now,
+       int noexpiredabove, uint8_t* expiretop, size_t expiretoplen)
 {
        /* try to find closest NS rrset */
        struct ub_packed_rrset_key* nskey;
@@ -470,7 +551,7 @@ dns_cache_find_delegation(struct module_env* env, uint8_t* qname,
        struct delegpt* dp;
 
        nskey = find_closest_of_type(env, qname, qnamelen, qclass, now,
-               LDNS_RR_TYPE_NS, 0);
+               LDNS_RR_TYPE_NS, 0, noexpiredabove, expiretop, expiretoplen);
        if(!nskey) /* hope the caller has hints to prime or something */
                return NULL;
        nsdata = (struct packed_rrset_data*)nskey->entry.data;
@@ -840,7 +921,7 @@ dns_cache_lookup(struct module_env* env,
         * consistent with the DNAME */
        if(!no_partial &&
                (rrset=find_closest_of_type(env, qname, qnamelen, qclass, now,
-               LDNS_RR_TYPE_DNAME, 1))) {
+               LDNS_RR_TYPE_DNAME, 1, 0, NULL, 0))) {
                /* synthesize a DNAME+CNAME message based on this */
                enum sec_status sec_status = sec_status_unchecked;
                struct dns_msg* msg = synth_dname_msg(rrset, region, now, &k,
@@ -973,7 +1054,7 @@ dns_cache_lookup(struct module_env* env,
 int
 dns_cache_store(struct module_env* env, struct query_info* msgqinf,
         struct reply_info* msgrep, int is_referral, time_t leeway, int pside,
-       struct regional* region, uint32_t flags)
+       struct regional* region, uint32_t flags, time_t qstarttime)
 {
        struct reply_info* rep = NULL;
        /* alloc, malloc properly (not in region, like msg is) */
@@ -996,9 +1077,9 @@ dns_cache_store(struct module_env* env, struct query_info* msgqinf,
                        /*ignore ret: it was in the cache, ref updated */
                        /* no leeway for typeNS */
                        (void)rrset_cache_update(env->rrset_cache, &ref, 
-                               env->alloc, *env->now + 
+                               env->alloc,
                                ((ntohs(ref.key->rk.type)==LDNS_RR_TYPE_NS
-                                && !pside) ? 0:leeway));
+                                && !pside) ? qstarttime:*env->now + leeway));
                }
                free(rep);
                return 1;
@@ -1020,7 +1101,7 @@ dns_cache_store(struct module_env* env, struct query_info* msgqinf,
                rep->flags &= ~(BIT_AA | BIT_CD);
                h = query_info_hash(&qinf, (uint16_t)flags);
                dns_cache_store_msg(env, &qinf, h, rep, leeway, pside, msgrep,
-                       flags, region);
+                       flags, region, qstarttime);
                /* qname is used inside query_info_entrysetup, and set to 
                 * NULL. If it has not been used, free it. free(0) is safe. */
                free(qinf.qname);
index bece837..147f992 100644 (file)
@@ -88,11 +88,13 @@ struct dns_msg {
  * @param flags: flags with BIT_CD for AAAA queries in dns64 translation.
  *   The higher 16 bits are used internally to customize the cache policy.
  *   (See DNSCACHE_STORE_xxx flags).
+ * @param qstarttime: time when the query was started, and thus when the
+ *     delegations were looked up.
  * @return 0 on alloc error (out of memory).
  */
 int dns_cache_store(struct module_env* env, struct query_info* qinf,
         struct reply_info* rep, int is_referral, time_t leeway, int pside,
-       struct regional* region, uint32_t flags);
+       struct regional* region, uint32_t flags, time_t qstarttime);
 
 /**
  * Store message in the cache. Stores in message cache and rrset cache.
@@ -112,11 +114,14 @@ int dns_cache_store(struct module_env* env, struct query_info* qinf,
  *     can be updated to full TTL even in prefetch situations.
  * @param qrep: message that can be altered with better rrs from cache.
  * @param flags: customization flags for the cache policy.
+ * @param qstarttime: time when the query was started, and thus when the
+ *     delegations were looked up.
  * @param region: to allocate into for qmsg.
  */
 void dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
        hashvalue_type hash, struct reply_info* rep, time_t leeway, int pside,
-       struct reply_info* qrep, uint32_t flags, struct regional* region);
+       struct reply_info* qrep, uint32_t flags, struct regional* region,
+       time_t qstarttime);
 
 /**
  * Find a delegation from the cache.
@@ -129,11 +134,18 @@ void dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
  * @param msg: if not NULL, delegation message is returned here, synthesized
  *     from the cache.
  * @param timenow: the time now, for checking if TTL on cache entries is OK.
+ * @param noexpiredabove: if set, no expired NS rrsets above the one found
+ *     are tolerated. It only returns delegations where the delegations above
+ *     it are valid.
+ * @param expiretop: if not NULL, name where check for expiry ends for
+ *     noexpiredabove.
+ * @param expiretoplen: length of expiretop dname.
  * @return new delegation or NULL on error or if not found in cache.
  */
 struct delegpt* dns_cache_find_delegation(struct module_env* env, 
        uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, 
-       struct regional* region, struct dns_msg** msg, time_t timenow);
+       struct regional* region, struct dns_msg** msg, time_t timenow,
+       int noexpiredabove, uint8_t* expiretop, size_t expiretoplen);
 
 /**
  * generate dns_msg from cached message
index 252e1e2..0461c81 100644 (file)
@@ -721,13 +721,13 @@ infra_get_lame_rtt(struct infra_cache* infra,
                        else    *rtt = USEFUL_SERVER_TOP_TIMEOUT-1000;
                }
        }
+       /* expired entry */
        if(timenow > host->ttl) {
-               /* expired entry */
+
                /* see if this can be a re-probe of an unresponsive server */
                /* minus 1000 because that is outside of the RTTBAND, so
                 * blacklisted servers stay blacklisted if this is chosen */
-               if(host->rtt.rto >= USEFUL_SERVER_TOP_TIMEOUT ||
-                       infra->infra_keep_probing) {
+               if(host->rtt.rto >= USEFUL_SERVER_TOP_TIMEOUT) {
                        lock_rw_unlock(&e->lock);
                        *rtt = USEFUL_SERVER_TOP_TIMEOUT-1000;
                        *lame = 0;
index 03153bd..1c7c177 100644 (file)
@@ -490,6 +490,7 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr,
                        return -1;
                }
 # elif defined(IPV6_MTU)
+#   ifndef USE_WINSOCK
                /*
                 * On Linux, to send no larger than 1280, the PMTUD is
                 * disabled by default for datagrams anyway, so we set
@@ -497,13 +498,27 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr,
                 */
                if (setsockopt(s, IPPROTO_IPV6, IPV6_MTU,
                        (void*)&mtu, (socklen_t)sizeof(mtu)) < 0) {
-                       log_err("setsockopt(..., IPV6_MTU, ...) failed: %s", 
+                       log_err("setsockopt(..., IPV6_MTU, ...) failed: %s",
                                sock_strerror(errno));
                        sock_close(s);
                        *noproto = 0;
                        *inuse = 0;
                        return -1;
                }
+#   elif defined(IPV6_USER_MTU)
+               /* As later versions of the mingw crosscompiler define
+                * IPV6_MTU, do the same for windows but use IPV6_USER_MTU
+                * instead which is writable; IPV6_MTU is readonly there. */
+               if (setsockopt(s, IPPROTO_IPV6, IPV6_USER_MTU,
+                       (void*)&mtu, (socklen_t)sizeof(mtu)) < 0) {
+                       log_err("setsockopt(..., IPV6_USER_MTU, ...) failed: %s",
+                               wsa_strerror(WSAGetLastError()));
+                       sock_close(s);
+                       *noproto = 0;
+                       *inuse = 0;
+                       return -1;
+               }
+#   endif /* USE_WINSOCK */
 # endif /* IPv6 MTU */
 # if defined(IPV6_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
 #  if defined(IP_PMTUDISC_OMIT)
index fbaa966..30bcf7c 100644 (file)
@@ -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;
@@ -953,6 +954,7 @@ mesh_state_create(struct module_env* env, struct query_info* qinfo,
        mstate->s.no_cache_store = 0;
        mstate->s.need_refetch = 0;
        mstate->s.was_ratelimited = 0;
+       mstate->s.qstarttime = *env->now;
 
        /* init modules */
        for(i=0; i<env->mesh->mods.num; i++) {
index ec37a4a..3f479a3 100644 (file)
@@ -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. */
index 4c5b96f..c383b8f 100644 (file)
@@ -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;
index 491c8f5..8ea0846 100644 (file)
@@ -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;
index fe5c8e1..322eff0 100644 (file)
@@ -381,9 +381,9 @@ static sldns_rr_descriptor rdata_field_descriptors[] = {
        /* 63 */
        {LDNS_RR_TYPE_ZONEMD, "ZONEMD", 4, 4, type_zonemd_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
        /* 64 */
-       {LDNS_RR_TYPE_SVCB, "SVCB", 2, 2, type_svcb_wireformat, LDNS_RDF_TYPE_SVCPARAM, LDNS_RR_NO_COMPRESS, 0 },
+       {LDNS_RR_TYPE_SVCB, "SVCB", 2, 2, type_svcb_wireformat, LDNS_RDF_TYPE_SVCPARAM, LDNS_RR_NO_COMPRESS, 1 },
        /* 65 */
-       {LDNS_RR_TYPE_HTTPS, "HTTPS", 2, 2, type_svcb_wireformat, LDNS_RDF_TYPE_SVCPARAM, LDNS_RR_NO_COMPRESS, 0 },
+       {LDNS_RR_TYPE_HTTPS, "HTTPS", 2, 2, type_svcb_wireformat, LDNS_RDF_TYPE_SVCPARAM, LDNS_RR_NO_COMPRESS, 1 },
 {(enum sldns_enum_rr_type)0, "TYPE66", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
 {(enum sldns_enum_rr_type)0, "TYPE67", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
 {(enum sldns_enum_rr_type)0, "TYPE68", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
index d6fb289..74d1b62 100644 (file)
@@ -1071,7 +1071,7 @@ static int sldns_wire2str_svcparam_mandatory2str(char** s,
        assert(data_len > 0);
 
        if (data_len % sizeof(uint16_t))
-               return -1; // wireformat error, data_len must be multiple of shorts
+               return -1; /* wireformat error, data_len must be multiple of shorts */
        w += sldns_str_print(s, slen, "=");
        w += sldns_print_svcparamkey(s, slen, sldns_read_uint16(data));
        data += 2;
index 89543e4..d473702 100644 (file)
@@ -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 */
 }
 
index d7bd37a..cd9389a 100644 (file)
@@ -173,6 +173,7 @@ config_create(void)
        cfg->infra_cache_slabs = 4;
        cfg->infra_cache_numhosts = 10000;
        cfg->infra_cache_min_rtt = 50;
+       cfg->infra_cache_max_rtt = 120000;
        cfg->infra_keep_probing = 0;
        cfg->delay_close = 0;
        cfg->udp_connect = 1;
@@ -595,8 +596,14 @@ int config_set_option(struct config_file* cfg, const char* opt,
        else if(strcmp(opt, "cache-min-ttl:") == 0)
        { IS_NUMBER_OR_ZERO; cfg->min_ttl = atoi(val); MIN_TTL=(time_t)cfg->min_ttl;}
        else if(strcmp(opt, "infra-cache-min-rtt:") == 0) {
-           IS_NUMBER_OR_ZERO; cfg->infra_cache_min_rtt = atoi(val);
-           RTT_MIN_TIMEOUT=cfg->infra_cache_min_rtt;
+               IS_NUMBER_OR_ZERO; cfg->infra_cache_min_rtt = atoi(val);
+               RTT_MIN_TIMEOUT=cfg->infra_cache_min_rtt;
+       }
+       else if(strcmp(opt, "infra-cache-max-rtt:") == 0) {
+               IS_NUMBER_OR_ZERO; cfg->infra_cache_max_rtt = atoi(val);
+               RTT_MAX_TIMEOUT=cfg->infra_cache_max_rtt;
+               USEFUL_SERVER_TOP_TIMEOUT = RTT_MAX_TIMEOUT;
+               BLACKLIST_PENALTY = USEFUL_SERVER_TOP_TIMEOUT*4;
        }
        else S_YNO("infra-keep-probing:", infra_keep_probing)
        else S_NUMBER_OR_ZERO("infra-host-ttl:", host_ttl)
@@ -1026,6 +1033,7 @@ config_get_option(struct config_file* cfg, const char* opt,
        else O_DEC(opt, "infra-host-ttl", host_ttl)
        else O_DEC(opt, "infra-cache-slabs", infra_cache_slabs)
        else O_DEC(opt, "infra-cache-min-rtt", infra_cache_min_rtt)
+       else O_UNS(opt, "infra-cache-max-rtt", infra_cache_max_rtt)
        else O_YNO(opt, "infra-keep-probing", infra_keep_probing)
        else O_MEM(opt, "infra-cache-numhosts", infra_cache_numhosts)
        else O_UNS(opt, "delay-close", delay_close)
@@ -2222,11 +2230,14 @@ config_apply(struct config_file* config)
        SERVE_ORIGINAL_TTL = config->serve_original_ttl;
        MAX_NEG_TTL = (time_t)config->max_negative_ttl;
        RTT_MIN_TIMEOUT = config->infra_cache_min_rtt;
+       RTT_MAX_TIMEOUT = config->infra_cache_max_rtt;
        EDNS_ADVERTISED_SIZE = (uint16_t)config->edns_buffer_size;
        MINIMAL_RESPONSES = config->minimal_responses;
        RRSET_ROUNDROBIN = config->rrset_roundrobin;
        LOG_TAG_QUERYREPLY = config->log_tag_queryreply;
        UNKNOWN_SERVER_NICENESS = config->unknown_server_time_limit;
+       USEFUL_SERVER_TOP_TIMEOUT = RTT_MAX_TIMEOUT;
+       BLACKLIST_PENALTY = USEFUL_SERVER_TOP_TIMEOUT*4;
        log_set_time_asc(config->log_time_ascii);
        autr_permit_small_holddown = config->permit_small_holddown;
        stream_wait_max = config->stream_wait_size;
index 0b457e3..ed37252 100644 (file)
@@ -186,8 +186,10 @@ struct config_file {
        size_t infra_cache_slabs;
        /** max number of hosts in the infra cache */
        size_t infra_cache_numhosts;
-       /** min value for infra cache rtt */
+       /** min value for infra cache rtt (min retransmit timeout) */
        int infra_cache_min_rtt;
+       /** max value for infra cache rtt (max retransmit timeout) */
+       int infra_cache_max_rtt;
        /** keep probing hosts that are down */
        int infra_keep_probing;
        /** delay close of udp-timeouted ports, if 0 no delayclose. in msec */
index 2d59fbc..a46a74f 100644 (file)
@@ -302,6 +302,7 @@ infra-cache-slabs{COLON}    { YDVAR(1, VAR_INFRA_CACHE_SLABS) }
 infra-cache-numhosts{COLON}    { YDVAR(1, VAR_INFRA_CACHE_NUMHOSTS) }
 infra-cache-lame-size{COLON}   { YDVAR(1, VAR_INFRA_CACHE_LAME_SIZE) }
 infra-cache-min-rtt{COLON}     { YDVAR(1, VAR_INFRA_CACHE_MIN_RTT) }
+infra-cache-max-rtt{COLON}     { YDVAR(1, VAR_INFRA_CACHE_MAX_RTT) }
 infra-keep-probing{COLON}      { YDVAR(1, VAR_INFRA_KEEP_PROBING) }
 num-queries-per-thread{COLON}  { YDVAR(1, VAR_NUM_QUERIES_PER_THREAD) }
 jostle-timeout{COLON}          { YDVAR(1, VAR_JOSTLE_TIMEOUT) }
index c003f33..c235340 100644 (file)
@@ -120,7 +120,7 @@ extern struct config_parser_state* cfg_parser;
 %token VAR_STUB_FIRST VAR_MINIMAL_RESPONSES VAR_RRSET_ROUNDROBIN
 %token VAR_MAX_UDP_SIZE VAR_DELAY_CLOSE VAR_UDP_CONNECT
 %token VAR_UNBLOCK_LAN_ZONES VAR_INSECURE_LAN_ZONES
-%token VAR_INFRA_CACHE_MIN_RTT VAR_INFRA_KEEP_PROBING
+%token VAR_INFRA_CACHE_MIN_RTT VAR_INFRA_CACHE_MAX_RTT VAR_INFRA_KEEP_PROBING
 %token VAR_DNS64_PREFIX VAR_DNS64_SYNTHALL VAR_DNS64_IGNORE_AAAA
 %token VAR_DNSTAP VAR_DNSTAP_ENABLE VAR_DNSTAP_SOCKET_PATH VAR_DNSTAP_IP
 %token VAR_DNSTAP_TLS VAR_DNSTAP_TLS_SERVER_NAME VAR_DNSTAP_TLS_CERT_BUNDLE
@@ -267,7 +267,7 @@ content_server: server_num_threads | server_verbosity | server_port |
        server_so_reuseport | server_delay_close | server_udp_connect |
        server_unblock_lan_zones | server_insecure_lan_zones |
        server_dns64_prefix | server_dns64_synthall | server_dns64_ignore_aaaa |
-       server_infra_cache_min_rtt | server_harden_algo_downgrade |
+       server_infra_cache_min_rtt | server_infra_cache_max_rtt | server_harden_algo_downgrade |
        server_ip_transparent | server_ip_ratelimit | server_ratelimit |
        server_ip_dscp | server_infra_keep_probing |
        server_ip_ratelimit_slabs | server_ratelimit_slabs |
@@ -1659,6 +1659,15 @@ server_infra_cache_min_rtt: VAR_INFRA_CACHE_MIN_RTT STRING_ARG
                free($2);
        }
        ;
+server_infra_cache_max_rtt: VAR_INFRA_CACHE_MAX_RTT STRING_ARG
+       {
+               OUTYY(("P(server_infra_cache_max_rtt:%s)\n", $2));
+               if(atoi($2) == 0 && strcmp($2, "0") != 0)
+                       yyerror("number expected");
+               else cfg_parser->cfg->infra_cache_max_rtt = atoi($2);
+               free($2);
+       }
+       ;
 server_infra_keep_probing: VAR_INFRA_KEEP_PROBING STRING_ARG
        {
                OUTYY(("P(server_infra_keep_probing:%s)\n", $2));
index e3ee607..1e6ee97 100644 (file)
@@ -1015,7 +1015,7 @@ int edns_opt_list_append_ede(struct edns_option** list, struct regional* region,
        prevp = list;
        while(*prevp != NULL)
                prevp = &((*prevp)->next);
-       verbose(VERB_ALGO, "attached EDE code: %d with message: %s", code, txt);
+       verbose(VERB_ALGO, "attached EDE code: %d with message: %s", code, (txt?txt:"\"\""));
        *prevp = opt;
        return 1;
 }
index c7662dc..80a8144 100644 (file)
 3297,
 3298,
 3299,
+3301,
 3302,
 3303,
 3304,
 4789,
 4790,
 4791,
+4792,
 4800,
 4801,
 4802,
 5859,
 5863,
 5900,
+5903,
+5904,
+5905,
+5906,
+5907,
+5908,
+5909,
 5910,
 5911,
 5912,
 6965,
 6966,
 6969,
+6980,
 6997,
 6998,
 6999,
index 33068a7..013c65b 100644 (file)
@@ -665,6 +665,12 @@ struct module_qstate {
        int need_refetch;
        /** whether the query (or a subquery) was ratelimited */
        int was_ratelimited;
+       /** time when query was started. This is when the qstate is created.
+        * This is used so that type NS data cannot be overwritten by them
+        * expiring while the lookup is in progress, using data fetched from
+        * those servers. By comparing expiry time with qstarttime for type NS.
+        */
+       time_t qstarttime;
 
        /**
         * Attributes of clients that share the qstate that may affect IP-based
index 114920e..8153dbd 100644 (file)
@@ -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;
                                }
index 07e65ee..63ed8ea 100644 (file)
@@ -58,7 +58,7 @@ struct rtt_info {
 /** min retransmit timeout value, in milliseconds */
 extern int RTT_MIN_TIMEOUT;
 /** max retransmit timeout value, in milliseconds */
-#define RTT_MAX_TIMEOUT 120000
+extern int RTT_MAX_TIMEOUT;
 
 /**
  * Initialize RTT estimators.
index 7abf66f..7865167 100644 (file)
@@ -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;
index d5f16b1..5ab21e2 100644 (file)
@@ -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;
 }
index 18a7c9c..e2319ee 100644 (file)
@@ -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;
        }
@@ -489,7 +489,6 @@ int val_favorite_ds_algo(struct ub_packed_rrset_key* ds_rrset)
        return digest_algo;
 }
 
-// @TODO change the use of this function to _ede function in authzone.c:8111
 enum sec_status
 val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve,
        struct ub_packed_rrset_key* dnskey_rrset,
index 75f3220..1723afe 100644 (file)
@@ -2202,7 +2202,7 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq,
                if(!qstate->no_cache_store) {
                        if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo,
                                vq->orig_msg->rep, 0, qstate->prefetch_leeway, 0, NULL,
-                               qstate->query_flags)) {
+                               qstate->query_flags, qstate->qstarttime)) {
                                log_err("out of memory caching validator results");
                        }
                }
@@ -2211,7 +2211,7 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq,
                /* and this does not get prefetched, so no leeway */
                if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo,
                        vq->orig_msg->rep, 1, 0, 0, NULL,
-                       qstate->query_flags)) {
+                       qstate->query_flags, qstate->qstarttime)) {
                        log_err("out of memory caching validator results");
                }
        }
@@ -2493,7 +2493,8 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
                        /* If they aren't usable, then we treat it like 
                         * there was no DS. */
 
-                       // @TODO add EDE Unsupported DS Digest Type
+                       /* TODO add EDE Unsupported DS Digest Type; this needs
+                        * EDE to be added on non SERVFAIL answers. */
 
                        *ke = key_entry_create_null(qstate->region, 
                                qinfo->qname, qinfo->qname_len, qinfo->qclass,