./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
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);
}
/**
/* 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
#! /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>.
#
# 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=''
# 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]...
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
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.
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
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
# 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
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
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
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
-version=1.16.0
+version=1.16.2
date=`date +'%b %e, %Y'`
# 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
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\\"
# 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
# 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
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"
else
LIBS="$LIBS -lgdi32"
fi
- LIBS="$LIBS -lz"
+ AC_CHECK_LIB([z], [compress], [ LIBS="$LIBS -lz" ])
LIBS="$LIBS -l:libssp.a"
fi
fi
else
LIBS="$LIBS -lgdi32"
fi
- LIBS="$LIBS -lz"
+ AC_CHECK_LIB([z], [compress], [ LIBS="$LIBS -lz" ])
LIBS="$LIBS -l:libssp.a"
fi
fi
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;
}
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");
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
(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",
/* 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);
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;
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;
}
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)) {
* 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);
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)
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 */
/* 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");
}
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
-README for Unbound 1.16.0
+README for Unbound 1.16.2
Copyright 2007 NLnet Labs
http://unbound.net
#
# 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.
# 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
# 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
# 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
-.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
.\"
.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
-.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
.\"
-.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
.\"
-.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
.\"
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.
-.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
.\"
-.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
.\"
.\"
.SH "NAME"
.B unbound
-\- Unbound DNS validating resolver 1.16.0.
+\- Unbound DNS validating resolver 1.16.2.
.SH "SYNOPSIS"
.B unbound
.RB [ \-h ]
.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.
-.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
.\"
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
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.
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
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.
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;
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);
}
}
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;
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;
}
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,
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;
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;
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) {
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;
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));
/** 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,
/* 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;
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,
}
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;
}
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,
/**
* 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);
/** 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
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;
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 */
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");
}
* 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.
*/
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.
/* 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);
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 */
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);
}
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*));
+ }
}
}
{
target_count_create(iq);
if(iq->target_count)
- iq->target_count[1] += num;
+ iq->target_count[TARGET_COUNT_QUERIES] += num;
iq->dp_target_count++;
}
{
target_count_create(iq);
if(iq->target_count)
- iq->target_count[2] += num;
+ iq->target_count[TARGET_COUNT_NX] += num;
}
/**
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;
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
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(
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
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) {
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,
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)) {
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)) {
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);
}
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
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
* 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];
}
}
/* 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);
/* 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");
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),
/* 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) {
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;
/* 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);
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;
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);
+ }
}
}
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;
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;
/** 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 */
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.
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.
*/
/** 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;
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 */
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);
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;
}
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);
* 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. */
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 */
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;
/* 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 */
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;
/* 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;
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;
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;
* 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,
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) */
/*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;
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);
* @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.
* 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.
* @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
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;
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
*/
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)
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;
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++) {
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
" 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;
}
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;
portcomm_loweruse(outnet, pend->pc);
return 0;
}
+ outnet->num_udp_outgoing++;
/* system calls to set timeout after sending UDP to make roundtrip
smaller. */
/** 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;
{
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;
com = 0;
quoted = 0;
prev_c = 0;
+ only_blank = 1; /* Assume we got only <blank> until now */
t = token;
if (del[0] == '"') {
quoted = 1;
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 {
/* 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++;
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;
/* 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 },
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;
/* 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);
config_delete(cfg);
#else
(void)cfgfile;
+ (void)quiet;
#endif /* HAVE_SHMGET */
}
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;
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)
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)
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;
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 */
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) }
%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
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 |
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));
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;
}
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,
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
(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 {
/* 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;
}
/** 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.
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)
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;
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;
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:
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
* @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)
{
}
#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;
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,
*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);
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;
}
}
/* 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;
}
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,
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");
}
}
/* 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");
}
}
/* 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,