diff options
author | Florian Obser <florian@cvs.openbsd.org> | 2018-02-07 00:24:34 +0000 |
---|---|---|
committer | Florian Obser <florian@cvs.openbsd.org> | 2018-02-07 00:24:34 +0000 |
commit | 57b80dd8cd9a6b27ad0b5a1e837d4d892df5d860 (patch) | |
tree | 915d90c97934e2a63d97cb28ecef928dd0cf74c7 | |
parent | 12e51d6e355308d9ca1ad65f3ef8be112f9e4d8c (diff) |
update to unbound 1.6.8, testing millert, OK sthen
81 files changed, 3743 insertions, 318 deletions
diff --git a/usr.sbin/unbound/Makefile.in b/usr.sbin/unbound/Makefile.in index f45e38cb27d..088e4722692 100644 --- a/usr.sbin/unbound/Makefile.in +++ b/usr.sbin/unbound/Makefile.in @@ -137,8 +137,7 @@ slabhash.lo timehist.lo tube.lo winsock_event.lo autotrust.lo val_anchor.lo \ validator.lo val_kcache.lo val_kentry.lo val_neg.lo val_nsec3.lo val_nsec.lo \ val_secalgo.lo val_sigcrypt.lo val_utils.lo dns64.lo cachedb.lo authzone.lo\ $(SUBNET_OBJ) $(PYTHONMOD_OBJ) $(CHECKLOCK_OBJ) $(DNSTAP_OBJ) $(DNSCRYPT_OBJ) \ -$(IPSECMOD_OBJ) -COMMON_OBJ_WITHOUT_NETCALL+=respip.lo +$(IPSECMOD_OBJ) respip.lo COMMON_OBJ_WITHOUT_UB_EVENT=$(COMMON_OBJ_WITHOUT_NETCALL) netevent.lo listen_dnsport.lo \ outside_network.lo COMMON_OBJ=$(COMMON_OBJ_WITHOUT_UB_EVENT) ub_event.lo @@ -534,6 +533,8 @@ install-all: all $(PYTHONMOD_INSTALL) $(PYUNBOUND_INSTALL) $(UNBOUND_EVENT_INSTA $(INSTALL) -m 755 -d $(DESTDIR)$(mandir)/man8 $(INSTALL) -m 755 -d $(DESTDIR)$(mandir)/man5 $(INSTALL) -m 755 -d $(DESTDIR)$(mandir)/man1 + $(INSTALL) -m 755 -d $(DESTDIR)$(libdir)/pkgconfig + $(INSTALL) -m 644 contrib/libunbound.pc $(DESTDIR)$(libdir)/pkgconfig $(LIBTOOL) --mode=install cp -f unbound$(EXEEXT) $(DESTDIR)$(sbindir)/unbound$(EXEEXT) $(LIBTOOL) --mode=install cp -f unbound-checkconf$(EXEEXT) $(DESTDIR)$(sbindir)/unbound-checkconf$(EXEEXT) $(LIBTOOL) --mode=install cp -f unbound-control$(EXEEXT) $(DESTDIR)$(sbindir)/unbound-control$(EXEEXT) @@ -824,9 +825,13 @@ shm_main.lo shm_main.o: $(srcdir)/util/shm_side/shm_main.c config.h $(srcdir)/ut $(srcdir)/util/fptr_wlist.h $(srcdir)/util/tube.h authzone.lo authzone.o: $(srcdir)/services/authzone.c config.h $(srcdir)/services/authzone.h \ $(srcdir)/util/rbtree.h $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/data/dname.h \ - $(srcdir)/util/storage/lruhash.h $(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h \ - $(srcdir)/util/regional.h $(srcdir)/util/net_help.h $(srcdir)/util/config_file.h $(srcdir)/services/cache/dns.h \ - $(srcdir)/sldns/rrdef.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/sbuffer.h $(srcdir)/sldns/str2wire.h \ + $(srcdir)/util/storage/lruhash.h $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h \ + $(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgencode.h \ + $(srcdir)/util/regional.h $(srcdir)/util/net_help.h $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \ + $(srcdir)/dnscrypt/cert.h $(srcdir)/util/config_file.h \ + $(srcdir)/util/module.h $(srcdir)/util/random.h $(srcdir)/services/cache/dns.h \ + $(srcdir)/services/outside_network.h \ + $(srcdir)/services/listen_dnsport.h $(srcdir)/sldns/sbuffer.h $(srcdir)/sldns/str2wire.h \ $(srcdir)/sldns/wire2str.h $(srcdir)/sldns/parseutil.h $(srcdir)/validator/val_nsec3.h \ $(srcdir)/validator/val_secalgo.h fptr_wlist.lo fptr_wlist.o: $(srcdir)/util/fptr_wlist.c config.h $(srcdir)/util/fptr_wlist.h \ @@ -1314,8 +1319,8 @@ unbound-checkconf.lo unbound-checkconf.o: $(srcdir)/smallapp/unbound-checkconf.c $(srcdir)/iterator/iterator.h $(srcdir)/services/outbound_list.h $(srcdir)/iterator/iter_fwd.h \ $(srcdir)/util/rbtree.h $(srcdir)/iterator/iter_hints.h $(srcdir)/util/storage/dnstree.h \ $(srcdir)/validator/validator.h $(srcdir)/validator/val_utils.h $(srcdir)/services/localzone.h \ - $(srcdir)/services/view.h $(srcdir)/respip/respip.h $(srcdir)/sldns/sbuffer.h $(PYTHONMOD_HEADER) \ - $(srcdir)/edns-subnet/subnet-whitelist.h + $(srcdir)/services/view.h $(srcdir)/services/authzone.h $(srcdir)/respip/respip.h $(srcdir)/sldns/sbuffer.h \ + $(PYTHONMOD_HEADER) $(srcdir)/edns-subnet/subnet-whitelist.h worker_cb.lo worker_cb.o: $(srcdir)/smallapp/worker_cb.c config.h $(srcdir)/libunbound/context.h \ $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/alloc.h $(srcdir)/util/rbtree.h $(srcdir)/services/modstack.h \ $(srcdir)/libunbound/unbound.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h \ diff --git a/usr.sbin/unbound/aclocal.m4 b/usr.sbin/unbound/aclocal.m4 index d1c7555a8d1..002c4723b6b 100644 --- a/usr.sbin/unbound/aclocal.m4 +++ b/usr.sbin/unbound/aclocal.m4 @@ -1,6 +1,6 @@ -# generated automatically by aclocal 1.15 -*- Autoconf -*- +# generated automatically by aclocal 1.15.1 -*- Autoconf -*- -# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# Copyright (C) 1996-2017 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -9390,7 +9390,7 @@ AS_IF([test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"], # AM_CONDITIONAL -*- Autoconf -*- -# Copyright (C) 1997-2014 Free Software Foundation, Inc. +# Copyright (C) 1997-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -9421,7 +9421,7 @@ AC_CONFIG_COMMANDS_PRE( Usually this means the macro was only invoked conditionally.]]) fi])]) -# Copyright (C) 2006-2014 Free Software Foundation, Inc. +# Copyright (C) 2006-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, diff --git a/usr.sbin/unbound/cachedb/cachedb.c b/usr.sbin/unbound/cachedb/cachedb.c index f5f6937eeee..d07d76973fd 100644 --- a/usr.sbin/unbound/cachedb/cachedb.c +++ b/usr.sbin/unbound/cachedb/cachedb.c @@ -347,6 +347,13 @@ prep_data(struct module_qstate* qstate, struct sldns_buffer* buf) if(!qstate->return_msg || !qstate->return_msg->rep) return 0; + /* We don't store the reply if its TTL is 0 unless serve-expired is + * enabled. Such a reply won't be reusable and simply be a waste for + * the backend. It's also compatible with the default behavior of + * dns_cache_store_msg(). */ + if(qstate->return_msg->rep->ttl == 0 && + !qstate->env->cfg->serve_expired) + return 0; if(verbosity >= VERB_ALGO) log_dns_msg("cachedb encoding", &qstate->return_msg->qinfo, qstate->return_msg->rep); @@ -387,32 +394,37 @@ good_expiry_and_qinfo(struct module_qstate* qstate, struct sldns_buffer* buf) &expiry, sizeof(expiry)); expiry = be64toh(expiry); - if((time_t)expiry < *qstate->env->now) + if((time_t)expiry < *qstate->env->now && + !qstate->env->cfg->serve_expired) return 0; return 1; } +/* Adjust the TTL of the given RRset by 'subtract'. If 'subtract' is + * negative, set the TTL to 0. */ static void packed_rrset_ttl_subtract(struct packed_rrset_data* data, time_t subtract) { size_t i; size_t total = data->count + data->rrsig_count; - if(data->ttl > subtract) + if(subtract >= 0 && data->ttl > subtract) data->ttl -= subtract; else data->ttl = 0; for(i=0; i<total; i++) { - if(data->rr_ttl[i] > subtract) + if(subtract >= 0 && data->rr_ttl[i] > subtract) data->rr_ttl[i] -= subtract; else data->rr_ttl[i] = 0; } } +/* Adjust the TTL of a DNS message and its RRs by 'adjust'. If 'adjust' is + * negative, set the TTLs to 0. */ static void adjust_msg_ttl(struct dns_msg* msg, time_t adjust) { size_t i; - if(msg->rep->ttl > adjust) + if(adjust >= 0 && msg->rep->ttl > adjust) msg->rep->ttl -= adjust; else msg->rep->ttl = 0; msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl); @@ -476,10 +488,26 @@ parse_data(struct module_qstate* qstate, struct sldns_buffer* buf) adjust = *qstate->env->now - (time_t)timestamp; if(qstate->return_msg->rep->ttl < adjust) { verbose(VERB_ALGO, "cachedb msg expired"); - return 0; /* message expired */ + /* If serve-expired is enabled, we still use an expired message + * setting the TTL to 0. */ + if(qstate->env->cfg->serve_expired) + adjust = -1; + else + return 0; /* message expired */ } verbose(VERB_ALGO, "cachedb msg adjusted down by %d", (int)adjust); adjust_msg_ttl(qstate->return_msg, adjust); + + /* Similar to the unbound worker, if serve-expired is enabled and + * the msg would be considered to be expired, mark the state so a + * refetch will be scheduled. The comparison between 'expiry' and + * 'now' should be redundant given how these values were calculated, + * but we check it just in case as does good_expiry_and_qinfo(). */ + if(qstate->env->cfg->serve_expired && + (adjust == -1 || (time_t)expiry < *qstate->env->now)) { + qstate->need_refetch = 1; + } + return 1; } @@ -563,11 +591,15 @@ cachedb_intcache_lookup(struct module_qstate* qstate) static void cachedb_intcache_store(struct module_qstate* qstate) { + uint32_t store_flags = qstate->query_flags; + + if(qstate->env->cfg->serve_expired) + store_flags |= DNSCACHE_STORE_ZEROTTL; if(!qstate->return_msg) return; (void)dns_cache_store(qstate->env, &qstate->qinfo, qstate->return_msg->rep, 0, qstate->prefetch_leeway, 0, - qstate->region, qstate->query_flags); + qstate->region, store_flags); } /** diff --git a/usr.sbin/unbound/config.h.in b/usr.sbin/unbound/config.h.in index 16a7b0281a1..90038a88fac 100644 --- a/usr.sbin/unbound/config.h.in +++ b/usr.sbin/unbound/config.h.in @@ -646,6 +646,9 @@ /* define if (v)snprintf does not return length needed, (but length used) */ #undef SNPRINTF_RET_BROKEN +/* Define to 1 if libsodium supports sodium_set_misuse_handler */ +#undef SODIUM_MISUSE_HANDLER + /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS diff --git a/usr.sbin/unbound/configure b/usr.sbin/unbound/configure index fff573752d4..9cccf64e2b0 100644 --- a/usr.sbin/unbound/configure +++ b/usr.sbin/unbound/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for unbound 1.6.6. +# Generated by GNU Autoconf 2.69 for unbound 1.6.8. # # Report bugs to <unbound-bugs@nlnetlabs.nl>. # @@ -590,8 +590,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='unbound' PACKAGE_TARNAME='unbound' -PACKAGE_VERSION='1.6.6' -PACKAGE_STRING='unbound 1.6.6' +PACKAGE_VERSION='1.6.8' +PACKAGE_STRING='unbound 1.6.8' PACKAGE_BUGREPORT='unbound-bugs@nlnetlabs.nl' PACKAGE_URL='' @@ -1437,7 +1437,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.6.6 to adapt to many kinds of systems. +\`configure' configures unbound 1.6.8 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1502,7 +1502,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of unbound 1.6.6:";; + short | recursive ) echo "Configuration of unbound 1.6.8:";; esac cat <<\_ACEOF @@ -1714,7 +1714,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -unbound configure 1.6.6 +unbound configure 1.6.8 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2423,7 +2423,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.6.6, which was +It was created by unbound $as_me 1.6.8, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2775,11 +2775,11 @@ UNBOUND_VERSION_MAJOR=1 UNBOUND_VERSION_MINOR=6 -UNBOUND_VERSION_MICRO=6 +UNBOUND_VERSION_MICRO=8 LIBUNBOUND_CURRENT=7 -LIBUNBOUND_REVISION=5 +LIBUNBOUND_REVISION=7 LIBUNBOUND_AGE=5 # 1.0.0 had 0:12:0 # 1.0.1 had 0:13:0 @@ -2836,6 +2836,8 @@ LIBUNBOUND_AGE=5 # 1.6.4 had 7:3:5 # 1.6.5 had 7:4:5 # 1.6.6 had 7:5:5 +# 1.6.7 had 7:6:5 +# 1.6.8 had 7:7:5 # Current -- the number of the binary API that we're implementing # Revision -- which iteration of the implementation of the binary @@ -2851,7 +2853,7 @@ LIBUNBOUND_AGE=5 # Current and Age. Set Revision to 0, since this is the first # implementation of the new API. # -# Otherwise, we're changing the binary API and breaking bakward +# Otherwise, we're changing the binary API and breaking backward # compatibility with old binaries. Increment Current. Set Age to 0, # since we're backward compatible with no previous APIs. Set Revision # to 0 too. @@ -4427,6 +4429,7 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $ ac_compiler_gnu=$ac_cv_c_compiler_gnu # allow user to override the -g -O2 flags. +default_cflags=no if test "x$CFLAGS" = "x" ; then @@ -4490,6 +4493,7 @@ $as_echo "no" >&6; } fi +default_cflags=yes fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' @@ -5943,6 +5947,10 @@ $as_echo "#define UNBOUND_DEBUG /**/" >>confdefs.h # nothing to do. ;; esac +if test "$default_cflags" = "yes"; then + # only when CFLAGS was "" at the start, if the users wants to + # override we shouldn't add default cflags, because they wouldn't + # be able to turn off these options and set the CFLAGS wanted. # Check whether --enable-flto was given. if test "${enable_flto+set}" = set; then : @@ -6077,6 +6085,7 @@ rm -f core conftest.err conftest.$ac_objext \ fi +fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 $as_echo_n "checking for inline... " >&6; } @@ -20486,6 +20495,66 @@ else fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing sodium_set_misuse_handler" >&5 +$as_echo_n "checking for library containing sodium_set_misuse_handler... " >&6; } +if ${ac_cv_search_sodium_set_misuse_handler+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char sodium_set_misuse_handler (); +int +main () +{ +return sodium_set_misuse_handler (); + ; + return 0; +} +_ACEOF +for ac_lib in '' sodium; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_sodium_set_misuse_handler=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_sodium_set_misuse_handler+:} false; then : + break +fi +done +if ${ac_cv_search_sodium_set_misuse_handler+:} false; then : + +else + ac_cv_search_sodium_set_misuse_handler=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_sodium_set_misuse_handler" >&5 +$as_echo "$ac_cv_search_sodium_set_misuse_handler" >&6; } +ac_res=$ac_cv_search_sodium_set_misuse_handler +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + + +$as_echo "#define SODIUM_MISUSE_HANDLER 1" >>confdefs.h + + +fi + $as_echo "#define USE_DNSCRYPT 1" >>confdefs.h @@ -20599,6 +20668,15 @@ if test "${with_libunbound_only+set}" = set; then : fi +if test $ALLTARGET = "alltargets"; then + if test $USE_NSS = "yes"; then + as_fn_error $? "--with-nss can only be used in combination with --with-libunbound-only." "$LINENO" 5 + fi + if test $USE_NETTLE = "yes"; then + as_fn_error $? "--with-nettle can only be used in combination with --with-libunbound-only." "$LINENO" 5 + fi +fi + @@ -20693,7 +20771,7 @@ _ACEOF -version=1.6.6 +version=1.6.8 date=`date +'%b %e, %Y'` @@ -21212,7 +21290,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.6.6, which was +This file was extended by unbound $as_me 1.6.8, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -21278,7 +21356,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -unbound config.status 1.6.6 +unbound config.status 1.6.8 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/usr.sbin/unbound/configure.ac b/usr.sbin/unbound/configure.ac index 6a3bfb1d898..6e4eec561c9 100644 --- a/usr.sbin/unbound/configure.ac +++ b/usr.sbin/unbound/configure.ac @@ -11,14 +11,14 @@ sinclude(dnscrypt/dnscrypt.m4) # must be numbers. ac_defun because of later processing m4_define([VERSION_MAJOR],[1]) m4_define([VERSION_MINOR],[6]) -m4_define([VERSION_MICRO],[6]) +m4_define([VERSION_MICRO],[8]) AC_INIT(unbound, m4_defn([VERSION_MAJOR]).m4_defn([VERSION_MINOR]).m4_defn([VERSION_MICRO]), unbound-bugs@nlnetlabs.nl, unbound) AC_SUBST(UNBOUND_VERSION_MAJOR, [VERSION_MAJOR]) AC_SUBST(UNBOUND_VERSION_MINOR, [VERSION_MINOR]) AC_SUBST(UNBOUND_VERSION_MICRO, [VERSION_MICRO]) LIBUNBOUND_CURRENT=7 -LIBUNBOUND_REVISION=5 +LIBUNBOUND_REVISION=7 LIBUNBOUND_AGE=5 # 1.0.0 had 0:12:0 # 1.0.1 had 0:13:0 @@ -75,6 +75,8 @@ LIBUNBOUND_AGE=5 # 1.6.4 had 7:3:5 # 1.6.5 had 7:4:5 # 1.6.6 had 7:5:5 +# 1.6.7 had 7:6:5 +# 1.6.8 had 7:7:5 # Current -- the number of the binary API that we're implementing # Revision -- which iteration of the implementation of the binary @@ -90,7 +92,7 @@ LIBUNBOUND_AGE=5 # Current and Age. Set Revision to 0, since this is the first # implementation of the new API. # -# Otherwise, we're changing the binary API and breaking bakward +# Otherwise, we're changing the binary API and breaking backward # compatibility with old binaries. Increment Current. Set Age to 0, # since we're backward compatible with no previous APIs. Set Revision # to 0 too. @@ -244,9 +246,11 @@ AC_DEFINE_UNQUOTED(RSRC_PACKAGE_VERSION, [$wnvs], [version number for resource f AC_C_CONST AC_LANG_C # allow user to override the -g -O2 flags. +default_cflags=no if test "x$CFLAGS" = "x" ; then ACX_CHECK_COMPILER_FLAG(g, [CFLAGS="$CFLAGS -g"]) ACX_CHECK_COMPILER_FLAG(O2, [CFLAGS="$CFLAGS -O2"]) +default_cflags=yes fi AC_PROG_CC ACX_DEPFLAG @@ -270,9 +274,14 @@ case "$debug_enabled" in # nothing to do. ;; esac -ACX_CHECK_FLTO -ACX_CHECK_PIE -ACX_CHECK_RELRO_NOW +if test "$default_cflags" = "yes"; then + # only when CFLAGS was "" at the start, if the users wants to + # override we shouldn't add default cflags, because they wouldn't + # be able to turn off these options and set the CFLAGS wanted. + ACX_CHECK_FLTO + ACX_CHECK_PIE + ACX_CHECK_RELRO_NOW +fi AC_C_INLINE ACX_CHECK_FORMAT_ATTRIBUTE @@ -1469,6 +1478,15 @@ AC_ARG_WITH(libunbound-only, AC_HELP_STRING([--with-libunbound-only], INSTALLTARGET="install-lib" fi ]) +if test $ALLTARGET = "alltargets"; then + if test $USE_NSS = "yes"; then + AC_ERROR([--with-nss can only be used in combination with --with-libunbound-only.]) + fi + if test $USE_NETTLE = "yes"; then + AC_ERROR([--with-nettle can only be used in combination with --with-libunbound-only.]) + fi +fi + AC_SUBST(ALLTARGET) AC_SUBST(INSTALLTARGET) diff --git a/usr.sbin/unbound/daemon/remote.c b/usr.sbin/unbound/daemon/remote.c index 243d94c49aa..3477340ff57 100644 --- a/usr.sbin/unbound/daemon/remote.c +++ b/usr.sbin/unbound/daemon/remote.c @@ -827,6 +827,7 @@ print_mem(SSL* ssl, struct worker* worker, struct daemon* daemon) #endif /* USE_IPSECMOD */ #ifdef USE_DNSCRYPT size_t dnscrypt_shared_secret = 0; + size_t dnscrypt_nonce = 0; #endif /* USE_DNSCRYPT */ msg = slabhash_get_mem(daemon->env->msg_cache); rrset = slabhash_get_mem(&daemon->env->rrset_cache->table); @@ -843,6 +844,7 @@ print_mem(SSL* ssl, struct worker* worker, struct daemon* daemon) if(daemon->dnscenv) { dnscrypt_shared_secret = slabhash_get_mem( daemon->dnscenv->shared_secrets_cache); + dnscrypt_nonce = slabhash_get_mem(daemon->dnscenv->nonces_cache); } #endif /* USE_DNSCRYPT */ @@ -868,6 +870,9 @@ print_mem(SSL* ssl, struct worker* worker, struct daemon* daemon) if(!print_longnum(ssl, "mem.cache.dnscrypt_shared_secret"SQ, dnscrypt_shared_secret)) return 0; + if(!print_longnum(ssl, "mem.cache.dnscrypt_nonce"SQ, + dnscrypt_nonce)) + return 0; #endif /* USE_DNSCRYPT */ return 1; } @@ -1058,8 +1063,12 @@ print_ext(SSL* ssl, struct ub_stats_info* s) #ifdef USE_DNSCRYPT if(!ssl_printf(ssl, "dnscrypt_shared_secret.cache.count"SQ"%u\n", (unsigned)s->svr.shared_secret_cache_count)) return 0; + if(!ssl_printf(ssl, "dnscrypt_nonce.cache.count"SQ"%u\n", + (unsigned)s->svr.nonce_cache_count)) return 0; if(!ssl_printf(ssl, "num.query.dnscrypt.shared_secret.cachemiss"SQ"%lu\n", (unsigned long)s->svr.num_query_dnscrypt_secret_missed_cache)) return 0; + if(!ssl_printf(ssl, "num.query.dnscrypt.replay"SQ"%lu\n", + (unsigned long)s->svr.num_query_dnscrypt_replay)) return 0; #endif /* USE_DNSCRYPT */ return 1; } @@ -1771,7 +1780,7 @@ negative_del_rrset(struct lruhash_entry* e, void* arg) struct ub_packed_rrset_key* k = (struct ub_packed_rrset_key*)e->key; struct packed_rrset_data* d = (struct packed_rrset_data*)e->data; /* delete the parentside negative cache rrsets, - * these are namerserver rrsets that failed lookup, rdata empty */ + * these are nameserver rrsets that failed lookup, rdata empty */ if((k->rk.flags & PACKED_RRSET_PARENT_SIDE) && d->count == 1 && d->rrsig_count == 0 && d->rr_len[0] == 0) { d->ttl = inf->expired; diff --git a/usr.sbin/unbound/daemon/stats.c b/usr.sbin/unbound/daemon/stats.c index 1058556be7c..ed788720846 100644 --- a/usr.sbin/unbound/daemon/stats.c +++ b/usr.sbin/unbound/daemon/stats.c @@ -174,6 +174,21 @@ get_dnscrypt_cache_miss(struct worker* worker, int reset) lock_basic_unlock(&de->shared_secrets_cache_lock); return r; } + +/** get the number of replayed queries */ +static size_t +get_dnscrypt_replay(struct worker* worker, int reset) +{ + size_t r; + struct dnsc_env* de = worker->daemon->dnscenv; + + lock_basic_lock(&de->nonces_cache_lock); + r = de->num_query_dnscrypt_replay; + if(reset && !worker->env.cfg->stat_cumulative) + de->num_query_dnscrypt_replay = 0; + lock_basic_unlock(&de->nonces_cache_lock); + return r; +} #endif /* USE_DNSCRYPT */ void @@ -225,13 +240,21 @@ server_stats_compile(struct worker* worker, struct ub_stats_info* s, int reset) (long long)get_dnscrypt_cache_miss(worker, reset); s->svr.shared_secret_cache_count = (long long)count_slabhash_entries( worker->daemon->dnscenv->shared_secrets_cache); + s->svr.nonce_cache_count = (long long)count_slabhash_entries( + worker->daemon->dnscenv->nonces_cache); + s->svr.num_query_dnscrypt_replay = + (long long)get_dnscrypt_replay(worker, reset); } else { s->svr.num_query_dnscrypt_secret_missed_cache = 0; s->svr.shared_secret_cache_count = 0; + s->svr.nonce_cache_count = 0; + s->svr.num_query_dnscrypt_replay = 0; } #else s->svr.num_query_dnscrypt_secret_missed_cache = 0; s->svr.shared_secret_cache_count = 0; + s->svr.nonce_cache_count = 0; + s->svr.num_query_dnscrypt_replay = 0; #endif /* USE_DNSCRYPT */ /* get tcp accept usage */ diff --git a/usr.sbin/unbound/daemon/unbound.c b/usr.sbin/unbound/daemon/unbound.c index 070a824d673..432aa912e68 100644 --- a/usr.sbin/unbound/daemon/unbound.c +++ b/usr.sbin/unbound/daemon/unbound.c @@ -400,7 +400,7 @@ detach(void) #endif /* HAVE_DAEMON */ } -/** daemonize, drop user priviliges and chroot if needed */ +/** daemonize, drop user privileges and chroot if needed */ static void perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode, const char** cfgfile, int need_pidfile) diff --git a/usr.sbin/unbound/daemon/worker.c b/usr.sbin/unbound/daemon/worker.c index 683f9316911..233ae38e76f 100644 --- a/usr.sbin/unbound/daemon/worker.c +++ b/usr.sbin/unbound/daemon/worker.c @@ -1046,7 +1046,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error, strcasecmp(buf, worker->daemon->dnscenv->provider_name) == 0)) { verbose(VERB_ALGO, - "dnscrypt: not TXT %s. Receive: %s %s", + "dnscrypt: not TXT \"%s\". Received: %s \"%s\"", worker->daemon->dnscenv->provider_name, sldns_rr_descript(qinfo.qtype)->_name, buf); @@ -1266,13 +1266,9 @@ worker_handle_request(struct comm_point* c, void* arg, int error, * ACLs allow the snooping. */ if(!(LDNS_RD_WIRE(sldns_buffer_begin(c->buffer))) && acl != acl_allow_snoop ) { - sldns_buffer_set_limit(c->buffer, LDNS_HEADER_SIZE); - sldns_buffer_write_at(c->buffer, 4, - (uint8_t*)"\0\0\0\0\0\0\0\0", 8); - LDNS_QR_SET(sldns_buffer_begin(c->buffer)); - LDNS_RCODE_SET(sldns_buffer_begin(c->buffer), - LDNS_RCODE_REFUSED); - sldns_buffer_flip(c->buffer); + error_encode(c->buffer, LDNS_RCODE_REFUSED, &qinfo, + *(uint16_t*)(void *)sldns_buffer_begin(c->buffer), + sldns_buffer_read_u16_at(c->buffer, 2), NULL); regional_free_all(worker->scratchpad); server_stats_insrcode(&worker->stats, c->buffer); log_addr(VERB_ALGO, "refused nonrec (cache snoop) query from", @@ -1633,7 +1629,8 @@ worker_init(struct worker* worker, struct config_file *cfg, cfg->use_caps_bits_for_id, worker->ports, worker->numports, cfg->unwanted_threshold, cfg->outgoing_tcp_mss, &worker_alloc_cleanup, worker, - cfg->do_udp, worker->daemon->connect_sslctx, cfg->delay_close, + cfg->do_udp || cfg->udp_upstream_without_downstream, + worker->daemon->connect_sslctx, cfg->delay_close, dtenv); if(!worker->back) { log_err("could not create outgoing sockets"); @@ -1671,8 +1668,10 @@ worker_init(struct worker* worker, struct config_file *cfg, if(worker->thread_num == 0) log_set_time(worker->env.now); worker->env.worker = worker; + worker->env.worker_base = worker->base; worker->env.send_query = &worker_send_query; worker->env.alloc = &worker->alloc; + worker->env.outnet = worker->back; worker->env.rnd = worker->rndstate; /* If case prefetch is triggered, the corresponding mesh will clear * the scratchpad for the module env in the middle of request handling. diff --git a/usr.sbin/unbound/dns64/dns64.c b/usr.sbin/unbound/dns64/dns64.c index 2f2d1255d6f..7889d72e229 100644 --- a/usr.sbin/unbound/dns64/dns64.c +++ b/usr.sbin/unbound/dns64/dns64.c @@ -792,6 +792,10 @@ dns64_inform_super(struct module_qstate* qstate, int id, qstate->return_msg->rep)) return; + /* Use return code from A query in response to client. */ + if (super->return_rcode != LDNS_RCODE_NOERROR) + super->return_rcode = qstate->return_rcode; + /* Generate a response suitable for the original query. */ if (qstate->qinfo.qtype == LDNS_RR_TYPE_A) { dns64_adjust_a(id, super, qstate); diff --git a/usr.sbin/unbound/dnscrypt/dnscrypt.c b/usr.sbin/unbound/dnscrypt/dnscrypt.c index db054df9d68..f139ce8b22b 100644 --- a/usr.sbin/unbound/dnscrypt/dnscrypt.c +++ b/usr.sbin/unbound/dnscrypt/dnscrypt.c @@ -5,6 +5,7 @@ #ifdef HAVE_TIME_H #include <time.h> #endif +#include <inttypes.h> #include <sys/time.h> #include <sys/types.h> #include "sldns/sbuffer.h" @@ -60,6 +61,17 @@ struct shared_secret_cache_key { }; +struct nonce_cache_key { + /** the nonce used by the client */ + uint8_t nonce[crypto_box_HALF_NONCEBYTES]; + /** the client_magic used by the client, this is associated to 1 cert only */ + uint8_t magic_query[DNSCRYPT_MAGIC_HEADER_LEN]; + /** the client public key */ + uint8_t client_publickey[crypto_box_PUBLICKEYBYTES]; + /** the hash table entry, data is uint8_t */ + struct lruhash_entry entry; +}; + /** * Generate a key suitable to find shared secret in slabhash. * \param[in] key: a uint8_t pointer of size DNSCRYPT_SHARED_SECRET_KEY_LENGTH @@ -136,6 +148,87 @@ dnsc_shared_secrets_lookup(struct slabhash* cache, } /** + * Generate a key hash suitable to find a nonce in slabhash. + * \param[in] nonce: a uint8_t pointer of size crypto_box_HALF_NONCEBYTES + * \param[in] magic_query: a uint8_t pointer of size DNSCRYPT_MAGIC_HEADER_LEN + * \param[in] pk: The public key of the client. uint8_t pointer of size + * crypto_box_PUBLICKEYBYTES. + * \return the hash of the key. + */ +static uint32_t +dnsc_nonce_cache_key_hash(const uint8_t nonce[crypto_box_HALF_NONCEBYTES], + const uint8_t magic_query[DNSCRYPT_MAGIC_HEADER_LEN], + const uint8_t pk[crypto_box_PUBLICKEYBYTES]) +{ + uint32_t h = 0; + h = hashlittle(nonce, crypto_box_HALF_NONCEBYTES, h); + h = hashlittle(magic_query, DNSCRYPT_MAGIC_HEADER_LEN, h); + return hashlittle(pk, crypto_box_PUBLICKEYBYTES, h); +} + +/** + * Inserts a nonce, magic_query, pk tuple into the nonces_cache slabhash. + * \param[in] cache: the slabhash in which to look for the key. + * \param[in] nonce: a uint8_t pointer of size crypto_box_HALF_NONCEBYTES + * \param[in] magic_query: a uint8_t pointer of size DNSCRYPT_MAGIC_HEADER_LEN + * \param[in] pk: The public key of the client. uint8_t pointer of size + * crypto_box_PUBLICKEYBYTES. + * \param[in] hash: the hash of the key. + */ +static void +dnsc_nonce_cache_insert(struct slabhash *cache, + const uint8_t nonce[crypto_box_HALF_NONCEBYTES], + const uint8_t magic_query[DNSCRYPT_MAGIC_HEADER_LEN], + const uint8_t pk[crypto_box_PUBLICKEYBYTES], + uint32_t hash) +{ + struct nonce_cache_key* k = + (struct nonce_cache_key*)calloc(1, sizeof(*k)); + if(!k) { + free(k); + return; + } + lock_rw_init(&k->entry.lock); + memcpy(k->nonce, nonce, crypto_box_HALF_NONCEBYTES); + memcpy(k->magic_query, magic_query, DNSCRYPT_MAGIC_HEADER_LEN); + memcpy(k->client_publickey, pk, crypto_box_PUBLICKEYBYTES); + k->entry.hash = hash; + k->entry.key = k; + k->entry.data = NULL; + slabhash_insert(cache, + hash, &k->entry, + NULL, + NULL); +} + +/** + * Lookup a record in nonces_cache. + * \param[in] cache: the slabhash in which to look for the key. + * \param[in] nonce: a uint8_t pointer of size crypto_box_HALF_NONCEBYTES + * \param[in] magic_query: a uint8_t pointer of size DNSCRYPT_MAGIC_HEADER_LEN + * \param[in] pk: The public key of the client. uint8_t pointer of size + * crypto_box_PUBLICKEYBYTES. + * \param[in] hash: the hash of the key. + * \return a pointer to the locked cache entry or NULL on failure. + */ +static struct lruhash_entry* +dnsc_nonces_lookup(struct slabhash* cache, + const uint8_t nonce[crypto_box_HALF_NONCEBYTES], + const uint8_t magic_query[DNSCRYPT_MAGIC_HEADER_LEN], + const uint8_t pk[crypto_box_PUBLICKEYBYTES], + uint32_t hash) +{ + struct nonce_cache_key k; + memset(&k, 0, sizeof(k)); + k.entry.hash = hash; + memcpy(k.nonce, nonce, crypto_box_HALF_NONCEBYTES); + memcpy(k.magic_query, magic_query, DNSCRYPT_MAGIC_HEADER_LEN); + memcpy(k.client_publickey, pk, crypto_box_PUBLICKEYBYTES); + + return slabhash_lookup(cache, hash, &k, 0); +} + +/** * Decrypt a query using the dnsccert that was found using dnsc_find_cert. * The client nonce will be extracted from the encrypted query and stored in * client_nonce, a shared secret will be computed and stored in nmkey and the @@ -163,11 +256,44 @@ dnscrypt_server_uncurve(struct dnsc_env* env, struct lruhash_entry* entry; uint32_t hash; + uint32_t nonce_hash; + if (len <= DNSCRYPT_QUERY_HEADER_SIZE) { return -1; } query_header = (struct dnscrypt_query_header *)buf; + + /* Detect replay attacks */ + nonce_hash = dnsc_nonce_cache_key_hash( + query_header->nonce, + cert->magic_query, + query_header->publickey); + + lock_basic_lock(&env->nonces_cache_lock); + entry = dnsc_nonces_lookup( + env->nonces_cache, + query_header->nonce, + cert->magic_query, + query_header->publickey, + nonce_hash); + + if(entry) { + lock_rw_unlock(&entry->lock); + env->num_query_dnscrypt_replay++; + lock_basic_unlock(&env->nonces_cache_lock); + return -1; + } + + dnsc_nonce_cache_insert( + env->nonces_cache, + query_header->nonce, + cert->magic_query, + query_header->publickey, + nonce_hash); + lock_basic_unlock(&env->nonces_cache_lock); + + /* Find existing shared secret */ hash = dnsc_shared_secrets_cache_key(key, cert->es_version[1], query_header->publickey, @@ -463,18 +589,26 @@ dnsc_chroot_path(struct config_file *cfg, char *path) static int dnsc_parse_certs(struct dnsc_env *env, struct config_file *cfg) { - struct config_strlist *head; + struct config_strlist *head, *head2; size_t signed_cert_id; + size_t rotated_cert_id; char *nm; env->signed_certs_count = 0U; + env->rotated_certs_count = 0U; for (head = cfg->dnscrypt_provider_cert; head; head = head->next) { env->signed_certs_count++; } + for (head = cfg->dnscrypt_provider_cert_rotated; head; head = head->next) { + env->rotated_certs_count++; + } env->signed_certs = sodium_allocarray(env->signed_certs_count, sizeof *env->signed_certs); + env->rotated_certs = sodium_allocarray(env->rotated_certs_count, + sizeof env->signed_certs); signed_cert_id = 0U; + rotated_cert_id = 0U; for(head = cfg->dnscrypt_provider_cert; head; head = head->next, signed_cert_id++) { nm = dnsc_chroot_path(cfg, head->str); if(dnsc_read_from_file( @@ -483,6 +617,14 @@ dnsc_parse_certs(struct dnsc_env *env, struct config_file *cfg) sizeof(struct SignedCert)) != 0) { fatal_exit("dnsc_parse_certs: failed to load %s: %s", head->str, strerror(errno)); } + for(head2 = cfg->dnscrypt_provider_cert_rotated; head2; head2 = head2->next) { + if(strcmp(head->str, head2->str) == 0) { + *(env->rotated_certs + rotated_cert_id) = env->signed_certs + signed_cert_id; + rotated_cert_id++; + verbose(VERB_OPS, "Cert %s is rotated and will not be distributed via DNS", head->str); + break; + } + } verbose(VERB_OPS, "Loaded cert %s", head->str); } return signed_cert_id; @@ -547,7 +689,7 @@ dnsc_find_cert(struct dnsc_env* dnscenv, struct sldns_buffer* buffer) * In order to be able to serve certs over TXT, we can reuse the local-zone and * local-data config option. The zone and qname are infered from the * provider_name and the content of the TXT record from the certificate content. - * returns the number of certtificate TXT record that were loaded. + * returns the number of certificate TXT record that were loaded. * < 0 in case of error. */ static int @@ -567,27 +709,54 @@ dnsc_load_local_data(struct dnsc_env* dnscenv, struct config_file *cfg) // 2.dnscrypt-cert.example.com 86400 IN TXT "DNSC......" for(i=0; i<dnscenv->signed_certs_count; i++) { const char *ttl_class_type = " 86400 IN TXT \""; + int rotated_cert = 0; + uint32_t serial; + uint16_t rrlen; + char* rr; struct SignedCert *cert = dnscenv->signed_certs + i; - uint16_t rrlen = strlen(dnscenv->provider_name) + + // Check if the certificate is being rotated and should not be published + for(j=0; j<dnscenv->rotated_certs_count; j++){ + if(cert == dnscenv->rotated_certs[j]) { + rotated_cert = 1; + break; + } + } + memcpy(&serial, cert->serial, sizeof serial); + serial = htonl(serial); + if(rotated_cert) { + verbose(VERB_OPS, + "DNSCrypt: not adding cert with serial #%" + PRIu32 + " to local-data as it is rotated", + serial + ); + continue; + } + rrlen = strlen(dnscenv->provider_name) + strlen(ttl_class_type) + 4 * sizeof(struct SignedCert) + // worst case scenario 1 + // trailing double quote 1; - char *rr = malloc(rrlen); + rr = malloc(rrlen); if(!rr) { log_err("Could not allocate memory"); return -2; } snprintf(rr, rrlen - 1, "%s 86400 IN TXT \"", dnscenv->provider_name); for(j=0; j<sizeof(struct SignedCert); j++) { - int c = (int)*((const uint8_t *) cert + j); + int c = (int)*((const uint8_t *) cert + j); if (isprint(c) && c != '"' && c != '\\') { snprintf(rr + strlen(rr), rrlen - 1 - strlen(rr), "%c", c); } else { snprintf(rr + strlen(rr), rrlen - 1 - strlen(rr), "\\%03d", c); } } - verbose(VERB_OPS, "DNSCrypt: adding local data to config: %s", rr); + verbose(VERB_OPS, + "DNSCrypt: adding cert with serial #%" + PRIu32 + " to local-data to config: %s", + serial, rr + ); snprintf(rr + strlen(rr), rrlen - 1 - strlen(rr), "\""); cfg_strlist_insert(&cfg->local_data, strdup(rr)); free(rr); @@ -701,6 +870,16 @@ dnsc_parse_keys(struct dnsc_env *env, struct config_file *cfg) return cert_id; } +static void +sodium_misuse_handler(void) +{ + fatal_exit( + "dnscrypt: libsodium could not be initialized, this typically" + " happens when no good source of entropy is found. If you run" + " unbound in a chroot, make sure /dev/random is available. See" + " https://www.unbound.net/documentation/unbound.conf.html"); +} + /** * ######################################################### @@ -764,14 +943,25 @@ struct dnsc_env * dnsc_create(void) { struct dnsc_env *env; +#ifdef SODIUM_MISUSE_HANDLER + sodium_set_misuse_handler(sodium_misuse_handler); +#endif if (sodium_init() == -1) { fatal_exit("dnsc_create: could not initialize libsodium."); } env = (struct dnsc_env *) calloc(1, sizeof(struct dnsc_env)); lock_basic_init(&env->shared_secrets_cache_lock); lock_protect(&env->shared_secrets_cache_lock, - &env->num_query_dnscrypt_secret_missed_cache, - sizeof(env->num_query_dnscrypt_secret_missed_cache)); + &env->num_query_dnscrypt_secret_missed_cache, + sizeof(env->num_query_dnscrypt_secret_missed_cache)); + lock_basic_init(&env->nonces_cache_lock); + lock_protect(&env->nonces_cache_lock, + &env->nonces_cache, + sizeof(env->nonces_cache)); + lock_protect(&env->nonces_cache_lock, + &env->num_query_dnscrypt_replay, + sizeof(env->num_query_dnscrypt_replay)); + return env; } @@ -803,6 +993,16 @@ dnsc_apply_cfg(struct dnsc_env *env, struct config_file *cfg) if(!env->shared_secrets_cache){ fatal_exit("dnsc_apply_cfg: could not create shared secrets cache."); } + env->nonces_cache = slabhash_create( + cfg->dnscrypt_nonce_cache_slabs, + HASH_DEFAULT_STARTARRAY, + cfg->dnscrypt_nonce_cache_size, + dnsc_nonces_sizefunc, + dnsc_nonces_compfunc, + dnsc_nonces_delkeyfunc, + dnsc_nonces_deldatafunc, + NULL + ); return 0; } @@ -814,10 +1014,13 @@ dnsc_delete(struct dnsc_env *env) } verbose(VERB_OPS, "DNSCrypt: Freeing environment."); sodium_free(env->signed_certs); + sodium_free(env->rotated_certs); sodium_free(env->certs); sodium_free(env->keypairs); slabhash_delete(env->shared_secrets_cache); + slabhash_delete(env->nonces_cache); lock_basic_destroy(&env->shared_secrets_cache_lock); + lock_basic_destroy(&env->nonces_cache_lock); free(env); } @@ -858,3 +1061,51 @@ dnsc_shared_secrets_deldatafunc(void* d, void* ATTR_UNUSED(arg)) uint8_t* data = (uint8_t*)d; free(data); } + +/** + * ######################################################### + * ############### Nonces cache functions ################## + * ######################################################### + */ + +size_t +dnsc_nonces_sizefunc(void *k, void* ATTR_UNUSED(d)) +{ + struct nonce_cache_key* nk = (struct nonce_cache_key*)k; + size_t key_size = sizeof(struct nonce_cache_key) + + lock_get_mem(&nk->entry.lock); + (void)nk; /* otherwise ssk is unused if no threading, or fixed locksize */ + return key_size; +} + +int +dnsc_nonces_compfunc(void *m1, void *m2) +{ + struct nonce_cache_key *k1 = m1, *k2 = m2; + return + sodium_memcmp( + k1->nonce, + k2->nonce, + crypto_box_HALF_NONCEBYTES) != 0 || + sodium_memcmp( + k1->magic_query, + k2->magic_query, + DNSCRYPT_MAGIC_HEADER_LEN) != 0 || + sodium_memcmp( + k1->client_publickey, k2->client_publickey, + crypto_box_PUBLICKEYBYTES) != 0; +} + +void +dnsc_nonces_delkeyfunc(void *k, void* ATTR_UNUSED(arg)) +{ + struct nonce_cache_key* nk = (struct nonce_cache_key*)k; + lock_rw_destroy(&nk->entry.lock); + free(nk); +} + +void +dnsc_nonces_deldatafunc(void* ATTR_UNUSED(d), void* ATTR_UNUSED(arg)) +{ + return; +} diff --git a/usr.sbin/unbound/dnscrypt/dnscrypt.h b/usr.sbin/unbound/dnscrypt/dnscrypt.h index dde36d6675d..666f54e62aa 100644 --- a/usr.sbin/unbound/dnscrypt/dnscrypt.h +++ b/usr.sbin/unbound/dnscrypt/dnscrypt.h @@ -54,8 +54,10 @@ typedef struct cert_ { struct dnsc_env { struct SignedCert *signed_certs; + struct SignedCert **rotated_certs; dnsccert *certs; size_t signed_certs_count; + size_t rotated_certs_count; uint8_t provider_publickey[crypto_sign_ed25519_PUBLICKEYBYTES]; uint8_t provider_secretkey[crypto_sign_ed25519_SECRETKEYBYTES]; KeyPair *keypairs; @@ -63,11 +65,20 @@ struct dnsc_env { uint64_t nonce_ts_last; unsigned char hash_key[crypto_shorthash_KEYBYTES]; char * provider_name; + + /** Caches */ struct slabhash *shared_secrets_cache; /** lock on shared secret cache counters */ lock_basic_type shared_secrets_cache_lock; /** number of misses from shared_secrets_cache */ size_t num_query_dnscrypt_secret_missed_cache; + + /** slabhash keeping track of nonce/cient pk/server sk pairs. */ + struct slabhash *nonces_cache; + /** lock on nonces_cache, used to avoid race condition in updating the hash */ + lock_basic_type nonces_cache_lock; + /** number of replayed queries */ + size_t num_query_dnscrypt_replay; }; struct dnscrypt_query_header { @@ -139,5 +150,26 @@ void dnsc_shared_secrets_delkeyfunc(void *k, void* arg); */ void dnsc_shared_secrets_deldatafunc(void* d, void* arg); +/** + * Computes the size of the nonce cache entry. + */ +size_t dnsc_nonces_sizefunc(void *k, void *d); + +/** + * Compares two nonce cache keys. + */ +int dnsc_nonces_compfunc(void *m1, void *m2); + +/** + * Function to delete a nonce cache key. + */ +void dnsc_nonces_delkeyfunc(void *k, void* arg); + +/** + * Function to delete a nonce cache value. + */ +void dnsc_nonces_deldatafunc(void* d, void* arg); + + #endif /* USE_DNSCRYPT */ #endif diff --git a/usr.sbin/unbound/dnscrypt/dnscrypt.m4 b/usr.sbin/unbound/dnscrypt/dnscrypt.m4 index 7193519fcf0..591bd137558 100644 --- a/usr.sbin/unbound/dnscrypt/dnscrypt.m4 +++ b/usr.sbin/unbound/dnscrypt/dnscrypt.m4 @@ -28,6 +28,14 @@ AC_DEFUN([dnsc_DNSCRYPT], [ AC_SUBST([ENABLE_DNSCRYPT_XCHACHA20], [0]) ]) + AC_SEARCH_LIBS([sodium_set_misuse_handler], [sodium], + [ + AC_DEFINE( + [SODIUM_MISUSE_HANDLER], [1], + [Define to 1 if libsodium supports sodium_set_misuse_handler]) + ], + [ + ]) $1 else AC_SUBST([ENABLE_DNSCRYPT_XCHACHA20], [0]) diff --git a/usr.sbin/unbound/doc/Changelog b/usr.sbin/unbound/doc/Changelog index 39a3a2b7f4a..05929c48d2e 100644 --- a/usr.sbin/unbound/doc/Changelog +++ b/usr.sbin/unbound/doc/Changelog @@ -1,5 +1,166 @@ +19 January 2018: Wouter + - tag 1.6.8 for release with CVE fix. + - trunk has 1.6.9 with fix and previous commits. + - patch for CVE-2017-15105: vulnerability in the processing of + wildcard synthesized NSEC records. + +4 January 2018: Ralph + - Copy query and correctly set flags on REFUSED answers when cache + snooping is not allowed. + +3 January 2018: Ralph + - Fix queries being leaked above stub when refetching glue. + +2 January 2017: Wouter + - Fix that DS queries with referral replies are answered straight + away, without a repeat query picking the DS from cache. + The correct reply should have been an answer, the reply is fixed + by the scrubber to have the answer in the answer section. + - Remove clang optimizer disable, + Fix that expiration date checks don't fail with clang -O2. + +15 December 2017: Wouter + - Fix timestamp failure because of clang optimizer failure, by + disabling -O2 when the compiler --version is clang. + - iana port update. + - Also disable -flto for clang, to make incep-expi signature check + work. + +12 December 2017: Ralph + - Fix qname-minimisation documentation (A QTYPE, not NS) + +12 December 2017: Wouter + - authzone work, transfer connect. + +7 December 2017: Ralph + - Check whether --with-libunbound-only is set when using --with-nettle + or --with-nss. + +4 December 2017: Wouter + - Fix link failure on OmniOS. + +1 December 2017: Wouter + - auth zone work. + +30 November 2017: Wouter + - Fix #3299 - forward CNAME daisy chain is not working + +14 November 2017: Wouter + - Fix #2882: Unbound behaviour changes (wrong) when domain-insecure is + set for stub zone. It no longer searches for DNSSEC information. + - auth xfer work on probe timer and lookup. + +13 November 2017: Wouter + - Fix #2801: Install libunbound.pc. + - Fix qname minimisation to send AAAA queries at zonecut like type A. + - reverted AAAA change. + +7 November 2017: Wouter + - Fix #2492: Documentation libunbound. + +3 November 2017: Wouter + - Fix #2362: TLS1.3/openssl-1.1.1 not working. + - Fix #2034 - Autoconf and -flto. + - Fix #2141 - for libsodium detect lack of entropy in chroot, print + a message and exit. + +2 November 2017: Wouter + - Fix #1913: ub_ctx_config is under circumstances thread-safe. + - make ip-transparent option work on OpenBSD. + +31 October 2017: Wouter + - Document that errno is left informative on libunbound config read + fail. + - lexer output. + - iana port update. + +25 October 2017: Ralph + - Fixed libunbound manual typo. + - Fix #1949: [dnscrypt] make provider name mismatch more obvious. + - Fix #2031: Double included headers + +24 October 2017: Ralph + - Update B root ipv4 address. + +19 October 2017: Wouter + - authzone work, probe timer setup. + +18 October 2017: Wouter + - lint for recent authzone commit. + +17 October 2017: Wouter + - Fix #1749: With harden-referral-path: performance drops, due to + circular dependency in NS and DS lookups. + - [dnscrypt] prevent dnscrypt-secret-key, dnscrypt-provider-cert + duplicates + - [dnscrypt] introduce dnscrypt-provider-cert-rotated option, + from Manu Bretelle. + This option allows handling multiple cert/key pairs while only + distributing some of them. + In order to reliably match a client magic with a given key without + strong assumption as to how those were generated, we need both key and + cert. Likewise, in order to know which ES version should be used. + On the other hand, when rotating a cert, it can be desirable to only + serve the new cert but still be able to handle clients that are still + using the old certs's public key. + The `dnscrypt-provider-cert-rotated` allow to instruct unbound to not + publish the cert as part of the DNS's provider_name's TXT answer. + - Better documentation for cache-max-negative-ttl. + - Work on local root zone code. + +10 October 2017: Wouter + - tag 1.6.7 + - trunk has version 1.6.8. + +6 October 2017: Wouter + - Fix spelling in unbound-control man page. + +5 October 2017: Wouter + - Fix trust-anchor-signaling works in libunbound. + - Fix some more crpls in testdata for different signaling default. + - tag 1.6.7rc1 + +5 October 2017: Ralph + - Set trust-anchor-signaling default to yes + - Use RCODE from A query on DNS64 synthesized answer. + +2 October 2017: Wouter + - Fix param unused warning for windows exportsymbol compile. + +25 September 2017: Ralph + - Fix #1450: Generate again patch contrib/aaaa-filter-iterator.patch + (by Danilo G. Baio). + +21 September 2017: Ralph + - Log name of looping module + +19 September 2017: Wouter + - use a cachedb answer even if it's "expired" when serve-expired is yes + (patch from Jinmei Tatuya). + - trigger refetching of the answer in that case (this will bypass + cachedb lookup) + - allow storing a 0-TTL answer from cachedb in the in-memory message + cache when serve-expired is yes + - Fix DNSCACHE_STORE_ZEROTTL to be bigger than 0xffff. + +18 September 2017: Ralph + - Fix #1400: allowing use of global cache on ECS-forwarding unless + always-forward. + +18 September 2017: Wouter + - tag 1.6.6 (is 1.6.6rc2) + - Fix that looping modules always stop the query, and don't pass + control. + - Fix #1435: Please allow UDP to be disabled separately upstream and + downstream. + - Fix #1440: [dnscrypt] client nonce cache. + +15 September 2017: Wouter + - Fix unbound-host to report error for DNSSEC state of failed lookups. + - Spelling fixes, from Josh Soref. + 13 September 2017: Wouter - - tag 1.6.6rc2 + - tag 1.6.6rc2, became 1.6.6 on 18 sep. trunk 1.6.7 in development. 12 September 2017: Wouter - Add dns64 for client-subnet in unbound-checkconf. diff --git a/usr.sbin/unbound/doc/example.conf.in b/usr.sbin/unbound/doc/example.conf.in index e7978b79c89..2d14d69c0f9 100644 --- a/usr.sbin/unbound/doc/example.conf.in +++ b/usr.sbin/unbound/doc/example.conf.in @@ -199,6 +199,10 @@ server: # upstream connections use TCP only (and no UDP), "yes" or "no" # useful for tunneling scenarios, default no. # tcp-upstream: no + + # upstream connections also use UDP (even if do-udp is no). + # useful if if you want UDP upstream, but don't provide UDP downstream. + # udp-upstream-without-downstream: no # Maximum segment size (MSS) of TCP socket on which the server # responds to queries. Default is 0, system default MSS. @@ -367,7 +371,7 @@ server: # Sent minimum amount of information to upstream servers to enhance # privacy. Only sent minimum required labels of the QNAME and set QTYPE - # to NS when possible. + # to A when possible. # qname-minimisation: no # QNAME minimisation in strict mode. Do not fall-back to sending full @@ -512,7 +516,7 @@ server: # that set CD but cannot validate themselves. # ignore-cd-flag: no - # Serve expired reponses from cache, with TTL 0 in the response, + # Serve expired responses from cache, with TTL 0 in the response, # and then attempt to fetch the data afresh. # serve-expired: no @@ -804,6 +808,30 @@ remote-control: # name: "example.org" # forward-host: fwd.example.com +# Authority zones +# The data for these zones is kept locally, from a file or downloaded. +# The data can be served to downstream clients, or used instead of the +# upstream (which saves a lookup to the upstream). The first example +# 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), master: fetches with AXFR, url: fetches zonefile over http. +# auth-zone: +# name: "." +# for-downstream: no +# for-upstream: yes +# master: b.root-servers.net +# master: c.root-servers.net +# master: e.root-servers.net +# master: f.root-servers.net +# master: g.root-servers.net +# master: k.root-servers.net +# auth-zone: +# name: "example.org" +# for-downstream: yes +# for-upstream: yes +# zonefile: "example.org.zone" +# url: "http://www.example.com/example.org.zone" + # Views # Create named views. Name must be unique. Map views to requests using # the access-control-view option. Views can contain zero or more local-zone diff --git a/usr.sbin/unbound/doc/libunbound.3.in b/usr.sbin/unbound/doc/libunbound.3.in index fbf3cd832af..8245f70cd84 100644 --- a/usr.sbin/unbound/doc/libunbound.3.in +++ b/usr.sbin/unbound/doc/libunbound.3.in @@ -150,7 +150,8 @@ is an implementation of a DNS resolver, that does caching and DNSSEC validation. This is the library API, for using the \-lunbound library. The server daemon is described in \fIunbound\fR(8). -The library can be used to convert hostnames to ip addresses, and back, +The library works independent from a running unbound server, and +can be used to convert hostnames to ip addresses, and back, and obtain other information from the DNS. The library performs public\-key validation of results with DNSSEC. .P @@ -162,7 +163,7 @@ and deleting it with It can be created and deleted at any time. Creating it anew removes any previous configuration (such as trusted keys) and clears any cached results. .P -The functions are thread\-safe, and a context an be used in a threaded (as +The functions are thread\-safe, and a context can be used in a threaded (as well as in a non\-threaded) environment. Also resolution (and validation) can be performed blocking and non\-blocking (also called asynchronous). The async method returns from the call immediately, so that processing @@ -203,7 +204,10 @@ without trailing ':'. The returned value must be free(2)d by the caller. A power\-user interface that lets you specify an unbound config file, see \fIunbound.conf\fR(5), which is read for configuration. Not all options are relevant. For some specific options, such as adding trust anchors, special -routines exist. +routines exist. This function is thread\-safe only if a single instance of +ub_ctx* exists in the application. If several instances exist the +application has to ensure that ub_ctx_config is not called in parallel by +the different instances. .TP .B ub_ctx_set_fwd Set machine to forward DNS queries to, the caching resolver to use. @@ -407,6 +411,10 @@ returns NULL on an error (a malloc failure). returns true if some information may be available, false otherwise. .B ub_fd returns a file descriptor or \-1 on error. +.B ub_ctx_config +and +.B ub_ctx_resolvconf +attempt to leave errno informative on a function return with file read failure. .SH "SEE ALSO" \fIunbound.conf\fR(5), \fIunbound\fR(8). diff --git a/usr.sbin/unbound/doc/requirements.txt b/usr.sbin/unbound/doc/requirements.txt index a66962d4a40..b643cec17d3 100644 --- a/usr.sbin/unbound/doc/requirements.txt +++ b/usr.sbin/unbound/doc/requirements.txt @@ -81,7 +81,7 @@ o Too many Features. 5. Choices ---------- -o rfc2181 decourages duplicates RRs in RRsets. unbound does not create +o rfc2181 discourages duplicates RRs in RRsets. unbound does not create duplicates, but when presented with duplicates on the wire from the authoritative servers, does not perform duplicate removal. It does do some rrsig duplicate removal, in the msgparser, for dnssec qtype diff --git a/usr.sbin/unbound/doc/unbound-control.8.in b/usr.sbin/unbound/doc/unbound-control.8.in index 66ea690390a..2f3fbf9e4f1 100644 --- a/usr.sbin/unbound/doc/unbound-control.8.in +++ b/usr.sbin/unbound/doc/unbound-control.8.in @@ -337,6 +337,19 @@ number of queries that were successfully answered using a cache lookup .I threadX.num.cachemiss number of queries that needed recursive processing .TP +.I threadX.num.dnscrypt.crypted +number of queries that were encrypted and successfully decapsulated by dnscrypt. +.TP +.I threadX.num.dnscrypt.cert +number of queries that were requesting dnscrypt certificates. +.TP +.I threadX.num.dnscrypt.cleartext +number of queries received on dnscrypt port that were cleartext and not a +request for certificates. +.TP +.I threadX.num.dnscrypt.malformed +number of request that were neither cleartext, not valid dnscrypt messages. +.TP .I threadX.num.prefetch number of cache prefetches performed. This number is included in cachehits, as the original query had the unprefetched answer from cache, @@ -393,6 +406,18 @@ summed over threads. .I total.num.cachemiss summed over threads. .TP +.I total.num.dnscrypt.crypted +summed over threads. +.TP +.I total.num.dnscrypt.cert +summed over threads. +.TP +.I total.num.dnscrypt.cleartext +summed over threads. +.TP +.I total.num.dnscrypt.malformed +summed over threads. +.TP .I total.num.prefetch summed over threads. .TP @@ -439,6 +464,12 @@ Memory in bytes in use by the RRset cache. .I mem.cache.message Memory in bytes in use by the message cache. .TP +.I mem.cache.dnscrypt_shared_secret +Memory in bytes in use by the dnscrypt shared secrets cache. +.TP +.I mem.cache.dnscrypt_nonce +Memory in bytes in use by the dnscrypt nonce cache. +.TP .I mem.mod.iterator Memory in bytes in use by the iterator module. .TP @@ -497,6 +528,14 @@ These queries are also included in the num.query.edns.present number. The number of queries that are turned away from being send to nameserver due to ratelimiting. .TP +.I num.query.dnscrypt.shared_secret.cachemiss +The number of dnscrypt queries that did not find a shared secret in the cache. +The can be use to compute the shared secret hitrate. +.TP +.I num.query.dnscrypt.replay +The number of dnscrypt queries that found a nonce hit in the nonce cache and +hence are considered a query replay. +.TP .I num.answer.rcode.NXDOMAIN The number of answers to queries, from cache or from recursion, that had the return code NXDOMAIN. Also printed for the other return codes. @@ -546,6 +585,19 @@ timing and protocol support information. .I key.cache.count The number of items in the key cache. These are DNSSEC keys, one item per delegation point, and their validation status. +.TP +.I dnscrypt_shared_secret.cache.count +The number of items in the shared secret cache. These are precomputed shared +secrets for a given client public key/server secret key pair. Shared secrets +are CPU intensive and this cache allows unbound to avoid recomputing the +shared secret when multiple dnscrypt queries are sent from the same client. +.TP +.I dnscrypt_nonce.cache.count +The number of items in the client nonce cache. This cache is used to prevent +dnscrypt queries replay. The client nonce must be unique for each client public +key/server secret key pair. This cache should be able to host QPS * `replay +window` interval keys to prevent replay of a query during `replay window` +seconds. .SH "FILES" .TP .I @ub_conf_file@ diff --git a/usr.sbin/unbound/doc/unbound.conf.5.in b/usr.sbin/unbound/doc/unbound.conf.5.in index fcad8017d73..7dc507c86a7 100644 --- a/usr.sbin/unbound/doc/unbound.conf.5.in +++ b/usr.sbin/unbound/doc/unbound.conf.5.in @@ -295,6 +295,7 @@ the data in the cache does not match up with the actual data any more. .B cache\-max\-negative\-ttl: \fI<seconds> Time to live maximum for negative responses, these have a SOA in the authority section that is limited in time. Default is 3600. +This applies to nxdomain and nodata answers. .TP .B infra\-host\-ttl: \fI<seconds> Time to live for entries in the host cache. The host cache contains @@ -356,6 +357,11 @@ negotiation between Unbound and other servers. Enable or disable whether the upstream queries use TCP only for transport. Default is no. Useful in tunneling scenarios. .TP +.B udp\-upstream\-without\-downstream: \fI<yes or no> +Enable udp upstream even if do-udp is no. Default is no, and this does not +change anything. Useful for TLS service providers, that want no udp downstream +but use udp to fetch data upstream. +.TP .B ssl\-upstream: \fI<yes or no> Enabled or disable whether the upstream queries use SSL only for transport. Default is no. Useful in tunneling scenarios. The SSL contains plain DNS in @@ -641,7 +647,7 @@ Can be given multiple times, for different domains. .TP .B qname\-minimisation: \fI<yes or no> Send minimum amount of information to upstream servers to enhance privacy. -Only sent minimum required labels of the QNAME and set QTYPE to NS when +Only sent minimum required labels of the QNAME and set QTYPE to A when possible. Best effort approach; full QNAME and original QTYPE will be sent when upstream replies with a RCODE other than NOERROR, except when receiving NXDOMAIN from a DNSSEC signed zone. Default is off. @@ -1343,6 +1349,9 @@ forward the queries to. The servers listed as \fBforward\-host:\fR and those servers are not authority servers, but are (just like unbound is) recursive servers too; unbound does not perform recursion itself for the forward zone, it lets the remote server do it. Class IN is assumed. +CNAMEs are chased by unbound itself, asking the remote server for every +name in the indirection chain, to protect the local cache from illegal +indirect referenced items. A forward\-zone entry with name "." and a forward\-addr target will forward all queries to that other server (unless it can answer from the cache). @@ -1464,6 +1473,19 @@ times. Path to the certificate related to the \fBdnscrypt\-secret\-key\fRs. This option may be specified multiple times. .TP +.B dnscrypt\-provider\-cert\-rotated: \fI<path to cert file>\fR +Path to a certificate that we should be able to serve existing connection from +but do not want to advertise over \fBdnscrypt\-provider\fR's TXT record certs +distribution. +A typical use case is when rotating certificates, existing clients may still use +the client magic from the old cert in their queries until they fetch and update +the new cert. Likewise, it would allow to prime the new cert/key without +distributing the new cert yet, this can be useful when using a network of +servers using anycast and on which the configuration may not get updated at the +exact same time. By priming the cert, the servers can handle both old and new +certs traffic while distributing only one. +This option may be specified multiple times. +.TP .B dnscrypt\-shared\-secret\-cache\-size: \fI<memory size> Give the size of the data structure in which the shared secret keys are kept in. Default 4m. In bytes or use m(mega), k(kilo), g(giga). @@ -1474,6 +1496,17 @@ using the same public key. It saves a substantial amount of CPU. Give power of 2 number of slabs, this is used to reduce lock contention in the dnscrypt shared secrets cache. Close to the number of cpus is a fairly good setting. +.TP +.B dnscrypt\-nonce\-cache\-size: \fI<memory size> +Give the size of the data structure in which the client nonces are kept in. +Default 4m. In bytes or use m(mega), k(kilo), g(giga). +The nonce cache is used to prevent dnscrypt message replaying. Client nonce +should be unique for any pair of client pk/server sk. +.TP +.B dnscrypt\-nonce\-cache\-slabs: \fI<number> +Give power of 2 number of slabs, this is used to reduce lock contention +in the dnscrypt nonce cache. Close to the number of cpus is +a fairly good setting. .SS "EDNS Client Subnet Module Options" .LP The ECS module must be configured in the \fBmodule\-config:\fR "subnetcache @@ -1487,7 +1520,7 @@ specialized cache. If the authority indicated no support, the response is stored in the regular cache. .LP Additionally, when a client includes the option in its queries, Unbound will -forward the option to the authority if prensent in the whitelist, or +forward the option to the authority if present in the whitelist, or \fBclient\-subnet\-always\-forward\fR is set to yes. In this case the lookup in the regular cache is skipped. .LP diff --git a/usr.sbin/unbound/edns-subnet/addrtree.h b/usr.sbin/unbound/edns-subnet/addrtree.h index 857e677f900..71d9d2ec6c9 100644 --- a/usr.sbin/unbound/edns-subnet/addrtree.h +++ b/usr.sbin/unbound/edns-subnet/addrtree.h @@ -108,7 +108,7 @@ struct addrnode { struct addredge { /** address of connected node */ addrkey_t *str; - /** lenght in bits of str */ + /** length in bits of str */ addrlen_t len; /** child node this edge is connected to */ struct addrnode *node; diff --git a/usr.sbin/unbound/edns-subnet/subnetmod.c b/usr.sbin/unbound/edns-subnet/subnetmod.c index 9e76cefdf3b..4b8a4b21127 100644 --- a/usr.sbin/unbound/edns-subnet/subnetmod.c +++ b/usr.sbin/unbound/edns-subnet/subnetmod.c @@ -532,7 +532,7 @@ parse_subnet_option(struct edns_option* ecs_option, struct ecs_data* ecs) ecs->subnet_addr_fam = sldns_read_uint16(ecs_option->opt_data); ecs->subnet_source_mask = ecs_option->opt_data[2]; ecs->subnet_scope_mask = ecs_option->opt_data[3]; - /* remaing bytes indicate address */ + /* remaining bytes indicate address */ /* validate input*/ /* option length matches calculated length? */ @@ -602,7 +602,7 @@ ecs_query_response(struct module_qstate* qstate, struct dns_msg* response, if(sq->subnet_sent && FLAGS_GET_RCODE(response->rep->flags) == LDNS_RCODE_REFUSED) { - /* REFUSED reponse to ECS query, remove ECS option. */ + /* REFUSED response to ECS query, remove ECS option. */ edns_opt_list_remove(&qstate->edns_opts_back_out, qstate->env->cfg->client_subnet_opcode); sq->subnet_sent = 0; @@ -628,7 +628,7 @@ ecs_edns_back_parsed(struct module_qstate* qstate, int id, sq->ecs_server_in.subnet_validdata) /* Only skip global cache store if we sent an ECS option * and received one back. Answers from non-whitelisted - * servers will end up in global cache. Ansers for + * servers will end up in global cache. Answers for * queries with 0 source will not (unless nameserver * does not support ECS). */ qstate->no_cache_store = 1; @@ -722,6 +722,7 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, sq->ecs_server_out.subnet_scope_mask = 0; sq->ecs_server_out.subnet_validdata = 1; if(sq->ecs_server_out.subnet_source_mask != 0 && + qstate->env->cfg->client_subnet_always_forward && sq->subnet_downstream) /* ECS specific data required, do not look at the global * cache in other modules. */ diff --git a/usr.sbin/unbound/edns-subnet/subnetmod.h b/usr.sbin/unbound/edns-subnet/subnetmod.h index 29bf64b4f55..c7f56327f9d 100644 --- a/usr.sbin/unbound/edns-subnet/subnetmod.h +++ b/usr.sbin/unbound/edns-subnet/subnetmod.h @@ -119,7 +119,7 @@ int ecs_whitelist_check(struct query_info* qinfo, uint16_t flags, socklen_t addrlen, uint8_t* zone, size_t zonelen, struct regional* region, int id, void* cbargs); -/** Check whether reponse from server contains ECS record, if so, skip cache +/** Check whether response from server contains ECS record, if so, skip cache * store. Called just after parsing EDNS data from server. */ int ecs_edns_back_parsed(struct module_qstate* qstate, int id, void* cbargs); diff --git a/usr.sbin/unbound/iterator/iter_hints.c b/usr.sbin/unbound/iterator/iter_hints.c index 47af96687fe..1d4c228fabc 100644 --- a/usr.sbin/unbound/iterator/iter_hints.c +++ b/usr.sbin/unbound/iterator/iter_hints.c @@ -129,7 +129,7 @@ compile_time_root_prime(int do_ip4, int do_ip6) dp->has_parent_side_NS = 1; if(do_ip4) { if(!ah(dp, "A.ROOT-SERVERS.NET.", "198.41.0.4")) goto failed; - if(!ah(dp, "B.ROOT-SERVERS.NET.", "192.228.79.201")) goto failed; + if(!ah(dp, "B.ROOT-SERVERS.NET.", "199.9.14.201")) goto failed; if(!ah(dp, "C.ROOT-SERVERS.NET.", "192.33.4.12")) goto failed; if(!ah(dp, "D.ROOT-SERVERS.NET.", "199.7.91.13")) goto failed; if(!ah(dp, "E.ROOT-SERVERS.NET.", "192.203.230.10")) goto failed; diff --git a/usr.sbin/unbound/iterator/iter_scrub.c b/usr.sbin/unbound/iterator/iter_scrub.c index 1bee85c0b99..12580dcdb53 100644 --- a/usr.sbin/unbound/iterator/iter_scrub.c +++ b/usr.sbin/unbound/iterator/iter_scrub.c @@ -503,6 +503,24 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg, continue; } } + /* if this is type DS and we query for type DS we just got + * a referral answer for our type DS query, fix packet */ + if(rrset->type==LDNS_RR_TYPE_DS && + qinfo->qtype == LDNS_RR_TYPE_DS && + dname_pkt_compare(pkt, qinfo->qname, rrset->dname) == 0) { + rrset->section = LDNS_SECTION_ANSWER; + msg->ancount = rrset->rr_count + rrset->rrsig_count; + msg->nscount = 0; + msg->arcount = 0; + msg->an_rrsets = 1; + msg->ns_rrsets = 0; + msg->ar_rrsets = 0; + msg->rrset_count = 1; + msg->rrset_first = rrset; + msg->rrset_last = rrset; + rrset->rrset_all_next = NULL; + return 1; + } mark_additional_rrset(pkt, msg, rrset); prev = rrset; rrset = rrset->rrset_all_next; diff --git a/usr.sbin/unbound/iterator/iter_utils.c b/usr.sbin/unbound/iterator/iter_utils.c index 0b1b456113f..70cab40faa8 100644 --- a/usr.sbin/unbound/iterator/iter_utils.c +++ b/usr.sbin/unbound/iterator/iter_utils.c @@ -656,6 +656,11 @@ iter_indicates_dnssec(struct module_env* env, struct delegpt* dp, /* a trust anchor exists with this name, RRSIGs expected */ if((a=anchor_find(env->anchors, dp->name, dp->namelabs, dp->namelen, dclass))) { + if(a->numDS == 0 && a->numDNSKEY == 0) { + /* insecure trust point */ + lock_basic_unlock(&a->lock); + return 0; + } lock_basic_unlock(&a->lock); return 1; } diff --git a/usr.sbin/unbound/iterator/iter_utils.h b/usr.sbin/unbound/iterator/iter_utils.h index 50c5fc093f6..602fa6db3d0 100644 --- a/usr.sbin/unbound/iterator/iter_utils.h +++ b/usr.sbin/unbound/iterator/iter_utils.h @@ -193,7 +193,7 @@ int iter_indicates_dnssec_fwd(struct module_env* env, * @param dp: delegation point. * @param msg: delegation message, with DS if a secure referral. * @param dclass: class of query. - * @return 1 if dnssec is expected, 0 if not. + * @return 1 if dnssec is expected, 0 if not or insecure point above qname. */ int iter_indicates_dnssec(struct module_env* env, struct delegpt* dp, struct dns_msg* msg, uint16_t dclass); @@ -252,7 +252,7 @@ void caps_strip_reply(struct reply_info* rep); int caps_failed_rcode(struct reply_info* rep); /** - * Store parent-side rrset in seperate rrset cache entries for later + * Store parent-side rrset in separate rrset cache entries for later * last-resort * lookups in case the child-side versions of this information * fails. * @param env: environment with cache, time, ... diff --git a/usr.sbin/unbound/iterator/iterator.c b/usr.sbin/unbound/iterator/iterator.c index 01ac883e81e..d95a6e03459 100644 --- a/usr.sbin/unbound/iterator/iterator.c +++ b/usr.sbin/unbound/iterator/iterator.c @@ -36,7 +36,7 @@ /** * \file * - * This file contains a module that performs recusive iterative DNS query + * This file contains a module that performs recursive iterative DNS query * processing. */ @@ -833,7 +833,7 @@ prime_stub(struct module_qstate* qstate, struct iter_qstate* iq, int id, /** * Generate A and AAAA checks for glue that is in-zone for the referral - * we just got to obtain authoritative information on the adresses. + * we just got to obtain authoritative information on the addresses. * * @param qstate: the qtstate that triggered the need to prime. * @param iq: iterator query state. @@ -914,6 +914,9 @@ generate_ns_check(struct module_qstate* qstate, struct iter_qstate* iq, int id) generate_a_aaaa_check(qstate, iq, id); return; } + /* no need to get the NS record for DS, it is above the zonecut */ + if(qstate->qinfo.qtype == LDNS_RR_TYPE_DS) + return; log_nametypeclass(VERB_ALGO, "schedule ns fetch", iq->dp->name, LDNS_RR_TYPE_NS, iq->qchase.qclass); @@ -1353,7 +1356,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, * the same init processing as ones that do not. Request events that reach * this state must have a valid currentDelegationPoint set. * - * This part is primarly handling stub zone priming. Events that reach this + * This part is primarily handling stub zone priming. Events that reach this * state must have a current delegation point. * * @param qstate: query state. @@ -1371,16 +1374,24 @@ processInitRequest2(struct module_qstate* qstate, struct iter_qstate* iq, log_query_info(VERB_QUERY, "resolving (init part 2): ", &qstate->qinfo); + delname = iq->qchase.qname; + delnamelen = iq->qchase.qname_len; if(iq->refetch_glue) { + struct iter_hints_stub* stub; if(!iq->dp) { log_err("internal or malloc fail: no dp for refetch"); return error_response(qstate, id, LDNS_RCODE_SERVFAIL); } - delname = iq->dp->name; - delnamelen = iq->dp->namelen; - } else { - delname = iq->qchase.qname; - delnamelen = iq->qchase.qname_len; + /* Do not send queries above stub, do not set delname to dp if + * this is above stub without stub-first. */ + stub = hints_lookup_stub( + qstate->env->hints, iq->qchase.qname, iq->qchase.qclass, + iq->dp); + if(!stub || !stub->dp->has_parent_side_NS || + dname_subdomain_c(iq->dp->name, stub->dp->name)) { + delname = iq->dp->name; + delnamelen = iq->dp->namelen; + } } if(iq->qchase.qtype == LDNS_RR_TYPE_DS || iq->refetch_glue) { if(!dname_is_root(delname)) @@ -2396,7 +2407,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, if(FLAGS_GET_RCODE(iq->response->rep->flags) == LDNS_RCODE_NXDOMAIN) { /* Stop resolving when NXDOMAIN is DNSSEC - * signed. Based on assumption that namservers + * signed. Based on assumption that nameservers * serving signed zones do not return NXDOMAIN * for empty-non-terminals. */ if(iq->dnssec_expected) @@ -2753,7 +2764,7 @@ processPrimeResponse(struct module_qstate* qstate, int id) /** * Do final processing on responses to target queries. Events reach this * state after the iterative resolution algorithm terminates. This state is - * responsible for reactiving the original event, and housekeeping related + * responsible for reactivating the original event, and housekeeping related * to received target responses (caching, updating the current delegation * point, etc). * Callback from walk_supers for every super state that is interested in @@ -3096,7 +3107,7 @@ processFinished(struct module_qstate* qstate, struct iter_qstate* iq, } /* - * Return priming query results to interestes super querystates. + * Return priming query results to interested super querystates. * * Sets the delegation point and delegation message (not nonRD queries). * This is a callback from walk_supers. diff --git a/usr.sbin/unbound/iterator/iterator.h b/usr.sbin/unbound/iterator/iterator.h index 75aafee475e..841a3643669 100644 --- a/usr.sbin/unbound/iterator/iterator.h +++ b/usr.sbin/unbound/iterator/iterator.h @@ -36,7 +36,7 @@ /** * \file * - * This file contains a module that performs recusive iterative DNS query + * This file contains a module that performs recursive iterative DNS query * processing. */ @@ -62,7 +62,7 @@ struct rbtree_type; /** max number of queries-sent-out. Make sure large NS set does not loop */ #define MAX_SENT_COUNT 32 /** max number of queries for which to perform dnsseclameness detection, - * (rrsigs misssing detection) after that, just pick up that response */ + * (rrsigs missing detection) after that, just pick up that response */ #define DNSSEC_LAME_DETECT_COUNT 4 /** * max number of QNAME minimisation iterations. Limits number of queries for @@ -142,11 +142,11 @@ struct iter_env { enum minimisation_state { /** * (Re)start minimisation. Outgoing QNAME should be set to dp->name. - * State entered on new query or after following refferal or CNAME. + * State entered on new query or after following referral or CNAME. */ INIT_MINIMISE_STATE = 0, /** - * QNAME minimisataion ongoing. Increase QNAME on every iteration. + * QNAME minimisation ongoing. Increase QNAME on every iteration. */ MINIMISE_STATE, /** @@ -184,7 +184,7 @@ enum iter_state { /** * Each time a delegation point changes for a given query or a * query times out and/or wakes up, this state is (re)visited. - * This state is reponsible for iterating through a list of + * This state is responsible for iterating through a list of * nameserver targets. */ QUERYTARGETS_STATE, @@ -378,7 +378,7 @@ struct iter_qstate { struct query_info qinfo_out; /** - * Count number of QNAME minisation iterations. Used to limit number of + * Count number of QNAME minimisation iterations. Used to limit number of * outgoing queries when QNAME minimisation is enabled. */ int minimise_count; @@ -430,7 +430,7 @@ void iter_operate(struct module_qstate* qstate, enum module_ev event, int id, struct outbound_entry* outbound); /** - * Return priming query results to interestes super querystates. + * Return priming query results to interested super querystates. * * Sets the delegation point and delegation message (not nonRD queries). * This is a callback from walk_supers. diff --git a/usr.sbin/unbound/libunbound/context.h b/usr.sbin/unbound/libunbound/context.h index 1761c4d8721..684d11effbc 100644 --- a/usr.sbin/unbound/libunbound/context.h +++ b/usr.sbin/unbound/libunbound/context.h @@ -90,7 +90,7 @@ struct ub_ctx { int dothread; /** next thread number for new threads */ int thr_next_num; - /** if logfile is overriden */ + /** if logfile is overridden */ int logfile_override; /** what logfile to use instead */ FILE* log_out; diff --git a/usr.sbin/unbound/libunbound/libunbound.c b/usr.sbin/unbound/libunbound/libunbound.c index eaa31c71c59..9b4dcab1579 100644 --- a/usr.sbin/unbound/libunbound/libunbound.c +++ b/usr.sbin/unbound/libunbound/libunbound.c @@ -37,7 +37,7 @@ * \file * * This file contains functions to resolve DNS queries and - * validate the answers. Synchonously and asynchronously. + * validate the answers. Synchronously and asynchronously. * */ diff --git a/usr.sbin/unbound/libunbound/libworker.c b/usr.sbin/unbound/libunbound/libworker.c index f6a1d1d1c47..c991d5df3f4 100644 --- a/usr.sbin/unbound/libunbound/libworker.c +++ b/usr.sbin/unbound/libunbound/libworker.c @@ -214,6 +214,7 @@ libworker_setup(struct ub_ctx* ctx, int is_bg, struct ub_event_base* eb) libworker_delete(w); return NULL; } + w->env->worker_base = w->base; if(!w->is_bg || w->is_bg_thread) { lock_basic_lock(&ctx->cfglock); } @@ -232,9 +233,10 @@ libworker_setup(struct ub_ctx* ctx, int is_bg, struct ub_event_base* eb) cfg->do_tcp?cfg->outgoing_num_tcp:0, w->env->infra_cache, w->env->rnd, cfg->use_caps_bits_for_id, ports, numports, cfg->unwanted_threshold, - cfg->outgoing_tcp_mss, - &libworker_alloc_cleanup, w, cfg->do_udp, w->sslctx, + cfg->outgoing_tcp_mss, &libworker_alloc_cleanup, w, + cfg->do_udp || cfg->udp_upstream_without_downstream, w->sslctx, cfg->delay_close, NULL); + w->env->outnet = w->back; if(!w->is_bg || w->is_bg_thread) { lock_basic_unlock(&ctx->cfglock); } @@ -251,6 +253,7 @@ libworker_setup(struct ub_ctx* ctx, int is_bg, struct ub_event_base* eb) w->env->send_query = &libworker_send_query; w->env->detach_subs = &mesh_detach_subs; w->env->attach_sub = &mesh_attach_sub; + w->env->add_sub = &mesh_add_sub; w->env->kill_sub = &mesh_state_delete; w->env->detect_cycle = &mesh_detect_cycle; comm_base_timept(w->base, &w->env->now, &w->env->now_tv); @@ -418,25 +421,6 @@ int libworker_bg(struct ub_ctx* ctx) return UB_NOERROR; } -/** get msg reply struct (in temp region) */ -static struct reply_info* -parse_reply(sldns_buffer* pkt, struct regional* region, struct query_info* qi) -{ - struct reply_info* rep; - struct msg_parse* msg; - if(!(msg = regional_alloc(region, sizeof(*msg)))) { - return NULL; - } - memset(msg, 0, sizeof(*msg)); - sldns_buffer_set_position(pkt, 0); - if(parse_packet(pkt, msg, region) != 0) - return 0; - if(!parse_create_msg(pkt, msg, NULL, qi, &rep, region)) { - return 0; - } - return rep; -} - /** insert canonname */ static int fill_canon(struct ub_result* res, uint8_t* s) @@ -510,7 +494,7 @@ libworker_enter_result(struct ub_result* res, sldns_buffer* buf, struct query_info rq; struct reply_info* rep; res->rcode = LDNS_RCODE_SERVFAIL; - rep = parse_reply(buf, temp, &rq); + rep = parse_reply_in_temp_region(buf, temp, &rq); if(!rep) { log_err("cannot parse buf"); return; /* error parsing buf, or out of memory */ diff --git a/usr.sbin/unbound/libunbound/unbound.h b/usr.sbin/unbound/libunbound/unbound.h index ac747a7cc6c..1b0f54fd2b6 100644 --- a/usr.sbin/unbound/libunbound/unbound.h +++ b/usr.sbin/unbound/libunbound/unbound.h @@ -37,7 +37,7 @@ * \file * * This file contains functions to resolve DNS queries and - * validate the answers. Synchonously and asynchronously. + * validate the answers. Synchronously and asynchronously. * * Several ways to use this interface from an application wishing * to perform (validated) DNS lookups. @@ -177,7 +177,7 @@ struct ub_result { * False, if validation failed or domain queried has no security info. * * It is possible to get a result with no data (havedata is false), - * and secure is true. This means that the non-existance of the data + * and secure is true. This means that the non-existence of the data * was cryptographically proven (with signatures). */ int secure; @@ -623,6 +623,7 @@ struct ub_shm_stat_info { long long ipsecmod; long long respip; long long dnscrypt_shared_secret; + long long dnscrypt_nonce; } mem; }; @@ -742,11 +743,15 @@ struct ub_server_stats { long long num_query_dnscrypt_secret_missed_cache; /** number of dnscrypt shared secret cache entries */ long long shared_secret_cache_count; + /** number of queries which are replays */ + long long num_query_dnscrypt_replay; + /** number of dnscrypt nonces cache entries */ + long long nonce_cache_count; }; /** * Statistics to send over the control pipe when asked - * This struct is made to be memcpied, sent in binary. + * This struct is made to be memcopied, sent in binary. * shm mapped with (number+1) at num_threads+1, with first as total */ struct ub_stats_info { diff --git a/usr.sbin/unbound/services/authzone.c b/usr.sbin/unbound/services/authzone.c index 88beddbf922..35f4cd3d41a 100644 --- a/usr.sbin/unbound/services/authzone.c +++ b/usr.sbin/unbound/services/authzone.c @@ -44,13 +44,21 @@ #include "config.h" #include "services/authzone.h" #include "util/data/dname.h" +#include "util/data/msgparse.h" #include "util/data/msgreply.h" +#include "util/data/msgencode.h" #include "util/data/packed_rrset.h" #include "util/regional.h" #include "util/net_help.h" +#include "util/netevent.h" #include "util/config_file.h" #include "util/log.h" +#include "util/module.h" +#include "util/random.h" #include "services/cache/dns.h" +#include "services/outside_network.h" +#include "services/listen_dnsport.h" +#include "services/mesh.h" #include "sldns/rrdef.h" #include "sldns/pkthdr.h" #include "sldns/sbuffer.h" @@ -64,6 +72,19 @@ #define N3HASHBUFLEN 32 /** max number of CNAMEs we are willing to follow (in one answer) */ #define MAX_CNAME_CHAIN 8 +/** timeout for probe packets for SOA */ +#define AUTH_PROBE_TIMEOUT 100 /* msec */ +/** when to stop with SOA probes (when exponential timeouts exceed this) */ +#define AUTH_PROBE_TIMEOUT_STOP 1000 /* msec */ +/* auth transfer timeout for TCP connections, in msec */ +#define AUTH_TRANSFER_TIMEOUT 10000 /* msec */ + +/** pick up nextprobe task to start waiting to perform transfer actions */ +static void xfr_set_timeout(struct auth_xfer* xfr, struct module_env* env, + int failure); +/** move to sending the probe packets, next if fails. task_probe */ +static void xfr_probe_send_or_end(struct auth_xfer* xfr, + struct module_env* env); /** create new dns_msg */ static struct dns_msg* @@ -246,15 +267,18 @@ msg_add_rrset_ar(struct auth_zone* z, struct regional* region, struct auth_zones* auth_zones_create(void) { + /* TODO: create and put in env in worker and libworker */ struct auth_zones* az = (struct auth_zones*)calloc(1, sizeof(*az)); if(!az) { log_err("out of memory"); return NULL; } rbtree_init(&az->ztree, &auth_zone_cmp); + rbtree_init(&az->xtree, &auth_xfer_cmp); lock_rw_init(&az->lock); lock_protect(&az->lock, &az->ztree, sizeof(az->ztree)); - /* also lock protects the rbnode's in struct auth_zone */ + lock_protect(&az->lock, &az->xtree, sizeof(az->xtree)); + /* also lock protects the rbnode's in struct auth_zone, auth_xfer */ return az; } @@ -285,6 +309,23 @@ int auth_data_cmp(const void* z1, const void* z2) b->namelabs, &m); } +int auth_xfer_cmp(const void* z1, const void* z2) +{ + /* first sort on class, so that hierarchy can be maintained within + * a class */ + struct auth_xfer* a = (struct auth_xfer*)z1; + struct auth_xfer* b = (struct auth_xfer*)z2; + int m; + if(a->dclass != b->dclass) { + if(a->dclass < b->dclass) + return -1; + return 1; + } + /* sorted such that higher zones sort before lower zones (their + * contents) */ + return dname_lab_cmp(a->name, a->namelabs, b->name, b->namelabs, &m); +} + /** delete auth rrset node */ static void auth_rrset_delete(struct auth_rrset* rrset) @@ -374,6 +415,19 @@ auth_zone_find(struct auth_zones* az, uint8_t* nm, size_t nmlen, return (struct auth_zone*)rbtree_search(&az->ztree, &key); } +struct auth_xfer* +auth_xfer_find(struct auth_zones* az, uint8_t* nm, size_t nmlen, + uint16_t dclass) +{ + struct auth_xfer key; + key.node.key = &key; + key.dclass = dclass; + key.name = nm; + key.namelen = nmlen; + key.namelabs = dname_count_labels(nm); + return (struct auth_xfer*)rbtree_search(&az->xtree, &key); +} + /** find an auth zone or sorted less-or-equal, return true if exact */ static int auth_zone_find_less_equal(struct auth_zones* az, uint8_t* nm, size_t nmlen, @@ -437,6 +491,23 @@ auth_zones_find_or_add_zone(struct auth_zones* az, char* name) return z; } +/** find or create xfer zone with name str. caller must have lock on az. + * returns a locked xfer */ +static struct auth_xfer* +auth_zones_find_or_add_xfer(struct auth_zones* az, struct auth_zone* z) +{ + struct auth_xfer* x; + x = auth_xfer_find(az, z->name, z->namelen, z->dclass); + if(!x) { + /* not found, create the zone */ + x = auth_xfer_create(az, z); + lock_basic_lock(&x->lock); + } else { + lock_basic_lock(&x->lock); + } + return x; +} + int auth_zone_set_zonefile(struct auth_zone* z, char* zonefile) { @@ -1052,6 +1123,13 @@ auth_zone_read_zonefile(struct auth_zone* z) in = fopen(z->zonefile, "r"); if(!in) { char* n = sldns_wire2str_dname(z->name, z->namelen); + if(z->zone_is_slave && errno == ENOENT) { + /* we fetch the zone contents later, no file yet */ + verbose(VERB_ALGO, "no zonefile %s for %s", + z->zonefile, n?n:"error"); + free(n); + return 1; + } log_err("cannot open zonefile %s for %s: %s", z->zonefile, n?n:"error", strerror(errno)); free(n); @@ -1185,68 +1263,249 @@ auth_zones_read_zones(struct auth_zones* az) return 1; } -/** set str2list with (zonename, zonefile) config items and create zones */ +/** Find auth_zone SOA and populate the values in xfr(soa values). */ static int -auth_zones_cfg_zonefile(struct auth_zones* az, struct config_str2list* zlist) +xfr_find_soa(struct auth_zone* z, struct auth_xfer* xfr) +{ + struct auth_data* apex; + struct auth_rrset* soa; + struct packed_rrset_data* d; + apex = az_find_name(z, z->name, z->namelen); + if(!apex) return 0; + soa = az_domain_rrset(apex, LDNS_RR_TYPE_SOA); + if(!soa || soa->data->count==0) + return 0; /* no RRset or no RRs in rrset */ + if(soa->data->rr_len[0] < 2+4*5) return 0; /* SOA too short */ + /* SOA record ends with serial, refresh, retry, expiry, minimum, + * as 4 byte fields */ + d = soa->data; + xfr->have_zone = 1; + xfr->serial = sldns_read_uint32(d->rr_data[0]+(d->rr_len[0]-20)); + xfr->retry = sldns_read_uint32(d->rr_data[0]+(d->rr_len[0]-16)); + xfr->refresh = sldns_read_uint32(d->rr_data[0]+(d->rr_len[0]-12)); + xfr->expiry = sldns_read_uint32(d->rr_data[0]+(d->rr_len[0]-8)); + /* soa minimum at d->rr_len[0]-4 */ + return 1; +} + +/** + * Setup auth_xfer zone + * This populates the have_zone, soa values, next_probe and so on times. + * Doesn't do network traffic yet, sets the timeout. + * @param z: locked by caller, and modified for setup + * @param x: locked by caller, and modified, timers and timeouts. + * @param env: module env with time. + * @return false on failure. + */ +static int +auth_xfer_setup(struct auth_zone* z, struct auth_xfer* x, struct module_env* env) +{ + if(!z || !x) return 1; + if(!xfr_find_soa(z, x)) { + return 1; + } + /* nextprobe setup */ + x->task_nextprobe->next_probe = 0; + if(x->have_zone) + x->task_nextprobe->lease_time = *env->now; + /* nothing for probe and transfer tasks */ + return 1; +} + +/** + * Setup all zones + * @param az: auth zones structure + * @param env: module env with time. + * @return false on failure. + */ +static int +auth_zones_setup_zones(struct auth_zones* az, struct module_env* env) { struct auth_zone* z; - while(zlist) { - lock_rw_wrlock(&az->lock); - if(!(z=auth_zones_find_or_add_zone(az, zlist->str))) { - lock_rw_unlock(&az->lock); - return 0; + struct auth_xfer* x; + lock_rw_wrlock(&az->lock); + RBTREE_FOR(z, struct auth_zone*, &az->ztree) { + lock_rw_wrlock(&z->lock); + x = auth_xfer_find(az, z->name, z->namelen, z->dclass); + if(x) { + lock_basic_lock(&x->lock); } - lock_rw_unlock(&az->lock); - if(!auth_zone_set_zonefile(z, zlist->str2)) { + if(!auth_xfer_setup(z, x, env)) { + if(x) { + lock_basic_unlock(&x->lock); + } lock_rw_unlock(&z->lock); + lock_rw_unlock(&az->lock); return 0; } + if(x) { + lock_basic_unlock(&x->lock); + } lock_rw_unlock(&z->lock); - zlist = zlist->next; } + lock_rw_unlock(&az->lock); return 1; } -/** set str2list with (zonename, fallback) config items and create zones */ +/** set config items and create zones */ static int -auth_zones_cfg_fallback(struct auth_zones* az, struct config_str2list* zlist) +auth_zones_cfg(struct auth_zones* az, struct config_auth* c) { struct auth_zone* z; - while(zlist) { - lock_rw_wrlock(&az->lock); - if(!(z=auth_zones_find_or_add_zone(az, zlist->str))) { + struct auth_xfer* x = NULL; + + /* create zone */ + lock_rw_wrlock(&az->lock); + if(!(z=auth_zones_find_or_add_zone(az, c->name))) { + lock_rw_unlock(&az->lock); + return 0; + } + if(c->masters || c->urls) { + if(!(x=auth_zones_find_or_add_xfer(az, z))) { lock_rw_unlock(&az->lock); + lock_rw_unlock(&z->lock); return 0; } - lock_rw_unlock(&az->lock); - if(!auth_zone_set_fallback(z, zlist->str2)) { + } + if(c->for_downstream) + az->have_downstream = 1; + lock_rw_unlock(&az->lock); + + /* set options */ + if(!auth_zone_set_zonefile(z, c->zonefile)) { + if(x) { + lock_basic_unlock(&x->lock); + } + lock_rw_unlock(&z->lock); + return 0; + } + z->for_downstream = c->for_downstream; + z->for_upstream = c->for_upstream; + /* TODO fallback option */ + //if(!auth_zone_set_fallback(z, zlist->str2)) { + /* TODO other options */ + + /* xfer zone */ + if(x) { + z->zone_is_slave = 1; + /* set options on xfer zone */ + if(!xfer_set_masters(&x->task_probe->masters, c)) { + lock_basic_unlock(&x->lock); lock_rw_unlock(&z->lock); return 0; } - lock_rw_unlock(&z->lock); - zlist = zlist->next; + if(!xfer_set_masters(&x->task_transfer->masters, c)) { + lock_basic_unlock(&x->lock); + lock_rw_unlock(&z->lock); + return 0; + } + lock_basic_unlock(&x->lock); } + + lock_rw_unlock(&z->lock); return 1; } -int auth_zones_apply_config(struct auth_zones* az, struct config_file* cfg) +int auth_zones_apply_cfg(struct auth_zones* az, struct config_file* cfg, + int setup, struct module_env* env) { - (void)cfg; - /* TODO cfg str2lists */ - /* create config items for - * auth-zone: name: "example.com" - * zonefile: "zones/example.com" - * fallback: yes - */ - if(!auth_zones_cfg_zonefile(az, NULL /*cfg->auth_zones*/)) - return 0; - if(!auth_zones_cfg_fallback(az, NULL /*cfg->auth_zones*/)) - return 0; + struct config_auth* p; + for(p = cfg->auths; p; p = p->next) { + if(!p->name || p->name[0] == 0) { + log_warn("auth-zone without a name, skipped"); + continue; + } + if(!auth_zones_cfg(az, p)) { + log_err("cannot config auth zone %s", p->name); + return 0; + } + } if(!auth_zones_read_zones(az)) return 0; + if(setup) { + if(!auth_zones_setup_zones(az, env)) + return 0; + } return 1; } +/** delete chunks + * @param at: transfer structure with chunks list. The chunks and their + * data are freed. + */ +void +auth_chunks_delete(struct auth_transfer* at) +{ + if(at->chunks_first) { + struct auth_chunk* c, *cn; + c = at->chunks_first; + while(c) { + cn = c->next; + free(c->data); + free(c); + c = cn; + } + } + at->chunks_first = NULL; + at->chunks_last = NULL; +} + +/** free master addr list */ +static void +auth_free_master_addrs(struct auth_addr* list) +{ + struct auth_addr *n; + while(list) { + n = list->next; + free(list); + list = n; + } +} + +/** free the masters list */ +static void +auth_free_masters(struct auth_master* list) +{ + struct auth_master* n; + while(list) { + n = list->next; + auth_free_master_addrs(list->list); + free(list->host); + free(list->file); + free(list); + list = n; + } +} + +/** delete auth xfer structure + * @param xfr: delete this xfer and its tasks. + */ +void +auth_xfer_delete(struct auth_xfer* xfr) +{ + if(!xfr) return; + lock_basic_destroy(&xfr->lock); + free(xfr->name); + if(xfr->task_nextprobe) { + comm_timer_delete(xfr->task_nextprobe->timer); + free(xfr->task_nextprobe); + } + if(xfr->task_probe) { + auth_free_masters(xfr->task_probe->masters); + comm_point_delete(xfr->task_probe->cp); + free(xfr->task_probe); + } + if(xfr->task_transfer) { + auth_free_masters(xfr->task_transfer->masters); + comm_point_delete(xfr->task_transfer->cp); + if(xfr->task_transfer->chunks_first) { + auth_chunks_delete(xfr->task_transfer); + } + free(xfr->task_transfer); + } + free(xfr); +} + /** helper traverse to delete zones */ static void auth_zone_del(rbnode_type* n, void* ATTR_UNUSED(arg)) @@ -1255,11 +1514,20 @@ auth_zone_del(rbnode_type* n, void* ATTR_UNUSED(arg)) auth_zone_delete(z); } +/** helper traverse to delete xfer zones */ +static void +auth_xfer_del(rbnode_type* n, void* ATTR_UNUSED(arg)) +{ + struct auth_xfer* z = (struct auth_xfer*)n->key; + auth_xfer_delete(z); +} + void auth_zones_delete(struct auth_zones* az) { if(!az) return; lock_rw_destroy(&az->lock); traverse_postorder(&az->ztree, auth_zone_del, NULL); + traverse_postorder(&az->xtree, auth_xfer_del, NULL); free(az); } @@ -1848,7 +2116,7 @@ az_nsec3_find_cover(struct auth_zone* z, uint8_t* nm, size_t nmlen, !az_domain_rrset(node, LDNS_RR_TYPE_NSEC3)) { node = (struct auth_data*)rbtree_previous(&node->node); } - if((rbnode_type*)node == RBTREE_NULL) + if((rbnode_type*)node == RBTREE_NULL) node = NULL; return node; } @@ -2348,6 +2616,8 @@ int auth_zones_lookup(struct auth_zones* az, struct query_info* qinfo, { int r; struct auth_zone* z; + /* TODO: in iterator, after cache lookup, before network lookup, + * call this to get answer */ /* find the zone that should contain the answer. */ lock_rw_rdlock(&az->lock); @@ -2367,3 +2637,1487 @@ int auth_zones_lookup(struct auth_zones* az, struct query_info* qinfo, lock_rw_unlock(&z->lock); return r; } + +/** encode auth answer */ +static void +auth_answer_encode(struct query_info* qinfo, struct module_env* env, + struct edns_data* edns, sldns_buffer* buf, struct regional* temp, + struct dns_msg* msg) +{ + uint16_t udpsize; + udpsize = edns->udp_size; + edns->edns_version = EDNS_ADVERTISED_VERSION; + edns->udp_size = EDNS_ADVERTISED_SIZE; + edns->ext_rcode = 0; + edns->bits &= EDNS_DO; + + if(!inplace_cb_reply_local_call(env, qinfo, NULL, msg->rep, + (int)FLAGS_GET_RCODE(msg->rep->flags), edns, temp) + || !reply_info_answer_encode(qinfo, msg->rep, + *(uint16_t*)sldns_buffer_begin(buf), + sldns_buffer_read_u16_at(buf, 2), + buf, 0, 0, temp, udpsize, edns, + (int)(edns->bits&EDNS_DO), 0)) { + error_encode(buf, (LDNS_RCODE_SERVFAIL|BIT_AA), qinfo, + *(uint16_t*)sldns_buffer_begin(buf), + sldns_buffer_read_u16_at(buf, 2), edns); + } +} + +/** encode auth error answer */ +static void +auth_error_encode(struct query_info* qinfo, struct module_env* env, + struct edns_data* edns, sldns_buffer* buf, struct regional* temp, + int rcode) +{ + edns->edns_version = EDNS_ADVERTISED_VERSION; + edns->udp_size = EDNS_ADVERTISED_SIZE; + edns->ext_rcode = 0; + edns->bits &= EDNS_DO; + + if(!inplace_cb_reply_local_call(env, qinfo, NULL, NULL, + rcode, edns, temp)) + edns->opt_list = NULL; + error_encode(buf, rcode|BIT_AA, qinfo, + *(uint16_t*)sldns_buffer_begin(buf), + sldns_buffer_read_u16_at(buf, 2), edns); +} + +int auth_zones_answer(struct auth_zones* az, struct module_env* env, + struct query_info* qinfo, struct edns_data* edns, struct sldns_buffer* buf, + struct regional* temp) +{ + /* TODO: in handle after localzones, before cache, if az != NULL, + * call this function to answer downstream */ + struct dns_msg* msg = NULL; + struct auth_zone* z; + int r; + int fallback = 0; + + lock_rw_rdlock(&az->lock); + if(!az->have_downstream) { + /* no downstream auth zones */ + lock_rw_unlock(&az->lock); + return 0; + } + z = auth_zones_find_zone(az, qinfo); + if(!z) { + /* no zone above it */ + lock_rw_unlock(&az->lock); + return 0; + } + lock_rw_unlock(&az->lock); + if(!z->for_downstream) { + lock_rw_unlock(&z->lock); + return 0; + } + + /* answer it from zone z */ + r = auth_zone_generate_answer(z, qinfo, temp, &msg, &fallback); + lock_rw_unlock(&z->lock); + if(fallback) { + /* fallback to regular answering (recursive) */ + return 0; + } + + /* encode answer */ + if(!r) + auth_error_encode(qinfo, env, edns, buf, temp, + LDNS_RCODE_SERVFAIL); + else auth_answer_encode(qinfo, env, edns, buf, temp, msg); + + return 1; +} + +/** set a zone expired */ +static void +auth_xfer_set_expired(struct auth_xfer* xfr, struct module_env* env, + int expired) +{ + struct auth_zone* z; + + /* expire xfr */ + lock_basic_lock(&xfr->lock); + xfr->zone_expired = expired; + lock_basic_unlock(&xfr->lock); + + /* find auth_zone */ + lock_rw_rdlock(&env->auth_zones->lock); + z = auth_zone_find(env->auth_zones, xfr->name, xfr->namelen, + xfr->dclass); + if(!z) { + lock_rw_unlock(&env->auth_zones->lock); + return; + } + lock_rw_wrlock(&z->lock); + lock_rw_unlock(&env->auth_zones->lock); + + /* expire auth_zone */ + z->zone_expired = expired; + lock_rw_unlock(&z->lock); +} + +/** the current transfer has finished, apply the results. + * set timer for future probe. See if zone is expired now. */ +void +xfr_master_transferresult(struct auth_xfer* xfr) +{ + (void)xfr; + /* TODO */ +} + +/** the current probe has finished, inspect the results. + * move on to the next master or start a transfer, or at last master, + * set timer for future probe. See if zone is expired now. */ +void +xfr_master_proberesult(struct auth_xfer* xfr) +{ + (void)xfr; + /* TODO */ +} + +/** with current master selected, start the probe, or transfer */ +int +xfr_master_start(struct auth_xfer* xfr) +{ + (void)xfr; + /* TODO */ + return 0; +} + +/** find master (from notify or probe) in list of masters */ +static struct auth_master* +find_master_by_host(struct auth_master* list, char* host) +{ + struct auth_master* p; + for(p=list; p; p=p->next) { + if(strcmp(p->host, host) == 0) + return p; + } + return NULL; +} + +/** delete the looked up auth_addrs for all the masters in the list */ +static void +xfr_masterlist_free_addrs(struct auth_master* list) +{ + struct auth_master* m; + for(m=list; m; m=m->next) { + if(m->list) { + auth_free_master_addrs(m->list); + m->list = NULL; + } + } +} + +/** start the lookups for task_transfer */ +static void +xfr_transfer_start_lookups(struct auth_xfer* xfr) +{ + /* delete all the looked up addresses in the list */ + xfr_masterlist_free_addrs(xfr->task_transfer->masters); + + /* start lookup at the first master */ + xfr->task_transfer->lookup_target = xfr->task_transfer->masters; + xfr->task_transfer->lookup_aaaa = 0; +} + +/** move to the next lookup of hostname for task_transfer */ +static void +xfr_transfer_move_to_next_lookup(struct auth_xfer* xfr, struct module_env* env) +{ + if(!xfr->task_transfer->lookup_target) + return; /* already at end of list */ + if(!xfr->task_transfer->lookup_aaaa && env->cfg->do_ip6) { + /* move to lookup AAAA */ + xfr->task_transfer->lookup_aaaa = 1; + return; + } + xfr->task_transfer->lookup_target = + xfr->task_transfer->lookup_target->next; + xfr->task_transfer->lookup_aaaa = 0; + if(!env->cfg->do_ip4 && xfr->task_transfer->lookup_target!=NULL) + xfr->task_transfer->lookup_aaaa = 1; +} + +/** start the lookups for task_probe */ +static void +xfr_probe_start_lookups(struct auth_xfer* xfr) +{ + /* delete all the looked up addresses in the list */ + xfr_masterlist_free_addrs(xfr->task_probe->masters); + + /* start lookup at the first master */ + xfr->task_probe->lookup_target = xfr->task_probe->masters; + xfr->task_probe->lookup_aaaa = 0; +} + +/** move to the next lookup of hostname for task_probe */ +static void +xfr_probe_move_to_next_lookup(struct auth_xfer* xfr, struct module_env* env) +{ + if(!xfr->task_probe->lookup_target) + return; /* already at end of list */ + if(!xfr->task_probe->lookup_aaaa && env->cfg->do_ip6) { + /* move to lookup AAAA */ + xfr->task_probe->lookup_aaaa = 1; + return; + } + xfr->task_probe->lookup_target = xfr->task_probe->lookup_target->next; + xfr->task_probe->lookup_aaaa = 0; + if(!env->cfg->do_ip4 && xfr->task_probe->lookup_target!=NULL) + xfr->task_probe->lookup_aaaa = 1; +} + +/** start the iteration of the task_transfer list of masters */ +static void +xfr_transfer_start_list(struct auth_xfer* xfr, struct auth_master* spec) +{ + if(spec) { + xfr->task_transfer->scan_specific = find_master_by_host( + xfr->task_transfer->masters, spec->host); + if(xfr->task_transfer->scan_specific) { + xfr->task_transfer->scan_target = NULL; + xfr->task_transfer->scan_addr = NULL; + if(xfr->task_transfer->scan_specific->list) + xfr->task_transfer->scan_addr = + xfr->task_transfer->scan_specific->list; + return; + } + } + /* no specific (notified) host to scan */ + xfr->task_transfer->scan_specific = NULL; + xfr->task_transfer->scan_addr = NULL; + /* pick up first scan target */ + xfr->task_transfer->scan_target = xfr->task_transfer->masters; + if(xfr->task_transfer->scan_target && xfr->task_transfer-> + scan_target->list) + xfr->task_transfer->scan_addr = + xfr->task_transfer->scan_target->list; +} + +/** start the iteration of the task_probe list of masters */ +static void +xfr_probe_start_list(struct auth_xfer* xfr, struct auth_master* spec) +{ + if(spec) { + xfr->task_probe->scan_specific = find_master_by_host( + xfr->task_probe->masters, spec->host); + if(xfr->task_probe->scan_specific) { + xfr->task_probe->scan_target = NULL; + xfr->task_probe->scan_addr = NULL; + if(xfr->task_probe->scan_specific->list) + xfr->task_probe->scan_addr = + xfr->task_probe->scan_specific->list; + return; + } + } + /* no specific (notified) host to scan */ + xfr->task_probe->scan_specific = NULL; + xfr->task_probe->scan_addr = NULL; + /* pick up first scan target */ + xfr->task_probe->scan_target = xfr->task_probe->masters; + if(xfr->task_probe->scan_target && xfr->task_probe->scan_target->list) + xfr->task_probe->scan_addr = + xfr->task_probe->scan_target->list; +} + +/** pick up the master that is being scanned right now, task_transfer */ +static struct auth_master* +xfr_transfer_current_master(struct auth_xfer* xfr) +{ + if(xfr->task_transfer->scan_specific) + return xfr->task_transfer->scan_specific; + return xfr->task_transfer->scan_target; +} + +/** pick up the master that is being scanned right now, task_probe */ +static struct auth_master* +xfr_probe_current_master(struct auth_xfer* xfr) +{ + if(xfr->task_probe->scan_specific) + return xfr->task_probe->scan_specific; + return xfr->task_probe->scan_target; +} + +/** true if at end of list, task_transfer */ +static int +xfr_transfer_end_of_list(struct auth_xfer* xfr) +{ + return !xfr->task_transfer->scan_specific && + !xfr->task_transfer->scan_target; +} + +/** true if at end of list, task_probe */ +static int +xfr_probe_end_of_list(struct auth_xfer* xfr) +{ + return !xfr->task_probe->scan_specific && !xfr->task_probe->scan_target; +} + +/** move to next master in list, task_transfer */ +static int +xfr_transfer_nextmaster(struct auth_xfer* xfr) +{ + /* TODO: no return value */ + if(!xfr->task_transfer->scan_specific && + !xfr->task_transfer->scan_target) + return 0; + if(xfr->task_transfer->scan_addr) { + xfr->task_transfer->scan_addr = + xfr->task_transfer->scan_addr->next; + if(xfr->task_transfer->scan_addr) + return 1; + } + if(xfr->task_transfer->scan_specific) { + xfr->task_transfer->scan_specific = NULL; + xfr->task_transfer->scan_target = xfr->task_transfer->masters; + return 1; + } + if(!xfr->task_transfer->scan_target) + return 0; + if(!xfr->task_transfer->scan_target->next) + return 0; + xfr->task_transfer->scan_target = xfr->task_transfer->scan_target->next; + return 1; +} + +/** move to next master in list, task_probe */ +static int +xfr_probe_nextmaster(struct auth_xfer* xfr) +{ + /* TODO: no return value */ + if(!xfr->task_probe->scan_specific && !xfr->task_probe->scan_target) + return 0; + if(xfr->task_probe->scan_addr) { + xfr->task_probe->scan_addr = xfr->task_probe->scan_addr->next; + if(xfr->task_probe->scan_addr) + return 1; + } + if(xfr->task_probe->scan_specific) { + xfr->task_probe->scan_specific = NULL; + xfr->task_probe->scan_target = xfr->task_probe->masters; + return 1; + } + if(!xfr->task_probe->scan_target) + return 0; + if(!xfr->task_probe->scan_target->next) + return 0; + xfr->task_probe->scan_target = xfr->task_probe->scan_target->next; + return 1; +} + +/** create fd to send to this master */ +static int +xfr_fd_for_master(struct module_env* env, struct sockaddr_storage* to_addr, + socklen_t to_addrlen, char* host) +{ + struct sockaddr_storage* addr; + socklen_t addrlen; + int i; + int try; + + /* select interface */ + if(addr_is_ip6(to_addr, to_addrlen)) { + if(env->outnet->num_ip6 == 0) { + verbose(VERB_QUERY, "need ipv6 to send, but no ipv6 outgoing interfaces, for %s", host); + return -1; + } + i = ub_random_max(env->rnd, env->outnet->num_ip6); + addr = &env->outnet->ip6_ifs[i].addr; + addrlen = env->outnet->ip6_ifs[i].addrlen; + } else { + if(env->outnet->num_ip4 == 0) { + verbose(VERB_QUERY, "need ipv4 to send, but no ipv4 outgoing interfaces, for %s", host); + return -1; + } + i = ub_random_max(env->rnd, env->outnet->num_ip4); + addr = &env->outnet->ip4_ifs[i].addr; + addrlen = env->outnet->ip4_ifs[i].addrlen; + } + + /* create fd */ + for(try = 0; try<1000; try++) { + int freebind = 0; + int noproto = 0; + int inuse = 0; + int port = ub_random(env->rnd)&0xffff; + int fd = -1; + if(addr_is_ip6(to_addr, to_addrlen)) { + struct sockaddr_in6 sa = *(struct sockaddr_in6*)addr; + sa.sin6_port = (in_port_t)htons((uint16_t)port); + fd = create_udp_sock(AF_INET6, SOCK_DGRAM, + (struct sockaddr*)&sa, addrlen, 1, &inuse, &noproto, + 0, 0, 0, NULL, 0, freebind, 0); + } else { + struct sockaddr_in* sa = (struct sockaddr_in*)addr; + sa->sin_port = (in_port_t)htons((uint16_t)port); + fd = create_udp_sock(AF_INET, SOCK_DGRAM, + (struct sockaddr*)&sa, addrlen, 1, &inuse, &noproto, + 0, 0, 0, NULL, 0, freebind, 0); + } + if(fd != -1) { + return fd; + } + if(!inuse) { + return -1; + } + } + /* too many tries */ + log_err("cannot send probe, ports are in use"); + return -1; +} + +/** create probe packet for xfr */ +static void +xfr_create_probe_packet(struct auth_xfer* xfr, sldns_buffer* buf, int soa, + uint16_t id) +{ + struct query_info qinfo; + uint32_t serial; + int have_zone; + lock_basic_lock(&xfr->lock); + have_zone = xfr->have_zone; + serial = xfr->serial; + lock_basic_unlock(&xfr->lock); + + memset(&qinfo, 0, sizeof(qinfo)); + qinfo.qname = xfr->name; + qinfo.qname_len = xfr->namelen; + if(soa) { + qinfo.qtype = LDNS_RR_TYPE_SOA; + } else { + qinfo.qtype = LDNS_RR_TYPE_IXFR; + if(!have_zone) + qinfo.qtype = LDNS_RR_TYPE_AXFR; + } + qinfo.qclass = xfr->dclass; + qinfo_query_encode(buf, &qinfo); + sldns_buffer_write_at(buf, 0, &id, 2); + + /* append serial for IXFR */ + if(qinfo.qtype == LDNS_RR_TYPE_IXFR) { + sldns_buffer_set_position(buf, sldns_buffer_limit(buf)); + /* auth section count 1 */ + sldns_buffer_write_u16_at(buf, 1, LDNS_NSCOUNT_OFF); + /* write SOA */ + sldns_buffer_write_u8(buf, 0xC0); /* compressed ptr to qname */ + sldns_buffer_write_u8(buf, 0x0C); + sldns_buffer_write_u16(buf, qinfo.qtype); + sldns_buffer_write_u16(buf, qinfo.qclass); + sldns_buffer_write_u32(buf, 0); /* ttl */ + sldns_buffer_write_u16(buf, 22); /* rdata length */ + sldns_buffer_write_u8(buf, 0); /* . */ + sldns_buffer_write_u8(buf, 0); /* . */ + sldns_buffer_write_u32(buf, serial); /* serial */ + sldns_buffer_write_u32(buf, 0); /* refresh */ + sldns_buffer_write_u32(buf, 0); /* retry */ + sldns_buffer_write_u32(buf, 0); /* expire */ + sldns_buffer_write_u32(buf, 0); /* minimum */ + sldns_buffer_flip(buf); + } +} + +/** check if returned packet is OK */ +static int +check_packet_ok(sldns_buffer* pkt, uint16_t qtype, struct auth_xfer* xfr, + uint32_t* serial) +{ + uint16_t id; + /* parse to see if packet worked, valid reply */ + + /* check serial number of SOA */ + if(sldns_buffer_limit(pkt) < LDNS_HEADER_SIZE) + return 0; + + /* check ID */ + sldns_buffer_read_at(pkt, 0, &id, 2); + if(id != xfr->task_probe->id) + return 0; + + /* check flag bits and rcode */ + if(!LDNS_QR_WIRE(sldns_buffer_begin(pkt))) + return 0; + if(LDNS_OPCODE_WIRE(sldns_buffer_begin(pkt)) != LDNS_PACKET_QUERY) + return 0; + if(LDNS_RCODE_WIRE(sldns_buffer_begin(pkt)) != LDNS_RCODE_NOERROR) + return 0; + + /* check qname */ + if(LDNS_QDCOUNT(sldns_buffer_begin(pkt)) != 1) + return 0; + sldns_buffer_skip(pkt, LDNS_HEADER_SIZE); + if(sldns_buffer_remaining(pkt) < xfr->namelen) + return 0; + if(query_dname_compare(sldns_buffer_current(pkt), xfr->name) != 0) + return 0; + sldns_buffer_skip(pkt, (ssize_t)xfr->namelen); + + /* check qtype, qclass */ + if(sldns_buffer_remaining(pkt) < 4) + return 0; + if(sldns_buffer_read_u16(pkt) != qtype) + return 0; + if(sldns_buffer_read_u16(pkt) != xfr->dclass) + return 0; + + if(serial) { + uint16_t rdlen; + /* read serial number, from answer section SOA */ + if(LDNS_ANCOUNT(sldns_buffer_begin(pkt)) == 0) + return 0; + /* read from first record SOA record */ + if(sldns_buffer_remaining(pkt) < 1) + return 0; + if(dname_pkt_compare(pkt, sldns_buffer_current(pkt), + xfr->name) != 0) + return 0; + if(!pkt_dname_len(pkt)) + return 0; + /* type, class, ttl, rdatalen */ + if(sldns_buffer_remaining(pkt) < 4+4+2) + return 0; + if(sldns_buffer_read_u16(pkt) != qtype) + return 0; + if(sldns_buffer_read_u16(pkt) != xfr->dclass) + return 0; + sldns_buffer_skip(pkt, 4); /* ttl */ + rdlen = sldns_buffer_read_u16(pkt); + if(sldns_buffer_remaining(pkt) < rdlen) + return 0; + if(sldns_buffer_remaining(pkt) < 1) + return 0; + if(!pkt_dname_len(pkt)) /* soa name */ + return 0; + if(sldns_buffer_remaining(pkt) < 1) + return 0; + if(!pkt_dname_len(pkt)) /* soa name */ + return 0; + if(sldns_buffer_remaining(pkt) < 20) + return 0; + *serial = sldns_buffer_read_u32(pkt); + } + return 1; +} + +/** see if the serial means the zone has to be updated, i.e. the serial + * is newer than the zone serial, or we have no zone */ +static int +xfr_serial_means_update(struct auth_xfer* xfr, uint32_t serial) +{ + uint32_t zserial; + int have_zone, zone_expired; + lock_basic_lock(&xfr->lock); + zserial = xfr->serial; + have_zone = xfr->have_zone; + zone_expired = xfr->zone_expired; + lock_basic_unlock(&xfr->lock); + + if(!have_zone) + return 1; /* no zone, anything is better */ + if(zone_expired) + return 1; /* expired, the sent serial is better than expired + data */ + if(compare_serial(zserial, serial) < 0) + return 1; /* our serial is smaller than the sent serial, + the data is newer, fetch it */ + return 0; +} + +/** disown task_transfer. caller must hold xfr.lock */ +static void +xfr_transfer_disown(struct auth_xfer* xfr) +{ + /* remove the commpoint */ + comm_point_delete(xfr->task_transfer->cp); + xfr->task_transfer->cp = NULL; + /* we don't own this item anymore */ + xfr->task_transfer->worker = NULL; + xfr->task_transfer->env = NULL; +} + +/** lookup a host name for its addresses, if needed */ +static int +xfr_transfer_lookup_host(struct auth_xfer* xfr, struct module_env* env) +{ + struct sockaddr_storage addr; + socklen_t addrlen = 0; + struct auth_master* master = xfr->task_transfer->lookup_target; + struct query_info qinfo; + uint16_t qflags = BIT_RD; + uint8_t dname[LDNS_MAX_DOMAINLEN+1]; + struct edns_data edns; + sldns_buffer* buf = env->scratch_buffer; + if(!master) return 0; + if(extstrtoaddr(master->host, &addr, &addrlen)) { + /* not needed, host is in IP addr format */ + return 0; + } + + /* use mesh_new_callback to probe for non-addr hosts, + * and then wait for them to be looked up (in cache, or query) */ + qinfo.qname_len = sizeof(dname); + if(sldns_str2wire_dname_buf(master->host, dname, &qinfo.qname_len) + != 0) { + log_err("cannot parse host name of master %s", master->host); + return 0; + } + qinfo.qname = dname; + qinfo.qclass = xfr->dclass; + qinfo.qtype = LDNS_RR_TYPE_A; + if(xfr->task_transfer->lookup_aaaa) + qinfo.qtype = LDNS_RR_TYPE_AAAA; + qinfo.local_alias = NULL; + if(verbosity >= VERB_ALGO) { + char buf[512]; + char buf2[LDNS_MAX_DOMAINLEN+1]; + dname_str(xfr->name, buf2); + snprintf(buf, sizeof(buf), "auth zone %s: master lookup" + " for task_transfer", buf2); + log_query_info(VERB_ALGO, buf, &qinfo); + } + edns.edns_present = 1; + edns.ext_rcode = 0; + edns.edns_version = 0; + edns.bits = EDNS_DO; + edns.opt_list = NULL; + if(sldns_buffer_capacity(buf) < 65535) + edns.udp_size = (uint16_t)sldns_buffer_capacity(buf); + else edns.udp_size = 65535; + + if(!mesh_new_callback(env->mesh, &qinfo, qflags, &edns, buf, 0, + &auth_xfer_transfer_lookup_callback, xfr)) { + log_err("out of memory lookup up master %s", master->host); + free(qinfo.qname); + return 0; + } + free(qinfo.qname); + return 1; +} + +/** initiate TCP to the target and fetch zone. + * returns true if that was successfully started, and timeout setup. */ +static int +xfr_transfer_init_fetch(struct auth_xfer* xfr, struct module_env* env) +{ + struct sockaddr_storage addr; + socklen_t addrlen = 0; + struct auth_master* master = xfr->task_transfer->master; + if(!master) return 0; + + /* get master addr */ + if(xfr->task_transfer->scan_addr) { + addrlen = xfr->task_transfer->scan_addr->addrlen; + memmove(&addr, &xfr->task_transfer->scan_addr->addr, addrlen); + } else { + if(!extstrtoaddr(master->host, &addr, &addrlen)) { + char zname[255+1]; + dname_str(xfr->name, zname); + log_err("%s: cannot parse master %s", zname, + master->host); + return 0; + } + } + + /* remove previous TCP connection (if any) */ + if(xfr->task_transfer->cp) { + comm_point_delete(xfr->task_transfer->cp); + xfr->task_transfer->cp = NULL; + } + + /* connect on fd */ + if(!xfr->task_transfer->cp) { + int fd = outnet_get_tcp_fd(&addr, addrlen, env->cfg->tcp_mss); + if(fd == -1) { + char zname[255+1]; + dname_str(xfr->name, zname); + verbose(VERB_ALGO, "cannot create fd for " + "xfr %s to %s", zname, master->host); + return 0; + } + fd_set_nonblock(fd); + if(!outnet_tcp_connect(fd, &addr, addrlen)) { + /* outnet_tcp_connect has closed fd on error for us */ + char zname[255+1]; + dname_str(xfr->name, zname); + verbose(VERB_ALGO, "cannot tcp connect() for" + "xfr %s to %s", zname, master->host); + return 0; + } + + xfr->task_transfer->cp = comm_point_create_tcp_out( + env->worker_base, 65552, + auth_xfer_transfer_tcp_callback, xfr); + if(!xfr->task_transfer->cp) { + close(fd); + log_err("malloc failure"); + return 0; + } + xfr->task_transfer->cp->repinfo.addrlen = addrlen; + memcpy(&xfr->task_transfer->cp->repinfo.addr, &addr, addrlen); + /* set timeout on TCP connection */ + comm_point_start_listening(xfr->task_transfer->cp, fd, + AUTH_TRANSFER_TIMEOUT); + } + + /* set the packet to be written */ + /* create new ID */ + xfr->task_transfer->id = (uint16_t)(ub_random(env->rnd)&0xffff); + xfr_create_probe_packet(xfr, xfr->task_transfer->cp->buffer, 0, + xfr->task_transfer->id); + + return 1; +} + +/** perform next lookup, next transfer TCP, or end and resume wait time task */ +static void +xfr_transfer_nexttarget_or_end(struct auth_xfer* xfr, struct module_env* env) +{ + log_assert(xfr->task_transfer->worker == env->worker); + + /* are we performing lookups? */ + while(xfr->task_transfer->lookup_target) { + if(xfr_transfer_lookup_host(xfr, env)) { + /* wait for lookup to finish, + * note that the hostname may be in unbound's cache + * and we may then get an instant cache response, + * and that calls the callback just like a full + * lookup and lookup failures also call callback */ + return; + } + xfr_transfer_move_to_next_lookup(xfr, env); + } + + /* initiate TCP and fetch the zone from the master */ + /* and set timeout on it */ + while(!xfr_transfer_end_of_list(xfr)) { + xfr->task_transfer->master = xfr_transfer_current_master(xfr); + if(xfr_transfer_init_fetch(xfr, env)) { + /* successfully started, wait for callback */ + return; + } + /* failed to fetch, next master */ + if(!xfr_transfer_nextmaster(xfr)) { + break; + } + } + + lock_basic_lock(&xfr->lock); + /* we failed to fetch the zone, move to wait task + * use the shorter retry timeout */ + xfr_transfer_disown(xfr); + + /* pick up the nextprobe task and wait */ + xfr_set_timeout(xfr, env, 1); + lock_basic_unlock(&xfr->lock); +} + +/** add addrs from A or AAAA rrset to the master */ +static void +xfr_master_add_addrs(struct auth_master* m, struct ub_packed_rrset_key* rrset, + uint16_t rrtype) +{ + size_t i; + struct packed_rrset_data* data; + if(!m || !rrset) return; + data = (struct packed_rrset_data*)rrset->entry.data; + for(i=0; i<data->count; i++) { + struct auth_addr* a; + size_t len = data->rr_len[i] - 2; + uint8_t* rdata = data->rr_data[i]+2; + if(rrtype == LDNS_RR_TYPE_A && len != INET_SIZE) + continue; /* wrong length for A */ + if(rrtype == LDNS_RR_TYPE_AAAA && len != INET6_SIZE) + continue; /* wrong length for AAAA */ + + /* add and alloc it */ + a = (struct auth_addr*)calloc(1, sizeof(*a)); + if(!a) { + log_err("out of memory"); + return; + } + if(rrtype == LDNS_RR_TYPE_A) { + struct sockaddr_in* sa; + a->addrlen = (socklen_t)sizeof(*sa); + sa = (struct sockaddr_in*)&a->addr; + sa->sin_family = AF_INET; + sa->sin_port = (in_port_t)htons(UNBOUND_DNS_PORT); + memmove(&sa->sin_addr, rdata, INET_SIZE); + } else { + struct sockaddr_in6* sa; + a->addrlen = (socklen_t)sizeof(*sa); + sa = (struct sockaddr_in6*)&a->addr; + sa->sin6_family = AF_INET6; + sa->sin6_port = (in_port_t)htons(UNBOUND_DNS_PORT); + memmove(&sa->sin6_addr, rdata, INET6_SIZE); + } + /* append to list */ + a->next = m->list; + m->list = a; + } +} + +/** callback for task_transfer lookup of host name, of A or AAAA */ +void auth_xfer_transfer_lookup_callback(void* arg, int rcode, sldns_buffer* buf, + enum sec_status ATTR_UNUSED(sec), char* ATTR_UNUSED(why_bogus)) +{ + struct auth_xfer* xfr = (struct auth_xfer*)arg; + struct module_env* env; + log_assert(xfr->task_transfer); + env = xfr->task_transfer->env; + + /* process result */ + if(rcode == LDNS_RCODE_NOERROR) { + uint16_t wanted_qtype = LDNS_RR_TYPE_A; + struct regional* temp = env->scratch; + struct query_info rq; + struct reply_info* rep; + if(xfr->task_transfer->lookup_aaaa) + wanted_qtype = LDNS_RR_TYPE_AAAA; + memset(&rq, 0, sizeof(rq)); + rep = parse_reply_in_temp_region(buf, temp, &rq); + if(rep && rq.qtype == wanted_qtype && + FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NOERROR) { + /* parsed successfully */ + struct ub_packed_rrset_key* answer = + reply_find_answer_rrset(&rq, rep); + if(answer) { + xfr_master_add_addrs(xfr->task_transfer-> + lookup_target, answer, wanted_qtype); + } + } + } + + /* move to lookup AAAA after A lookup, move to next hostname lookup, + * or move to fetch the zone, or, if nothing to do, end task_transfer */ + xfr_transfer_move_to_next_lookup(xfr, env); + xfr_transfer_nexttarget_or_end(xfr, env); +} + +/** callback for task_transfer tcp connections */ +int +auth_xfer_transfer_tcp_callback(struct comm_point* c, void* arg, int err, + struct comm_reply* ATTR_UNUSED(repinfo)) +{ + struct auth_xfer* xfr = (struct auth_xfer*)arg; + struct module_env* env; + log_assert(xfr->task_probe); + env = xfr->task_probe->env; + + if(err != NETEVENT_NOERROR) { + /* connection failed, closed, or timeout */ + /* stop this transfer, cleanup + * and continue task_transfer*/ + verbose(VERB_ALGO, "xfr stopped, connection lost to %s", + xfr->task_transfer->master->host); + comm_point_delete(xfr->task_transfer->cp); + xfr->task_transfer->cp = NULL; + xfr_transfer_nextmaster(xfr); + xfr_transfer_nexttarget_or_end(xfr, env); + return 0; + } + + /* TODO: handle returned packet */ + /* if it fails, cleanup and end this transfer */ + /* if it needs to fallback from IXFR to AXFR, do that */ + /* if it is good, link it into the list of data */ + + /* if we want to read more messages, setup the commpoint to read + * a DNS packet, and the timeout */ + c->tcp_is_reading = 1; + comm_point_start_listening(c, -1, AUTH_TRANSFER_TIMEOUT); + return 0; +} + + +/** start transfer task by this worker , xfr is locked. */ +static void +xfr_start_transfer(struct auth_xfer* xfr, struct module_env* env, + struct auth_master* master) +{ + log_assert(xfr->task_transfer != NULL); + log_assert(xfr->task_transfer->worker == NULL); + log_assert(xfr->task_transfer->chunks_first == NULL); + log_assert(xfr->task_transfer->chunks_last == NULL); + xfr->task_transfer->worker = env->worker; + xfr->task_transfer->env = env; + lock_basic_unlock(&xfr->lock); + + /* init transfer process */ + /* find that master in the transfer's list of masters? */ + xfr_transfer_start_list(xfr, master); + /* start lookup for hostnames in transfer master list */ + xfr_transfer_start_lookups(xfr); + + /* initiate TCP, and set timeout on it */ + xfr_transfer_nexttarget_or_end(xfr, env); +} + +/** disown task_probe. caller must hold xfr.lock */ +static void +xfr_probe_disown(struct auth_xfer* xfr) +{ + /* remove timer (from this worker's event base) */ + comm_timer_delete(xfr->task_probe->timer); + xfr->task_probe->timer = NULL; + /* remove the commpoint */ + comm_point_delete(xfr->task_probe->cp); + xfr->task_probe->cp = NULL; + /* we don't own this item anymore */ + xfr->task_probe->worker = NULL; + xfr->task_probe->env = NULL; +} + +/** send the UDP probe to the master, this is part of task_probe */ +static int +xfr_probe_send_probe(struct auth_xfer* xfr, struct module_env* env, + int timeout) +{ + struct sockaddr_storage addr; + socklen_t addrlen = 0; + struct timeval t; + /* pick master */ + struct auth_master* master = xfr_probe_current_master(xfr); + if(!master) return 0; + + /* get master addr */ + if(xfr->task_probe->scan_addr) { + addrlen = xfr->task_probe->scan_addr->addrlen; + memmove(&addr, &xfr->task_probe->scan_addr->addr, addrlen); + } else { + if(!extstrtoaddr(master->host, &addr, &addrlen)) { + char zname[255+1]; + dname_str(xfr->name, zname); + log_err("%s: cannot parse master %s", zname, + master->host); + return 0; + } + } + + /* create packet */ + /* create new ID for new probes, but not on timeout retries, + * this means we'll accept replies to previous retries to same ip */ + if(timeout == AUTH_PROBE_TIMEOUT) + xfr->task_probe->id = (uint16_t)(ub_random(env->rnd)&0xffff); + xfr_create_probe_packet(xfr, env->scratch_buffer, 1, + xfr->task_probe->id); + if(!xfr->task_probe->cp) { + int fd = xfr_fd_for_master(env, &addr, addrlen, master->host); + if(fd == -1) { + char zname[255+1]; + dname_str(xfr->name, zname); + verbose(VERB_ALGO, "cannot create fd for " + "probe %s to %s", zname, master->host); + return 0; + } + xfr->task_probe->cp = comm_point_create_udp(env->worker_base, + fd, env->outnet->udp_buff, auth_xfer_probe_udp_callback, + xfr); + if(!xfr->task_probe->cp) { + close(fd); + log_err("malloc failure"); + return 0; + } + } + if(!xfr->task_probe->timer) { + xfr->task_probe->timer = comm_timer_create(env->worker_base, + auth_xfer_probe_timer_callback, xfr); + if(!xfr->task_probe->timer) { + log_err("malloc failure"); + return 0; + } + } + + /* send udp packet */ + if(!comm_point_send_udp_msg(xfr->task_probe->cp, env->scratch_buffer, + (struct sockaddr*)&addr, addrlen)) { + char zname[255+1]; + dname_str(xfr->name, zname); + verbose(VERB_ALGO, "failed to send soa probe for %s to %s", + zname, master->host); + return 0; + } + xfr->task_probe->timeout = timeout; +#ifndef S_SPLINT_S + t.tv_sec = timeout/1000; + t.tv_usec = (timeout%1000)*1000; +#endif + comm_timer_set(xfr->task_probe->timer, &t); + + return 1; +} + +/** callback for task_probe timer */ +void +auth_xfer_probe_timer_callback(void* arg) +{ + struct auth_xfer* xfr = (struct auth_xfer*)arg; + struct module_env* env; + log_assert(xfr->task_probe); + env = xfr->task_probe->env; + + if(xfr->task_probe->timeout <= AUTH_PROBE_TIMEOUT_STOP) { + /* try again with bigger timeout */ + if(xfr_probe_send_probe(xfr, env, xfr->task_probe->timeout*2)) { + return; + } + } + /* delete commpoint so a new one is created, with a fresh port nr */ + comm_point_delete(xfr->task_probe->cp); + xfr->task_probe->cp = NULL; + + /* too many timeouts (or fail to send), move to next or end */ + xfr_probe_nextmaster(xfr); + xfr_probe_send_or_end(xfr, env); +} + +/** callback for task_probe udp packets */ +int +auth_xfer_probe_udp_callback(struct comm_point* c, void* arg, int err, + struct comm_reply* repinfo) +{ + struct auth_xfer* xfr = (struct auth_xfer*)arg; + struct module_env* env; + log_assert(xfr->task_probe); + env = xfr->task_probe->env; + + /* the comm_point_udp_callback is in a for loop for NUM_UDP_PER_SELECT + * and we set rep.c=NULL to stop if from looking inside the commpoint*/ + repinfo->c = NULL; + /* stop the timer */ + comm_timer_disable(xfr->task_probe->timer); + + /* see if we got a packet and what that means */ + if(err == NETEVENT_NOERROR) { + uint32_t serial = 0; + if(check_packet_ok(c->buffer, LDNS_RR_TYPE_SOA, xfr, + &serial)) { + /* successful lookup */ + /* see if this serial indicates that the zone has + * to be updated */ + lock_basic_lock(&xfr->lock); + if(xfr_serial_means_update(xfr, serial)) { + /* if updated, start the transfer task, if needed */ + if(xfr->task_transfer->worker == NULL) { + xfr_probe_disown(xfr); + xfr_start_transfer(xfr, env, + xfr_probe_current_master(xfr)); + return 0; + + } + } else { + /* if zone not updated, start the wait timer again */ + if(xfr->task_nextprobe->worker == NULL) + xfr_set_timeout(xfr, env, 0); + } + /* other tasks are running, we don't do this anymore */ + xfr_probe_disown(xfr); + lock_basic_unlock(&xfr->lock); + /* return, we don't sent a reply to this udp packet, + * and we setup the tasks to do next */ + return 0; + } + } + + /* failed lookup */ + /* delete commpoint so a new one is created, with a fresh port nr */ + comm_point_delete(xfr->task_probe->cp); + xfr->task_probe->cp = NULL; + + /* if the result was not a successfull probe, we need + * to send the next one */ + xfr_probe_nextmaster(xfr); + xfr_probe_send_or_end(xfr, env); + return 0; +} + +/** lookup a host name for its addresses, if needed */ +static int +xfr_probe_lookup_host(struct auth_xfer* xfr, struct module_env* env) +{ + struct sockaddr_storage addr; + socklen_t addrlen = 0; + struct auth_master* master = xfr->task_probe->lookup_target; + struct query_info qinfo; + uint16_t qflags = BIT_RD; + uint8_t dname[LDNS_MAX_DOMAINLEN+1]; + struct edns_data edns; + sldns_buffer* buf = env->scratch_buffer; + if(!master) return 0; + if(extstrtoaddr(master->host, &addr, &addrlen)) { + /* not needed, host is in IP addr format */ + return 0; + } + + /* use mesh_new_callback to probe for non-addr hosts, + * and then wait for them to be looked up (in cache, or query) */ + qinfo.qname_len = sizeof(dname); + if(sldns_str2wire_dname_buf(master->host, dname, &qinfo.qname_len) + != 0) { + log_err("cannot parse host name of master %s", master->host); + return 0; + } + qinfo.qname = dname; + qinfo.qclass = xfr->dclass; + qinfo.qtype = LDNS_RR_TYPE_A; + if(xfr->task_probe->lookup_aaaa) + qinfo.qtype = LDNS_RR_TYPE_AAAA; + qinfo.local_alias = NULL; + if(verbosity >= VERB_ALGO) { + char buf[512]; + char buf2[LDNS_MAX_DOMAINLEN+1]; + dname_str(xfr->name, buf2); + snprintf(buf, sizeof(buf), "auth zone %s: master lookup" + " for task_probe", buf2); + log_query_info(VERB_ALGO, buf, &qinfo); + } + edns.edns_present = 1; + edns.ext_rcode = 0; + edns.edns_version = 0; + edns.bits = EDNS_DO; + edns.opt_list = NULL; + if(sldns_buffer_capacity(buf) < 65535) + edns.udp_size = (uint16_t)sldns_buffer_capacity(buf); + else edns.udp_size = 65535; + + if(!mesh_new_callback(env->mesh, &qinfo, qflags, &edns, buf, 0, + &auth_xfer_probe_lookup_callback, xfr)) { + log_err("out of memory lookup up master %s", master->host); + free(qinfo.qname); + return 0; + } + free(qinfo.qname); + return 1; +} + +/** move to sending the probe packets, next if fails. task_probe */ +static void +xfr_probe_send_or_end(struct auth_xfer* xfr, struct module_env* env) +{ + /* are we doing hostname lookups? */ + while(xfr->task_probe->lookup_target) { + if(xfr_probe_lookup_host(xfr, env)) { + /* wait for lookup to finish, + * note that the hostname may be in unbound's cache + * and we may then get an instant cache response, + * and that calls the callback just like a full + * lookup and lookup failures also call callback */ + return; + } + xfr_probe_move_to_next_lookup(xfr, env); + } + + /* send probe packets */ + while(!xfr_probe_end_of_list(xfr)) { + if(xfr_probe_send_probe(xfr, env, AUTH_PROBE_TIMEOUT)) { + /* successfully sent probe, wait for callback */ + return; + } + /* failed to send probe, next master */ + if(!xfr_probe_nextmaster(xfr)) { + break; + } + } + + lock_basic_lock(&xfr->lock); + /* we failed to send this as well, move to the wait task, + * use the shorter retry timeout */ + xfr_probe_disown(xfr); + + /* pick up the nextprobe task and wait */ + xfr_set_timeout(xfr, env, 1); + lock_basic_unlock(&xfr->lock); +} + +/** callback for task_probe lookup of host name, of A or AAAA */ +void auth_xfer_probe_lookup_callback(void* arg, int rcode, sldns_buffer* buf, + enum sec_status ATTR_UNUSED(sec), char* ATTR_UNUSED(why_bogus)) +{ + struct auth_xfer* xfr = (struct auth_xfer*)arg; + struct module_env* env; + log_assert(xfr->task_probe); + env = xfr->task_probe->env; + + /* process result */ + if(rcode == LDNS_RCODE_NOERROR) { + uint16_t wanted_qtype = LDNS_RR_TYPE_A; + struct regional* temp = env->scratch; + struct query_info rq; + struct reply_info* rep; + if(xfr->task_probe->lookup_aaaa) + wanted_qtype = LDNS_RR_TYPE_AAAA; + memset(&rq, 0, sizeof(rq)); + rep = parse_reply_in_temp_region(buf, temp, &rq); + if(rep && rq.qtype == wanted_qtype && + FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NOERROR) { + /* parsed successfully */ + struct ub_packed_rrset_key* answer = + reply_find_answer_rrset(&rq, rep); + if(answer) { + xfr_master_add_addrs(xfr->task_probe-> + lookup_target, answer, wanted_qtype); + } + } + } + + /* move to lookup AAAA after A lookup, move to next hostname lookup, + * or move to send the probes, or, if nothing to do, end task_probe */ + xfr_probe_move_to_next_lookup(xfr, env); + xfr_probe_send_or_end(xfr, env); +} + +/** xfer nextprobe timeout callback, this is part of task_nextprobe */ +void +auth_xfer_timer(void* arg) +{ + struct auth_xfer* xfr = (struct auth_xfer*)arg; + struct module_env* env; + log_assert(xfr->task_nextprobe); + lock_basic_lock(&xfr->lock); + env = xfr->task_nextprobe->env; + + /* see if zone has expired, and if so, also set auth_zone expired */ + if(xfr->have_zone && !xfr->zone_expired && + *env->now >= xfr->task_nextprobe->lease_time + xfr->expiry) { + lock_basic_unlock(&xfr->lock); + auth_xfer_set_expired(xfr, env, 1); + lock_basic_lock(&xfr->lock); + } + + /* delete the timer, because the next worker to pick this up may + * not have the same event base */ + comm_timer_delete(xfr->task_nextprobe->timer); + xfr->task_nextprobe->timer = NULL; + xfr->task_nextprobe->next_probe = 0; + /* we don't own this item anymore */ + xfr->task_nextprobe->worker = NULL; + xfr->task_nextprobe->env = NULL; + + /* see if we need to start a probe (or maybe it is already in + * progress (due to notify)) */ + if(xfr->task_probe->worker == NULL) { + /* pick up the probe task ourselves */ + xfr->task_probe->worker = env->worker; + xfr->task_probe->env = env; + lock_basic_unlock(&xfr->lock); + xfr->task_probe->cp = NULL; + + /* start the task */ + /* this was a timeout, so no specific first master to scan */ + xfr_probe_start_list(xfr, NULL); + /* setup to start the lookup of hostnames of masters afresh */ + xfr_probe_start_lookups(xfr); + /* send the probe packet or next send, or end task */ + xfr_probe_send_or_end(xfr, env); + } else { + lock_basic_unlock(&xfr->lock); + } +} + +/** for task_nextprobe. + * determine next timeout for auth_xfer. Also (re)sets timer. + * @param xfr: task structure + * @param env: module environment, with worker and time. + * @param failure: set true if timer should be set for failure retry. + */ +static void +xfr_set_timeout(struct auth_xfer* xfr, struct module_env* env, + int failure) +{ + struct timeval tv; + log_assert(xfr->task_nextprobe != NULL); + log_assert(xfr->task_nextprobe->worker == NULL || + xfr->task_nextprobe->worker == env->worker); + /* normally, nextprobe = startoflease + refresh, + * but if expiry is sooner, use that one. + * after a failure, use the retry timer instead. */ + xfr->task_nextprobe->next_probe = *env->now; + if(xfr->task_nextprobe->lease_time) + xfr->task_nextprobe->next_probe = + xfr->task_nextprobe->lease_time; + if(xfr->have_zone) { + time_t wait = xfr->refresh; + if(failure) wait = xfr->retry; + if(xfr->expiry < wait) + xfr->task_nextprobe->next_probe += xfr->expiry; + else xfr->task_nextprobe->next_probe += wait; + } + + if(!xfr->task_nextprobe->timer) { + xfr->task_nextprobe->timer = comm_timer_create( + env->worker_base, auth_xfer_timer, xfr); + if(!xfr->task_nextprobe->timer) { + /* failed to malloc memory. likely zone transfer + * also fails for that. skip the timeout */ + char zname[255+1]; + dname_str(xfr->name, zname); + log_err("cannot allocate timer, no refresh for %s", + zname); + return; + } + } + xfr->task_nextprobe->worker = env->worker; + xfr->task_nextprobe->env = env; + if(*(xfr->task_nextprobe->env->now) <= xfr->task_nextprobe->next_probe) + tv.tv_sec = xfr->task_nextprobe->next_probe - + *(xfr->task_nextprobe->env->now); + else tv.tv_sec = 0; + tv.tv_usec = 0; + comm_timer_set(xfr->task_nextprobe->timer, &tv); +} + +/** initial pick up of worker timeouts, ties events to worker event loop */ +void +auth_xfer_pickup_initial(struct auth_zones* az, struct module_env* env) +{ + /* TODO: call this from worker0 at start of unbound */ + struct auth_xfer* x; + lock_rw_wrlock(&az->lock); + RBTREE_FOR(x, struct auth_xfer*, &az->xtree) { + lock_basic_lock(&x->lock); + if(x->task_nextprobe && x->task_nextprobe->worker == NULL) + xfr_set_timeout(x, env, 0); + lock_basic_unlock(&x->lock); + } + lock_rw_unlock(&az->lock); +} + +/** + * malloc the xfer and tasks + * @param z: auth_zone with name of zone. + */ +static struct auth_xfer* +auth_xfer_new(struct auth_zone* z) +{ + struct auth_xfer* xfr; + xfr = (struct auth_xfer*)calloc(1, sizeof(*xfr)); + if(!xfr) return NULL; + xfr->name = memdup(z->name, z->namelen); + if(!xfr->name) { + free(xfr); + return NULL; + } + xfr->node.key = xfr; + xfr->namelen = z->namelen; + xfr->namelabs = z->namelabs; + xfr->dclass = z->dclass; + + xfr->task_nextprobe = (struct auth_nextprobe*)calloc(1, + sizeof(struct auth_nextprobe)); + if(!xfr->task_nextprobe) { + free(xfr->name); + free(xfr); + return NULL; + } + xfr->task_probe = (struct auth_probe*)calloc(1, + sizeof(struct auth_probe)); + if(!xfr->task_probe) { + free(xfr->task_nextprobe); + free(xfr->name); + free(xfr); + return NULL; + } + xfr->task_transfer = (struct auth_transfer*)calloc(1, + sizeof(struct auth_transfer)); + if(!xfr->task_transfer) { + free(xfr->task_probe); + free(xfr->task_nextprobe); + free(xfr->name); + free(xfr); + return NULL; + } + + lock_basic_init(&xfr->lock); + lock_protect(&xfr->lock, xfr, sizeof(*xfr)); + lock_protect(&xfr->lock, &xfr->task_nextprobe->worker, + sizeof(xfr->task_nextprobe->worker)); + lock_protect(&xfr->lock, &xfr->task_probe->worker, + sizeof(xfr->task_probe->worker)); + lock_protect(&xfr->lock, &xfr->task_transfer->worker, + sizeof(xfr->task_transfer->worker)); + return xfr; +} + +/** Create auth_xfer structure. + * This populates the have_zone, soa values, next_probe and so on times. + * and sets the timeout, if a zone transfer is needed a short timeout is set. + * For that the auth_zone itself must exist (and read in zonefile) + * returns false on alloc failure. */ +struct auth_xfer* +auth_xfer_create(struct auth_zones* az, struct auth_zone* z) +{ + struct auth_xfer* xfr; + + /* malloc it */ + xfr = auth_xfer_new(z); + if(!xfr) { + log_err("malloc failure"); + return NULL; + } + /* insert in tree */ + (void)rbtree_insert(&az->xtree, &xfr->node); + return xfr; +} + +/** create new auth_master structure */ +static struct auth_master* +auth_master_new(struct auth_master*** list) +{ + struct auth_master *m; + m = (struct auth_master*)calloc(1, sizeof(*m)); + if(!m) { + log_err("malloc failure"); + return NULL; + } + /* set first pointer to m, or next pointer of previous element to m */ + (**list) = m; + /* store m's next pointer as future point to store at */ + (*list) = &(m->next); + return m; +} + +int +xfer_set_masters(struct auth_master** list, struct config_auth* c) +{ + struct auth_master* m; + struct config_strlist* p; + /* list points to the first, or next pointer for the new element */ + while(*list) { + list = &( (*list)->next ); + } + for(p = c->masters; p; p = p->next) { + m = auth_master_new(&list); + m->ixfr = 1; + m->host = strdup(p->str); + if(!m->host) { + log_err("malloc failure"); + return 0; + } + } + for(p = c->urls; p; p = p->next) { + m = auth_master_new(&list); + m->http = 1; + /* TODO parse url, get host, file */ + m->host = strdup(p->str); + if(!m->host) { + log_err("malloc failure"); + return 0; + } + } + return 1; +} + +#define SERIAL_BITS 32 +int +compare_serial(uint32_t a, uint32_t b) +{ + const uint32_t cutoff = ((uint32_t) 1 << (SERIAL_BITS - 1)); + + if (a == b) { + return 0; + } else if ((a < b && b - a < cutoff) || (a > b && a - b > cutoff)) { + return -1; + } else { + return 1; + } +} diff --git a/usr.sbin/unbound/services/authzone.h b/usr.sbin/unbound/services/authzone.h index 5b4623b6520..2ebc5f97e40 100644 --- a/usr.sbin/unbound/services/authzone.h +++ b/usr.sbin/unbound/services/authzone.h @@ -45,20 +45,38 @@ #define SERVICES_AUTHZONE_H #include "util/rbtree.h" #include "util/locks.h" +#include "services/mesh.h" struct ub_packed_rrset_key; struct regional; struct config_file; +struct config_auth; struct query_info; struct dns_msg; +struct edns_data; +struct module_env; +struct worker; +struct comm_point; +struct comm_timer; +struct comm_reply; +struct auth_rrset; +struct auth_nextprobe; +struct auth_probe; +struct auth_transfer; +struct auth_master; +struct auth_chunk; /** * Authoritative zones, shared. */ struct auth_zones { - /** lock on the authzone tree */ + /** lock on the authzone trees */ lock_rw_type lock; /** rbtree of struct auth_zone */ rbtree_type ztree; + /** rbtree of struct auth_xfer */ + rbtree_type xtree; + /** do we have downstream enabled */ + int have_downstream; }; /** @@ -89,10 +107,21 @@ struct auth_zone { * rbtree of struct auth_data */ rbtree_type data; - /* zonefile name (or NULL for no zonefile) */ + /** zonefile name (or NULL for no zonefile) */ char* zonefile; - /* fallback to the internet on failure or ttl-expiry of auth zone */ + /** fallback to the internet on failure or ttl-expiry of auth zone */ int fallback_enabled; + /** the zone has expired (enabled by the xfer worker), fallback + * happens if that option is enabled. */ + int zone_expired; + /** zone is a slave zone (it has masters) */ + int zone_is_slave; + /** for downstream: this zone answers queries towards the downstream + * clients */ + int for_downstream; + /** for upstream: this zone answers queries that unbound intends to + * send upstream. */ + int for_upstream; }; /** @@ -128,14 +157,267 @@ struct auth_rrset { }; /** + * Authoritative zone transfer structure. + * Create and destroy needs the auth_zones* biglock. + * The structure consists of different tasks. Each can be unowned (-1) or + * owner by a worker (worker-num). A worker can pick up a task and then do + * it. This means the events (timeouts, sockets) are for that worker. + * + * (move this to tasks). + * They don't have locks themselves, the worker (that owns it) uses it, + * also as part of callbacks, hence it has separate zonename pointers for + * lookup in the main zonetree. If the zone has no transfers, this + * structure is not created. + */ +struct auth_xfer { + /** rbtree node, key is name and class */ + rbnode_type node; + + /** lock on this structure, and on the workernum elements of the + * tasks. First hold the tree-lock in auth_zones, find the auth_xfer, + * lock this lock. Then a worker can reassign itself to fill up + * one of the tasks. + * Once it has the task assigned to it, the worker can access the + * other elements of the task structure without a lock, because that + * is necessary for the eventloop and callbacks from that. */ + lock_basic_type lock; + + /** zone name, in uncompressed wireformat */ + uint8_t* name; + /** length of zone name */ + size_t namelen; + /** number of labels in zone name */ + int namelabs; + /** the class of this zone, in host byteorder. + * uses 'dclass' to not conflict with c++ keyword class. */ + uint16_t dclass; + + /** task to wait for next-probe-timeout, + * once timeouted, see if a SOA probe is needed, or already + * in progress */ + struct auth_nextprobe* task_nextprobe; + + /** task for SOA probe. Check if the zone can be updated */ + struct auth_probe* task_probe; + + /** Task for transfer. Transferring and updating the zone. This + * includes trying (potentially) several upstream masters. Downloading + * and storing the zone */ + struct auth_transfer* task_transfer; + + /** a notify was received, but a zone transfer or probe was already + * acted on. + * However, the zone transfer could signal a newer serial number. + * The serial number of that notify is saved below. The transfer and + * probe tasks should check this once done to see if they need to + * restart the transfer task for the newer notify serial. + * Hold the lock to access this member (and the serial). + */ + int notify_received; + /** serial number of the notify */ + uint32_t notify_serial; + + /* protected by the lock on the structure, information about + * the loaded authority zone. */ + /** is the zone currently considered expired? after expiry also older + * serial numbers are allowed (not just newer) */ + int zone_expired; + /** do we have a zone (if 0, no zone data at all) */ + int have_zone; + + /** current serial (from SOA), if we have no zone, 0 */ + uint32_t serial; + /** retry time (from SOA), time to wait with next_probe + * if no master responds */ + time_t retry; + /** refresh time (from SOA), time to wait with next_probe + * if everything is fine */ + time_t refresh; + /** expiry time (from SOA), time until zone data is not considered + * valid any more, if no master responds within this time, either + * with the current zone or a new zone. */ + time_t expiry; +}; + +/** + * The next probe task. + * This task consists of waiting for the probetimeout. It is a task because + * it needs an event in the eventtable. Once the timeout has passed, that + * worker can (potentially) become the auth_probe worker, or if another worker + * is already doing that, do nothing. Tasks becomes unowned. + * The probe worker, if it detects nothing has to be done picks up this task, + * if unowned. + */ +struct auth_nextprobe { + /* Worker pointer. NULL means unowned. */ + struct worker* worker; + /* module env for this task */ + struct module_env* env; + + /** Timeout for next probe (for SOA) */ + time_t next_probe; + /** zone lease start time (start+expiry is expiration time). + * this is renewed every SOA probe and transfer. On zone load + * from zonefile it is also set (with probe set soon to check) */ + time_t lease_time; + /** timeout callback for next_probe or expiry(if that is sooner). + * it is on the worker's event_base */ + struct comm_timer* timer; +}; + +/** + * The probe task. + * Send a SOA UDP query to see if the zone needs to be updated (or similar, + * potential, HTTP probe query) and check serial number. + * If yes, start the auth_transfer task. If no, make sure auth_nextprobe + * timeout wait task is running. + * Needs to be a task, because the UDP query needs an event entry. + * This task could also be started by eg. a NOTIFY being received, even though + * another worker is performing the nextprobe task (and that worker keeps + * waiting uninterrupted). + */ +struct auth_probe { + /* Worker pointer. NULL means unowned. */ + struct worker* worker; + /* module env for this task */ + struct module_env* env; + + /** list of upstream masters for this zone, from config */ + struct auth_master* masters; + + /** for the hostname lookups, which master is current */ + struct auth_master* lookup_target; + /** are we looking up A or AAAA, first A, then AAAA (if ip6 enabled) */ + int lookup_aaaa; + + /** once notified, or the timeout has been reached. a scan starts. */ + /** the scan specific target (notify source), or NULL if none */ + struct auth_master* scan_specific; + /** scan tries all the upstream masters. the scan current target. + * or NULL if not working on sequential scan */ + struct auth_master* scan_target; + /** if not NULL, the specific addr for the current master */ + struct auth_addr* scan_addr; + + /** dns id of packet in flight */ + uint16_t id; + /** the SOA probe udp event. + * on the workers event base. */ + struct comm_point* cp; + /** timeout for packets. + * on the workers event base. */ + struct comm_timer* timer; + /** timeout in msec */ + int timeout; +}; + +/** + * The transfer task. + * Once done, make sure the nextprobe waiting task is running, whether done + * with failure or success. If failure, use shorter timeout for wait time. + */ +struct auth_transfer { + /* Worker pointer. NULL means unowned. */ + struct worker* worker; + /* module env for this task */ + struct module_env* env; + + /** xfer data that has been transferred, the data is applied + * once the transfer has completed correctly */ + struct auth_chunk* chunks_first; + /** last element in chunks list (to append new data at the end) */ + struct auth_chunk* chunks_last; + + /** list of upstream masters for this zone, from config */ + struct auth_master* masters; + + /** for the hostname lookups, which master is current */ + struct auth_master* lookup_target; + /** are we looking up A or AAAA, first A, then AAAA (if ip6 enabled) */ + int lookup_aaaa; + + /** once notified, or the timeout has been reached. a scan starts. */ + /** the scan specific target (notify source), or NULL if none */ + struct auth_master* scan_specific; + /** scan tries all the upstream masters. the scan current target. + * or NULL if not working on sequential scan */ + struct auth_master* scan_target; + /** what address we are scanning for the master, or NULL if the + * master is in IP format itself */ + struct auth_addr* scan_addr; + /** the zone transfer in progress (or NULL if in scan). It is + * from this master */ + struct auth_master* master; + + /** failed ixfr transfer, retry with axfr (to the current master), + * the IXFR was 'REFUSED', 'SERVFAIL', 'NOTIMPL' or the contents of + * the IXFR did not apply cleanly (out of sync, delete of nonexistent + * data or add of duplicate data). Flag is cleared once the retry + * with axfr is done. */ + int ixfr_fail; + + /** dns id of AXFR query */ + uint16_t id; + /** the transfer (TCP) to the master. + * on the workers event base. */ + struct comm_point* cp; +}; + +/** list of addresses */ +struct auth_addr { + /** next in list */ + struct auth_addr* next; + /** IP address */ + struct sockaddr_storage addr; + /** addr length */ + socklen_t addrlen; +}; + +/** auth zone master upstream, and the config settings for it */ +struct auth_master { + /** next master in list */ + struct auth_master* next; + /** master IP address (and port), or hostname, string */ + char* host; + /** for http, filename */ + char* file; + /** use HTTP for this master */ + int http; + /** use IXFR for this master */ + int ixfr; + /** use ssl for channel */ + int ssl; + /** if the host is a hostname, the list of resolved addrs, if any*/ + struct auth_addr* list; +}; + +/** auth zone master zone transfer data chunk */ +struct auth_chunk { + /** next chunk in list */ + struct auth_chunk* next; + /** the data from this chunk, this is what was received. + * for an IXFR that means results from comm_net tcp actions, + * packets. also for an AXFR. For HTTP a zonefile chunk. */ + uint8_t* data; + /** length of allocated data */ + size_t len; +}; + +/** * Create auth zones structure */ struct auth_zones* auth_zones_create(void); /** * Apply configuration to auth zones. Reads zonefiles. + * @param az: auth zones structure + * @param cfg: config to apply. + * @param setup: if true, also sets up values in the auth zones structure + * @param env: for setup, with current time. + * @return false on failure. */ -int auth_zones_apply_config(struct auth_zones* az, struct config_file* cfg); +int auth_zones_apply_cfg(struct auth_zones* az, struct config_file* cfg, + int setup, struct module_env* env); /** * Delete auth zones structure @@ -170,6 +452,20 @@ int auth_zones_lookup(struct auth_zones* az, struct query_info* qinfo, struct regional* region, struct dns_msg** msg, int* fallback, uint8_t* dp_nm, size_t dp_nmlen); +/** + * Answer query from auth zone. Create authoritative answer. + * @param az: auth zones structure. + * @param env: the module environment. + * @param qinfo: query info (parsed). + * @param edns: edns info (parsed). + * @param buf: buffer with query ID and flags, also for reply. + * @param temp: temporary storage region. + * @return false if not answered + */ +int auth_zones_answer(struct auth_zones* az, struct module_env* env, + struct query_info* qinfo, struct edns_data* edns, struct sldns_buffer* buf, + struct regional* temp); + /** * Find the auth zone that is above the given qname. * Return NULL when there is no auth_zone above the give name, otherwise @@ -185,6 +481,11 @@ struct auth_zone* auth_zones_find_zone(struct auth_zones* az, struct auth_zone* auth_zone_find(struct auth_zones* az, uint8_t* nm, size_t nmlen, uint16_t dclass); +/** find an xfer zone by name (exact match by name or NULL returned) */ +struct auth_xfer* auth_xfer_find(struct auth_zones* az, uint8_t* nm, + size_t nmlen, uint16_t dclass); + + /** create an auth zone. returns wrlocked zone. caller must have wrlock * on az. returns NULL on malloc failure */ struct auth_zone* auth_zone_create(struct auth_zones* az, uint8_t* nm, @@ -206,4 +507,50 @@ int auth_zone_cmp(const void* z1, const void* z2); /** compare auth_data for sorted rbtree */ int auth_data_cmp(const void* z1, const void* z2); +/** compare auth_xfer for sorted rbtree */ +int auth_xfer_cmp(const void* z1, const void* z2); + +/** Create auth_xfer structure. + * Caller must have wrlock on az. Returns locked xfer zone. + * @param az: zones structure. + * @param z: zone with name and class + * @return xfer zone or NULL + */ +struct auth_xfer* auth_xfer_create(struct auth_zones* az, struct auth_zone* z); + +/** + * Set masters in auth xfer structure from config. + * @param list: pointer to start of list. The malloced list is returned here. + * @param c: the config items to copy over. + * @return false on failure. + */ +int xfer_set_masters(struct auth_master** list, struct config_auth* c); + + +/** xfer nextprobe timeout callback, this is part of task_nextprobe */ +void auth_xfer_timer(void* arg); + +/** callback for commpoint udp replies to task_probe */ +int auth_xfer_probe_udp_callback(struct comm_point* c, void* arg, int err, + struct comm_reply* repinfo); +/** callback for task_transfer tcp connections */ +int auth_xfer_transfer_tcp_callback(struct comm_point* c, void* arg, int err, + struct comm_reply* repinfo); +/** xfer probe timeout callback, part of task_probe */ +void auth_xfer_probe_timer_callback(void* arg); +/** mesh callback for task_probe on lookup of host names */ +void auth_xfer_probe_lookup_callback(void* arg, int rcode, + struct sldns_buffer* buf, enum sec_status sec, char* why_bogus); +/** mesh callback for task_transfer on lookup of host names */ +void auth_xfer_transfer_lookup_callback(void* arg, int rcode, + struct sldns_buffer* buf, enum sec_status sec, char* why_bogus); + +/* + * Compares two 32-bit serial numbers as defined in RFC1982. Returns + * <0 if a < b, 0 if a == b, and >0 if a > b. The result is undefined + * if a != b but neither is greater or smaller (see RFC1982 section + * 3.2.). + */ +int compare_serial(uint32_t a, uint32_t b); + #endif /* SERVICES_AUTHZONE_H */ diff --git a/usr.sbin/unbound/services/cache/dns.c b/usr.sbin/unbound/services/cache/dns.c index da43c504dfa..f9dc5922f68 100644 --- a/usr.sbin/unbound/services/cache/dns.c +++ b/usr.sbin/unbound/services/cache/dns.c @@ -111,7 +111,7 @@ store_rrsets(struct module_env* env, struct reply_info* rep, time_t now, 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, struct regional* region) + struct reply_info* qrep, uint32_t flags, struct regional* region) { struct msgreply_entry* e; time_t ttl = rep->ttl; @@ -127,7 +127,7 @@ dns_cache_store_msg(struct module_env* env, struct query_info* qinfo, * 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); - if(ttl == 0) { + 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 */ verbose(VERB_ALGO, "TTL 0: dropped msg from cache"); @@ -845,7 +845,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, uint16_t flags) + struct regional* region, uint32_t flags) { struct reply_info* rep = NULL; /* alloc, malloc properly (not in region, like msg is) */ @@ -890,9 +890,9 @@ dns_cache_store(struct module_env* env, struct query_info* msgqinf, * Not AA from cache. Not CD in cache (depends on client bit). */ rep->flags |= (BIT_RA | BIT_QR); rep->flags &= ~(BIT_AA | BIT_CD); - h = query_info_hash(&qinf, flags); + h = query_info_hash(&qinf, (uint16_t)flags); dns_cache_store_msg(env, &qinf, h, rep, leeway, pside, msgrep, - region); + flags, region); /* 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); diff --git a/usr.sbin/unbound/services/cache/dns.h b/usr.sbin/unbound/services/cache/dns.h index 096ddf28db6..9e10f437f1b 100644 --- a/usr.sbin/unbound/services/cache/dns.h +++ b/usr.sbin/unbound/services/cache/dns.h @@ -49,6 +49,12 @@ struct reply_info; struct regional; struct delegpt; +/** Flags to control behavior of dns_cache_store() and dns_cache_store_msg(). + * Must be an unsigned 32-bit value larger than 0xffff */ + +/** Allow caching a DNS message with a zero TTL. */ +#define DNSCACHE_STORE_ZEROTTL 0x100000 + /** * Region allocated message reply */ @@ -80,11 +86,13 @@ struct dns_msg { * @param region: region to allocate better entries from cache into. * (used when is_referral is false). * @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). * @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, uint16_t flags); + struct regional* region, uint32_t flags); /** * Store message in the cache. Stores in message cache and rrset cache. @@ -103,11 +111,12 @@ int dns_cache_store(struct module_env* env, struct query_info* qinf, * from the parentside of the zonecut. This means that the type NS * 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 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, struct regional* region); + struct reply_info* qrep, uint32_t flags, struct regional* region); /** * Find a delegation from the cache. diff --git a/usr.sbin/unbound/services/localzone.c b/usr.sbin/unbound/services/localzone.c index 6bde432e8d0..90106d5c809 100644 --- a/usr.sbin/unbound/services/localzone.c +++ b/usr.sbin/unbound/services/localzone.c @@ -52,7 +52,6 @@ #include "util/data/msgreply.h" #include "util/data/msgparse.h" #include "util/as112.h" -#include "util/config_file.h" /* maximum RRs in an RRset, to cap possible 'endless' list RRs. * with 16 bytes for an A record, a 64K packet has about 4000 max */ diff --git a/usr.sbin/unbound/services/mesh.c b/usr.sbin/unbound/services/mesh.c index f04ae16ddbc..3395dc62e67 100644 --- a/usr.sbin/unbound/services/mesh.c +++ b/usr.sbin/unbound/services/mesh.c @@ -533,9 +533,23 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo, return 1; } +static void mesh_schedule_prefetch(struct mesh_area* mesh, + struct query_info* qinfo, uint16_t qflags, time_t leeway, int run); + void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo, uint16_t qflags, time_t leeway) { + mesh_schedule_prefetch(mesh, qinfo, qflags, leeway, 1); +} + +/* Internal backend routine of mesh_new_prefetch(). It takes one additional + * parameter, 'run', which controls whether to run the prefetch state + * immediately. When this function is called internally 'run' could be + * 0 (false), in which case the new state is only made runnable so it + * will not be run recursively on top of the current state. */ +static void mesh_schedule_prefetch(struct mesh_area* mesh, + struct query_info* qinfo, uint16_t qflags, time_t leeway, int run) +{ struct mesh_state* s = mesh_area_find(mesh, NULL, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); #ifdef UNBOUND_DEBUG @@ -589,6 +603,18 @@ void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo, s->list_select = mesh_jostle_list; } } + + if(!run) { +#ifdef UNBOUND_DEBUG + n = +#else + (void) +#endif + rbtree_insert(&mesh->run, &s->run_node); + log_assert(n != NULL); + return; + } + mesh_run(mesh, s, module_event_new, NULL); } @@ -666,6 +692,8 @@ mesh_state_create(struct module_env* env, struct query_info* qinfo, mstate->s.prefetch_leeway = 0; mstate->s.no_cache_lookup = 0; mstate->s.no_cache_store = 0; + mstate->s.need_refetch = 0; + /* init modules */ for(i=0; i<env->mesh->mods.num; i++) { mstate->s.minfo[i] = NULL; @@ -1288,9 +1316,30 @@ int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns, return 1; } +/* Extract the query info and flags from 'mstate' into '*qinfop' and '*qflags'. + * Since this is only used for internal refetch of otherwise-expired answer, + * we simply ignore the rare failure mode when memory allocation fails. */ +static void +mesh_copy_qinfo(struct mesh_state* mstate, struct query_info** qinfop, + uint16_t* qflags) +{ + struct regional* region = mstate->s.env->scratch; + struct query_info* qinfo; + + qinfo = regional_alloc_init(region, &mstate->s.qinfo, sizeof(*qinfo)); + if(!qinfo) + return; + qinfo->qname = regional_alloc_init(region, qinfo->qname, + qinfo->qname_len); + if(!qinfo->qname) + return; + *qinfop = qinfo; + *qflags = mstate->s.query_flags; +} + /** * Continue processing the mesh state at another module. - * Handles module to modules tranfer of control. + * Handles module to modules transfer of control. * Handles module finished. * @param mesh: the mesh area. * @param mstate: currently active mesh state. @@ -1310,7 +1359,8 @@ mesh_continue(struct mesh_area* mesh, struct mesh_state* mstate, mstate->num_activated++; if(mstate->num_activated > MESH_MAX_ACTIVATION) { /* module is looping. Stop it. */ - log_err("internal error: looping module stopped"); + log_err("internal error: looping module (%s) stopped", + mesh->mods.mod[mstate->s.curmod]->name); log_query_info(VERB_QUERY, "pass error for qstate", &mstate->s.qinfo); s = module_error; @@ -1350,11 +1400,32 @@ mesh_continue(struct mesh_area* mesh, struct mesh_state* mstate, /* error is bad, handle pass back up below */ mstate->s.return_rcode = LDNS_RCODE_SERVFAIL; } - if(s == module_error || s == module_finished) { + if(s == module_error) { + mesh_query_done(mstate); + mesh_walk_supers(mesh, mstate); + mesh_state_delete(&mstate->s); + return 0; + } + if(s == module_finished) { if(mstate->s.curmod == 0) { + struct query_info* qinfo = NULL; + uint16_t qflags; + mesh_query_done(mstate); mesh_walk_supers(mesh, mstate); + + /* If the answer to the query needs to be refetched + * from an external DNS server, we'll need to schedule + * a prefetch after removing the current state, so + * we need to make a copy of the query info here. */ + if(mstate->s.need_refetch) + mesh_copy_qinfo(mstate, &qinfo, &qflags); + mesh_state_delete(&mstate->s); + if(qinfo) { + mesh_schedule_prefetch(mesh, qinfo, qflags, + 0, 1); + } return 0; } /* pass along the locus of control */ diff --git a/usr.sbin/unbound/services/outside_network.c b/usr.sbin/unbound/services/outside_network.c index fe2b55b1ac1..1e4059888b9 100644 --- a/usr.sbin/unbound/services/outside_network.c +++ b/usr.sbin/unbound/services/outside_network.c @@ -198,21 +198,17 @@ pick_outgoing_tcp(struct waiting_tcp* w, int s) return 1; } -/** use next free buffer to service a tcp query */ -static int -outnet_tcp_take_into_use(struct waiting_tcp* w, uint8_t* pkt, size_t pkt_len) +/** get TCP file descriptor for address, returns -1 on failure, + * tcp_mss is 0 or maxseg size to set for TCP packets. */ +int +outnet_get_tcp_fd(struct sockaddr_storage* addr, socklen_t addrlen, int tcp_mss) { - struct pending_tcp* pend = w->outnet->tcp_free; int s; #ifdef SO_REUSEADDR int on = 1; #endif - log_assert(pend); - log_assert(pkt); - log_assert(w->addrlen > 0); - /* open socket */ #ifdef INET6 - if(addr_is_ip6(&w->addr, w->addrlen)) + if(addr_is_ip6(addr, addrlen)) s = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP); else #endif @@ -220,12 +216,12 @@ outnet_tcp_take_into_use(struct waiting_tcp* w, uint8_t* pkt, size_t pkt_len) if(s == -1) { #ifndef USE_WINSOCK log_err_addr("outgoing tcp: socket", strerror(errno), - &w->addr, w->addrlen); + addr, addrlen); #else log_err_addr("outgoing tcp: socket", - wsa_strerror(WSAGetLastError()), &w->addr, w->addrlen); + wsa_strerror(WSAGetLastError()), addr, addrlen); #endif - return 0; + return -1; } #ifdef SO_REUSEADDR @@ -235,11 +231,11 @@ outnet_tcp_take_into_use(struct waiting_tcp* w, uint8_t* pkt, size_t pkt_len) " setsockopt(.. SO_REUSEADDR ..) failed"); } #endif - if (w->outnet->tcp_mss > 0) { + + if(tcp_mss > 0) { #if defined(IPPROTO_TCP) && defined(TCP_MAXSEG) if(setsockopt(s, IPPROTO_TCP, TCP_MAXSEG, - (void*)&w->outnet->tcp_mss, - (socklen_t)sizeof(w->outnet->tcp_mss)) < 0) { + (void*)&tcp_mss, (socklen_t)sizeof(tcp_mss)) < 0) { verbose(VERB_ALGO, "outgoing tcp:" " setsockopt(.. TCP_MAXSEG ..) failed"); } @@ -249,6 +245,49 @@ outnet_tcp_take_into_use(struct waiting_tcp* w, uint8_t* pkt, size_t pkt_len) #endif /* defined(IPPROTO_TCP) && defined(TCP_MAXSEG) */ } + return s; +} + +/** connect tcp connection to addr, 0 on failure */ +int +outnet_tcp_connect(int s, struct sockaddr_storage* addr, socklen_t addrlen) +{ + if(connect(s, (struct sockaddr*)addr, addrlen) == -1) { +#ifndef USE_WINSOCK +#ifdef EINPROGRESS + if(errno != EINPROGRESS) { +#endif + if(tcp_connect_errno_needs_log( + (struct sockaddr*)addr, addrlen)) + log_err_addr("outgoing tcp: connect", + strerror(errno), addr, addrlen); + close(s); +#ifdef EINPROGRESS + } +#endif +#else /* USE_WINSOCK */ + if(WSAGetLastError() != WSAEINPROGRESS && + WSAGetLastError() != WSAEWOULDBLOCK) { + closesocket(s); + } +#endif + return 0; + } + return 1; +} + +/** use next free buffer to service a tcp query */ +static int +outnet_tcp_take_into_use(struct waiting_tcp* w, uint8_t* pkt, size_t pkt_len) +{ + struct pending_tcp* pend = w->outnet->tcp_free; + int s; + log_assert(pend); + log_assert(pkt); + log_assert(w->addrlen > 0); + /* open socket */ + s = outnet_get_tcp_fd(&w->addr, w->addrlen, w->outnet->tcp_mss); + if(!pick_outgoing_tcp(w, s)) return 0; @@ -364,9 +403,9 @@ use_free_buffer(struct outside_network* outnet) } } -/** decomission a tcp buffer, closes commpoint and frees waiting_tcp entry */ +/** decommission a tcp buffer, closes commpoint and frees waiting_tcp entry */ static void -decomission_pending_tcp(struct outside_network* outnet, +decommission_pending_tcp(struct outside_network* outnet, struct pending_tcp* pend) { if(pend->c->ssl) { @@ -406,7 +445,7 @@ outnet_tcp_cb(struct comm_point* c, void* arg, int error, } fptr_ok(fptr_whitelist_pending_tcp(pend->query->cb)); (void)(*pend->query->cb)(c, pend->query->cb_arg, error, reply_info); - decomission_pending_tcp(outnet, pend); + decommission_pending_tcp(outnet, pend); return 0; } @@ -1416,7 +1455,7 @@ serviced_delete(struct serviced_query* sq) struct waiting_tcp* p = (struct waiting_tcp*) sq->pending; if(p->pkt == NULL) { - decomission_pending_tcp(sq->outnet, + decommission_pending_tcp(sq->outnet, (struct pending_tcp*)p->next_waiting); } else { waiting_list_remove(sq->outnet, p); diff --git a/usr.sbin/unbound/services/outside_network.h b/usr.sbin/unbound/services/outside_network.h index befd512f0da..04e89a0b7fe 100644 --- a/usr.sbin/unbound/services/outside_network.h +++ b/usr.sbin/unbound/services/outside_network.h @@ -533,6 +533,13 @@ size_t outnet_get_mem(struct outside_network* outnet); */ size_t serviced_get_mem(struct serviced_query* sq); +/** get TCP file descriptor for address, returns -1 on failure, + * tcp_mss is 0 or maxseg size to set for TCP packets. */ +int outnet_get_tcp_fd(struct sockaddr_storage* addr, socklen_t addrlen, int tcp_mss); + +/** connect tcp connection to addr, 0 on failure */ +int outnet_tcp_connect(int s, struct sockaddr_storage* addr, socklen_t addrlen); + /** callback for incoming udp answers from the network */ int outnet_udp_cb(struct comm_point* c, void* arg, int error, struct comm_reply *reply_info); diff --git a/usr.sbin/unbound/services/view.h b/usr.sbin/unbound/services/view.h index e0b346419e9..17778100474 100644 --- a/usr.sbin/unbound/services/view.h +++ b/usr.sbin/unbound/services/view.h @@ -67,7 +67,7 @@ struct view { /** rbtree node, key is name */ rbnode_type node; /** view name. - * Has to be right after rbnode_t due to pointer arithmatic in + * Has to be right after rbnode_t due to pointer arithmetic in * view_create's lock protect */ char* name; /** view specific local authority zones */ diff --git a/usr.sbin/unbound/sldns/parse.c b/usr.sbin/unbound/sldns/parse.c index e30a753a49b..b62c4059775 100644 --- a/usr.sbin/unbound/sldns/parse.c +++ b/usr.sbin/unbound/sldns/parse.c @@ -33,14 +33,14 @@ ssize_t sldns_fget_token_l(FILE *f, char *token, const char *delim, size_t limit, int *line_nr) { int c, prev_c; - int p; /* 0 -> no parenthese seen, >0 nr of ( seen */ + int p; /* 0 -> no parentheses seen, >0 nr of ( seen */ int com, quoted; char *t; size_t i; const char *d; const char *del; - /* standard delimeters */ + /* standard delimiters */ if (!delim) { /* from isspace(3) */ del = LDNS_PARSE_NORMAL; @@ -244,7 +244,7 @@ sldns_bget_token_par(sldns_buffer *b, char *token, const char *delim, size_t limit, int* par, const char* skipw) { int c, lc; - int p; /* 0 -> no parenthese seen, >0 nr of ( seen */ + int p; /* 0 -> no parentheses seen, >0 nr of ( seen */ int com, quoted; char *t; size_t i; diff --git a/usr.sbin/unbound/sldns/parse.h b/usr.sbin/unbound/sldns/parse.h index 7b7456dd206..44236bfd495 100644 --- a/usr.sbin/unbound/sldns/parse.h +++ b/usr.sbin/unbound/sldns/parse.h @@ -103,9 +103,9 @@ ssize_t sldns_bget_token(struct sldns_buffer *b, char *token, const char *delim, * after the keyword + k_del until we hit d_del * \param[in] f file pointer to read from * \param[in] keyword keyword to look for - * \param[in] k_del keyword delimeter + * \param[in] k_del keyword delimiter * \param[out] data the data found - * \param[in] d_del the data delimeter + * \param[in] d_del the data delimiter * \param[in] data_limit maximum size the the data buffer * \return the number of character read */ @@ -116,9 +116,9 @@ ssize_t sldns_fget_keyword_data(FILE *f, const char *keyword, const char *k_del, * after the keyword + k_del until we hit d_del * \param[in] f file pointer to read from * \param[in] keyword keyword to look for - * \param[in] k_del keyword delimeter + * \param[in] k_del keyword delimiter * \param[out] data the data found - * \param[in] d_del the data delimeter + * \param[in] d_del the data delimiter * \param[in] data_limit maximum size the the data buffer * \param[in] line_nr pointer to an integer containing the current line number (for debugging purposes) @@ -131,9 +131,9 @@ ssize_t sldns_fget_keyword_data_l(FILE *f, const char *keyword, const char *k_de * after the keyword + k_del until we hit d_del * \param[in] b buffer pointer to read from * \param[in] keyword keyword to look for - * \param[in] k_del keyword delimeter + * \param[in] k_del keyword delimiter * \param[out] data the data found - * \param[in] d_del the data delimeter + * \param[in] d_del the data delimiter * \param[in] data_limit maximum size the the data buffer * \return the number of character read */ diff --git a/usr.sbin/unbound/sldns/parseutil.c b/usr.sbin/unbound/sldns/parseutil.c index 769987c6477..82dbc0fe113 100644 --- a/usr.sbin/unbound/sldns/parseutil.c +++ b/usr.sbin/unbound/sldns/parseutil.c @@ -165,20 +165,20 @@ sldns_gmtime64_r(int64_t clock, struct tm *result) #endif /* SIZEOF_TIME_T <= 4 */ static int64_t -sldns_serial_arithmitics_time(int32_t time, time_t now) +sldns_serial_arithmetics_time(int32_t time, time_t now) { int32_t offset = time - (int32_t) now; return (int64_t) now + offset; } struct tm * -sldns_serial_arithmitics_gmtime_r(int32_t time, time_t now, struct tm *result) +sldns_serial_arithmetics_gmtime_r(int32_t time, time_t now, struct tm *result) { #if SIZEOF_TIME_T <= 4 - int64_t secs_since_epoch = sldns_serial_arithmitics_time(time, now); + int64_t secs_since_epoch = sldns_serial_arithmetics_time(time, now); return sldns_gmtime64_r(secs_since_epoch, result); #else - time_t secs_since_epoch = sldns_serial_arithmitics_time(time, now); + time_t secs_since_epoch = sldns_serial_arithmetics_time(time, now); return gmtime_r(&secs_since_epoch, result); #endif } diff --git a/usr.sbin/unbound/sldns/parseutil.h b/usr.sbin/unbound/sldns/parseutil.h index c5238bc10a9..10a2630a973 100644 --- a/usr.sbin/unbound/sldns/parseutil.h +++ b/usr.sbin/unbound/sldns/parseutil.h @@ -62,13 +62,13 @@ time_t sldns_mktime_from_utc(const struct tm *tm); * fields of RRSIG records. * * \param[in] time number of seconds since epoch (midnight, January 1st, 1970) - * to be intepreted as a serial arithmetics number relative to now. + * to be interpreted as a serial arithmetics number relative to now. * \param[in] now number of seconds since epoch (midnight, January 1st, 1970) * to which the time value is compared to determine the final value. * \param[out] result the struct with the broken-out time information * \return result on success or NULL on error */ -struct tm * sldns_serial_arithmitics_gmtime_r(int32_t time, time_t now, struct tm *result); +struct tm * sldns_serial_arithmetics_gmtime_r(int32_t time, time_t now, struct tm *result); /** * converts a ttl value (like 5d2h) to a long. diff --git a/usr.sbin/unbound/sldns/rrdef.h b/usr.sbin/unbound/sldns/rrdef.h index 09d81d9b192..0446687bd33 100644 --- a/usr.sbin/unbound/sldns/rrdef.h +++ b/usr.sbin/unbound/sldns/rrdef.h @@ -331,13 +331,13 @@ enum sldns_enum_rdf_type LDNS_RDF_TYPE_NSEC3_NEXT_OWNER, /** 4 shorts represented as 4 * 16 bit hex numbers - * seperated by colons. For NID and L64. + * separated by colons. For NID and L64. */ LDNS_RDF_TYPE_ILNP64, - /** 6 * 8 bit hex numbers seperated by dashes. For EUI48. */ + /** 6 * 8 bit hex numbers separated by dashes. For EUI48. */ LDNS_RDF_TYPE_EUI48, - /** 8 * 8 bit hex numbers seperated by dashes. For EUI64. */ + /** 8 * 8 bit hex numbers separated by dashes. For EUI64. */ LDNS_RDF_TYPE_EUI64, /** A non-zero sequence of US-ASCII letters and numbers in lower case. diff --git a/usr.sbin/unbound/sldns/sbuffer.h b/usr.sbin/unbound/sldns/sbuffer.h index d1aadf8a198..2c30b373670 100644 --- a/usr.sbin/unbound/sldns/sbuffer.h +++ b/usr.sbin/unbound/sldns/sbuffer.h @@ -497,7 +497,7 @@ sldns_buffer_set_at(sldns_buffer *buffer, size_t at, int c, size_t count) * writes count bytes of data to the current position of the buffer * \param[in] buffer the buffer * \param[in] data the data to write - * \param[in] count the lenght of the data to write + * \param[in] count the length of the data to write */ INLINE void sldns_buffer_write(sldns_buffer *buffer, const void *data, size_t count) diff --git a/usr.sbin/unbound/sldns/str2wire.c b/usr.sbin/unbound/sldns/str2wire.c index f84d7d6b823..6759944e4a7 100644 --- a/usr.sbin/unbound/sldns/str2wire.c +++ b/usr.sbin/unbound/sldns/str2wire.c @@ -1541,7 +1541,7 @@ int sldns_str2wire_loc_buf(const char* str, uint8_t* rd, size_t* len) s = strtod(my_str, &my_str); } - /* skip blanks before norterness */ + /* skip blanks before northerness */ while (isblank((unsigned char) *my_str)) { my_str++; } diff --git a/usr.sbin/unbound/sldns/wire2str.c b/usr.sbin/unbound/sldns/wire2str.c index 52b1ed0c2aa..861b7648ade 100644 --- a/usr.sbin/unbound/sldns/wire2str.c +++ b/usr.sbin/unbound/sldns/wire2str.c @@ -1335,7 +1335,7 @@ int sldns_wire2str_time_scan(uint8_t** d, size_t* dl, char** s, size_t* sl) if(*dl < 4) return -1; t = sldns_read_uint32(*d); date_buf[15]=0; - if(sldns_serial_arithmitics_gmtime_r(t, time(NULL), &tm) && + if(sldns_serial_arithmetics_gmtime_r(t, time(NULL), &tm) && strftime(date_buf, 15, "%Y%m%d%H%M%S", &tm)) { (*d) += 4; (*dl) -= 4; diff --git a/usr.sbin/unbound/smallapp/unbound-anchor.c b/usr.sbin/unbound/smallapp/unbound-anchor.c index c060ad394c1..b3009108800 100644 --- a/usr.sbin/unbound/smallapp/unbound-anchor.c +++ b/usr.sbin/unbound/smallapp/unbound-anchor.c @@ -669,7 +669,7 @@ wipe_ip_usage(struct ip_list* p) } } -/** cound unused IPs */ +/** count unused IPs */ static int count_unused(struct ip_list* p) { diff --git a/usr.sbin/unbound/smallapp/unbound-checkconf.c b/usr.sbin/unbound/smallapp/unbound-checkconf.c index 7e9cb4740ae..2b84aad12ab 100644 --- a/usr.sbin/unbound/smallapp/unbound-checkconf.c +++ b/usr.sbin/unbound/smallapp/unbound-checkconf.c @@ -54,6 +54,7 @@ #include "validator/validator.h" #include "services/localzone.h" #include "services/view.h" +#include "services/authzone.h" #include "respip/respip.h" #include "sldns/sbuffer.h" #ifdef HAVE_GETOPT_H @@ -573,6 +574,17 @@ check_hints(struct config_file* cfg) hints_delete(hints); } +/** check auth zones */ +static void +check_auth(struct config_file* cfg) +{ + struct auth_zones* az = auth_zones_create(); + if(!az || !auth_zones_apply_cfg(az, cfg, 0, NULL)) { + fatal_exit("Could not setup authority zones"); + } + auth_zones_delete(az); +} + /** check config file */ static void checkconf(const char* cfgfile, const char* opt, int final) @@ -607,6 +619,7 @@ checkconf(const char* cfgfile, const char* opt, int final) #endif check_fwd(cfg); check_hints(cfg); + check_auth(cfg); printf("unbound-checkconf: no errors in %s\n", cfgfile); config_delete(cfg); } diff --git a/usr.sbin/unbound/smallapp/unbound-control.c b/usr.sbin/unbound/smallapp/unbound-control.c index 4b3efc134dc..086afa8ddde 100644 --- a/usr.sbin/unbound/smallapp/unbound-control.c +++ b/usr.sbin/unbound/smallapp/unbound-control.c @@ -254,6 +254,8 @@ static void print_mem(struct ub_shm_stat_info* shm_stat) #ifdef USE_DNSCRYPT PR_LL("mem.cache.dnscrypt_shared_secret", shm_stat->mem.dnscrypt_shared_secret); + PR_LL("mem.cache.dnscrypt_nonce", + shm_stat->mem.dnscrypt_nonce); #endif } @@ -360,6 +362,9 @@ static void print_extended(struct ub_stats_info* s) s->svr.shared_secret_cache_count); PR_UL("num.query.dnscrypt.shared_secret.cachemiss", s->svr.num_query_dnscrypt_secret_missed_cache); + PR_UL("dnscrypt_nonce.cache.count", s->svr.nonce_cache_count); + PR_UL("num.query.dnscrypt.replay", + s->svr.num_query_dnscrypt_replay); #endif /* USE_DNSCRYPT */ } @@ -721,7 +726,7 @@ int main(int argc, char* argv[]) WSADATA wsa_data; #endif #ifdef USE_THREAD_DEBUG - /* stop the file output from unbound-control, overwites the servers */ + /* stop the file output from unbound-control, overwrites the servers */ extern int check_locking_order; check_locking_order = 0; #endif /* USE_THREAD_DEBUG */ diff --git a/usr.sbin/unbound/smallapp/unbound-host.c b/usr.sbin/unbound/smallapp/unbound-host.c index d7a36a23193..93b5e487001 100644 --- a/usr.sbin/unbound/smallapp/unbound-host.c +++ b/usr.sbin/unbound/smallapp/unbound-host.c @@ -209,6 +209,7 @@ massage_class(const char* c) static const char* secure_str(struct ub_result* result) { + if(result->rcode != 0 && result->rcode != 3) return "(error)"; if(result->secure) return "(secure)"; if(result->bogus) return "(BOGUS (security failure))"; return "(insecure)"; diff --git a/usr.sbin/unbound/util/config_file.c b/usr.sbin/unbound/util/config_file.c index 796c1a61f26..073bc02ce48 100644 --- a/usr.sbin/unbound/util/config_file.c +++ b/usr.sbin/unbound/util/config_file.c @@ -69,9 +69,9 @@ #include <pwd.h> #endif -/** from cfg username, after daemonise setup performed */ +/** from cfg username, after daemonize setup performed */ uid_t cfg_uid = (uid_t)-1; -/** from cfg username, after daemonise setup performed */ +/** from cfg username, after daemonize setup performed */ gid_t cfg_gid = (gid_t)-1; /** for debug allow small timeout values for fast rollovers */ int autr_permit_small_holddown = 0; @@ -101,6 +101,7 @@ config_create(void) cfg->do_udp = 1; cfg->do_tcp = 1; cfg->tcp_upstream = 0; + cfg->udp_upstream_without_downstream = 0; cfg->tcp_mss = 0; cfg->outgoing_tcp_mss = 0; cfg->ssl_service_key = NULL; @@ -176,6 +177,7 @@ config_create(void) cfg->out_ifs = NULL; cfg->stubs = NULL; cfg->forwards = NULL; + cfg->auths = NULL; #ifdef CLIENT_SUBNET cfg->client_subnet = NULL; cfg->client_subnet_zone = NULL; @@ -281,9 +283,12 @@ config_create(void) cfg->dnscrypt_port = 0; cfg->dnscrypt_provider = NULL; cfg->dnscrypt_provider_cert = NULL; + cfg->dnscrypt_provider_cert_rotated = NULL; cfg->dnscrypt_secret_key = NULL; cfg->dnscrypt_shared_secret_cache_size = 4*1024*1024; cfg->dnscrypt_shared_secret_cache_slabs = 4; + cfg->dnscrypt_nonce_cache_size = 4*1024*1024; + cfg->dnscrypt_nonce_cache_slabs = 4; #ifdef USE_IPSECMOD cfg->ipsecmod_enabled = 1; cfg->ipsecmod_ignore_bogus = 0; @@ -371,6 +376,10 @@ struct config_file* config_create_forlib(void) /** put string into strlist */ #define S_STRLIST(str, var) if(strcmp(opt, str)==0) \ { return cfg_strlist_insert(&cfg->var, strdup(val)); } +/** put string into strlist if not present yet*/ +#define S_STRLIST_UNIQ(str, var) if(strcmp(opt, str)==0) \ + { if(cfg_strlist_find(cfg->var, val)) { return 0;} \ + return cfg_strlist_insert(&cfg->var, strdup(val)); } int config_set_option(struct config_file* cfg, const char* opt, const char* val) @@ -426,6 +435,8 @@ int config_set_option(struct config_file* cfg, const char* opt, else S_YNO("do-udp:", do_udp) else S_YNO("do-tcp:", do_tcp) else S_YNO("tcp-upstream:", tcp_upstream) + else S_YNO("udp-upstream-without-downstream:", + udp_upstream_without_downstream) else S_NUMBER_NONZERO("tcp-mss:", tcp_mss) else S_NUMBER_NONZERO("outgoing-tcp-mss:", outgoing_tcp_mss) else S_YNO("ssl-upstream:", ssl_upstream) @@ -565,12 +576,17 @@ int config_set_option(struct config_file* cfg, const char* opt, else S_YNO("dnscrypt-enable:", dnscrypt) else S_NUMBER_NONZERO("dnscrypt-port:", dnscrypt_port) else S_STR("dnscrypt-provider:", dnscrypt_provider) - else S_STRLIST("dnscrypt-provider-cert:", dnscrypt_provider_cert) - else S_STRLIST("dnscrypt-secret-key:", dnscrypt_secret_key) + else S_STRLIST_UNIQ("dnscrypt-provider-cert:", dnscrypt_provider_cert) + else S_STRLIST("dnscrypt-provider-cert-rotated:", dnscrypt_provider_cert_rotated) + else S_STRLIST_UNIQ("dnscrypt-secret-key:", dnscrypt_secret_key) else S_MEMSIZE("dnscrypt-shared-secret-cache-size:", dnscrypt_shared_secret_cache_size) else S_POW2("dnscrypt-shared-secret-cache-slabs:", dnscrypt_shared_secret_cache_slabs) + else S_MEMSIZE("dnscrypt-nonce-cache-size:", + dnscrypt_nonce_cache_size) + else S_POW2("dnscrypt-nonce-cache-slabs:", + dnscrypt_nonce_cache_slabs) #endif else if(strcmp(opt, "ip-ratelimit:") == 0) { IS_NUMBER_OR_ZERO; cfg->ip_ratelimit = atoi(val); @@ -619,7 +635,7 @@ int config_set_option(struct config_file* cfg, const char* opt, * interface, outgoing-interface, access-control, * stub-zone, name, stub-addr, stub-host, stub-prime * forward-first, stub-first, forward-ssl-upstream, - * stub-ssl-upstream, forward-zone, + * stub-ssl-upstream, forward-zone, auth-zone * name, forward-addr, forward-host, * ratelimit-for-domain, ratelimit-below-domain, * local-zone-tag, access-control-view, @@ -828,6 +844,7 @@ config_get_option(struct config_file* cfg, const char* opt, else O_YNO(opt, "do-udp", do_udp) else O_YNO(opt, "do-tcp", do_tcp) else O_YNO(opt, "tcp-upstream", tcp_upstream) + else O_YNO(opt, "udp-upstream-without-downstream", udp_upstream_without_downstream) else O_DEC(opt, "tcp-mss", tcp_mss) else O_DEC(opt, "outgoing-tcp-mss", outgoing_tcp_mss) else O_YNO(opt, "ssl-upstream", ssl_upstream) @@ -931,11 +948,16 @@ config_get_option(struct config_file* cfg, const char* opt, else O_DEC(opt, "dnscrypt-port", dnscrypt_port) else O_STR(opt, "dnscrypt-provider", dnscrypt_provider) else O_LST(opt, "dnscrypt-provider-cert", dnscrypt_provider_cert) + else O_LST(opt, "dnscrypt-provider-cert-rotated", dnscrypt_provider_cert_rotated) else O_LST(opt, "dnscrypt-secret-key", dnscrypt_secret_key) else O_MEM(opt, "dnscrypt-shared-secret-cache-size", dnscrypt_shared_secret_cache_size) else O_DEC(opt, "dnscrypt-shared-secret-cache-slabs", dnscrypt_shared_secret_cache_slabs) + else O_MEM(opt, "dnscrypt-nonce-cache-size", + dnscrypt_nonce_cache_size) + else O_DEC(opt, "dnscrypt-nonce-cache-slabs", + dnscrypt_nonce_cache_slabs) #endif else O_YNO(opt, "unblock-lan-zones", unblock_lan_zones) else O_YNO(opt, "insecure-lan-zones", insecure_lan_zones) @@ -1144,6 +1166,28 @@ config_deltrplstrlist(struct config_str3list* p) } void +config_delauth(struct config_auth* p) +{ + if(!p) return; + free(p->name); + config_delstrlist(p->masters); + config_delstrlist(p->urls); + free(p->zonefile); + free(p); +} + +void +config_delauths(struct config_auth* p) +{ + struct config_auth* np; + while(p) { + np = p->next; + config_delauth(p); + p = np; + } +} + +void config_delstub(struct config_stub* p) { if(!p) return; @@ -1228,6 +1272,7 @@ config_delete(struct config_file* cfg) config_del_strarray(cfg->out_ifs, cfg->num_out_ifs); config_delstubs(cfg->stubs); config_delstubs(cfg->forwards); + config_delauths(cfg->auths); config_delviews(cfg->views); config_delstrlist(cfg->donotqueryaddrs); config_delstrlist(cfg->root_hints); @@ -1444,6 +1489,22 @@ cfg_region_strlist_insert(struct regional* region, return 1; } +struct config_strlist* +cfg_strlist_find(struct config_strlist* head, const char *item) +{ + struct config_strlist *s = head; + if(!head){ + return NULL; + } + while(s) { + if(strcmp(s->str, item) == 0) { + return s; + } + s = s->next; + } + return NULL; +} + int cfg_strlist_insert(struct config_strlist** head, char* item) { diff --git a/usr.sbin/unbound/util/config_file.h b/usr.sbin/unbound/util/config_file.h index fdc48111e30..2fc6fdfb4cd 100644 --- a/usr.sbin/unbound/util/config_file.h +++ b/usr.sbin/unbound/util/config_file.h @@ -42,6 +42,7 @@ #ifndef UTIL_CONFIG_FILE_H #define UTIL_CONFIG_FILE_H struct config_stub; +struct config_auth; struct config_view; struct config_strlist; struct config_str2list; @@ -84,6 +85,8 @@ struct config_file { int do_tcp; /** tcp upstream queries (no UDP upstream queries) */ int tcp_upstream; + /** udp upstream enabled when no UDP downstream is enabled (do_udp no)*/ + int udp_upstream_without_downstream; /** maximum segment size of tcp socket which queries are answered */ int tcp_mss; /** maximum segment size of tcp socket for outgoing queries */ @@ -168,6 +171,8 @@ struct config_file { struct config_stub* stubs; /** the forward zone definitions, linked list */ struct config_stub* forwards; + /** the auth zone definitions, linked list */ + struct config_auth* auths; /** the views definitions, linked list */ struct config_view* views; /** list of donotquery addresses, linked list */ @@ -464,11 +469,18 @@ struct config_file { struct config_strlist* dnscrypt_secret_key; /** dnscrypt provider certs 1.cert */ struct config_strlist* dnscrypt_provider_cert; + /** dnscrypt provider certs 1.cert which have been rotated and should not be + * advertised through DNS's providername TXT record but are required to be + * able to handle existing traffic using the old cert. */ + struct config_strlist* dnscrypt_provider_cert_rotated; /** memory size in bytes for dnscrypt shared secrets cache */ size_t dnscrypt_shared_secret_cache_size; /** number of slabs for dnscrypt shared secrets cache */ size_t dnscrypt_shared_secret_cache_slabs; - + /** memory size in bytes for dnscrypt nonces cache */ + size_t dnscrypt_nonce_cache_size; + /** number of slabs for dnscrypt nonces cache */ + size_t dnscrypt_nonce_cache_slabs; /** IPsec module */ #ifdef USE_IPSECMOD /** false to bypass the IPsec module */ @@ -494,9 +506,9 @@ struct config_file { #endif }; -/** from cfg username, after daemonise setup performed */ +/** from cfg username, after daemonize setup performed */ extern uid_t cfg_uid; -/** from cfg username, after daemonise setup performed */ +/** from cfg username, after daemonize setup performed */ extern gid_t cfg_gid; /** debug and enable small timeouts */ extern int autr_permit_small_holddown; @@ -522,6 +534,26 @@ struct config_stub { }; /** + * Auth config options + */ +struct config_auth { + /** next in list */ + struct config_auth* next; + /** domain name (in text) of the auth apex domain */ + char* name; + /** list of masters */ + struct config_strlist* masters; + /** list of urls */ + struct config_strlist* urls; + /** zonefile (or NULL) */ + char* zonefile; + /** provide downstream answers */ + int for_downstream; + /** provide upstream answers */ + int for_upstream; +}; + +/** * View config options */ struct config_view { @@ -716,6 +748,15 @@ char* config_collate_cat(struct config_strlist* list); int cfg_strlist_append(struct config_strlist_head* list, char* item); /** + * Find string in strlist. + * @param head: pointer to strlist head variable. + * @param item: the item to search for. + * @return: the element in the list when found, NULL otherwise. + */ +struct config_strlist* cfg_strlist_find(struct config_strlist* head, + const char* item); + +/** * Insert string into strlist. * @param head: pointer to strlist head variable. * @param item: new item. malloced by caller. If NULL the insertion fails. @@ -803,6 +844,18 @@ void config_delstub(struct config_stub* p); void config_delstubs(struct config_stub* list); /** + * Delete an auth item + * @param p: auth item + */ +void config_delauth(struct config_auth* p); + +/** + * Delete items in config auth list. + * @param list: list. + */ +void config_delauths(struct config_auth* list); + +/** * Delete a view item * @param p: view item */ diff --git a/usr.sbin/unbound/util/configlexer.lex b/usr.sbin/unbound/util/configlexer.lex index 0c9a4df0429..793c70bcb8a 100644 --- a/usr.sbin/unbound/util/configlexer.lex +++ b/usr.sbin/unbound/util/configlexer.lex @@ -14,7 +14,6 @@ #endif #include <ctype.h> -#include <string.h> #include <strings.h> #ifdef HAVE_GLOB_H # include <glob.h> @@ -295,6 +294,12 @@ forward-addr{COLON} { YDVAR(1, VAR_FORWARD_ADDR) } forward-host{COLON} { YDVAR(1, VAR_FORWARD_HOST) } forward-first{COLON} { YDVAR(1, VAR_FORWARD_FIRST) } forward-ssl-upstream{COLON} { YDVAR(1, VAR_FORWARD_SSL_UPSTREAM) } +auth-zone{COLON} { YDVAR(0, VAR_AUTH_ZONE) } +zonefile{COLON} { YDVAR(1, VAR_ZONEFILE) } +master{COLON} { YDVAR(1, VAR_MASTER) } +url{COLON} { YDVAR(1, VAR_URL) } +for-downstream{COLON} { YDVAR(1, VAR_FOR_DOWNSTREAM) } +for-upstream{COLON} { YDVAR(1, VAR_FOR_UPSTREAM) } view{COLON} { YDVAR(0, VAR_VIEW) } view-first{COLON} { YDVAR(1, VAR_VIEW_FIRST) } do-not-query-address{COLON} { YDVAR(1, VAR_DO_NOT_QUERY_ADDRESS) } @@ -417,10 +422,13 @@ dnscrypt-port{COLON} { YDVAR(1, VAR_DNSCRYPT_PORT) } dnscrypt-provider{COLON} { YDVAR(1, VAR_DNSCRYPT_PROVIDER) } dnscrypt-secret-key{COLON} { YDVAR(1, VAR_DNSCRYPT_SECRET_KEY) } dnscrypt-provider-cert{COLON} { YDVAR(1, VAR_DNSCRYPT_PROVIDER_CERT) } +dnscrypt-provider-cert-rotated{COLON} { YDVAR(1, VAR_DNSCRYPT_PROVIDER_CERT_ROTATED) } dnscrypt-shared-secret-cache-size{COLON} { YDVAR(1, VAR_DNSCRYPT_SHARED_SECRET_CACHE_SIZE) } dnscrypt-shared-secret-cache-slabs{COLON} { YDVAR(1, VAR_DNSCRYPT_SHARED_SECRET_CACHE_SLABS) } +dnscrypt-nonce-cache-size{COLON} { YDVAR(1, VAR_DNSCRYPT_NONCE_CACHE_SIZE) } +dnscrypt-nonce-cache-slabs{COLON} { YDVAR(1, VAR_DNSCRYPT_NONCE_CACHE_SLABS) } ipsecmod-enabled{COLON} { YDVAR(1, VAR_IPSECMOD_ENABLED) } ipsecmod-ignore-bogus{COLON} { YDVAR(1, VAR_IPSECMOD_IGNORE_BOGUS) } ipsecmod-hook{COLON} { YDVAR(1, VAR_IPSECMOD_HOOK) } @@ -430,6 +438,7 @@ ipsecmod-strict{COLON} { YDVAR(1, VAR_IPSECMOD_STRICT) } cachedb{COLON} { YDVAR(0, VAR_CACHEDB) } backend{COLON} { YDVAR(1, VAR_CACHEDB_BACKEND) } secret-seed{COLON} { YDVAR(1, VAR_CACHEDB_SECRETSEED) } +udp-upstream-without-downstream{COLON} { YDVAR(1, VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM) } <INITIAL,val>{NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++; } /* Quoted strings. Strip leading and ending quotes */ diff --git a/usr.sbin/unbound/util/configparser.y b/usr.sbin/unbound/util/configparser.y index 7b41b1d767d..f0069599479 100644 --- a/usr.sbin/unbound/util/configparser.y +++ b/usr.sbin/unbound/util/configparser.y @@ -144,20 +144,24 @@ extern struct config_parser_state* cfg_parser; %token VAR_USE_SYSTEMD VAR_SHM_ENABLE VAR_SHM_KEY %token VAR_DNSCRYPT VAR_DNSCRYPT_ENABLE VAR_DNSCRYPT_PORT VAR_DNSCRYPT_PROVIDER %token VAR_DNSCRYPT_SECRET_KEY VAR_DNSCRYPT_PROVIDER_CERT +%token VAR_DNSCRYPT_PROVIDER_CERT_ROTATED %token VAR_DNSCRYPT_SHARED_SECRET_CACHE_SIZE %token VAR_DNSCRYPT_SHARED_SECRET_CACHE_SLABS +%token VAR_DNSCRYPT_NONCE_CACHE_SIZE +%token VAR_DNSCRYPT_NONCE_CACHE_SLABS %token VAR_IPSECMOD_ENABLED VAR_IPSECMOD_HOOK VAR_IPSECMOD_IGNORE_BOGUS %token VAR_IPSECMOD_MAX_TTL VAR_IPSECMOD_WHITELIST VAR_IPSECMOD_STRICT %token VAR_CACHEDB VAR_CACHEDB_BACKEND VAR_CACHEDB_SECRETSEED +%token VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM VAR_FOR_UPSTREAM +%token VAR_AUTH_ZONE VAR_ZONEFILE VAR_MASTER VAR_URL VAR_FOR_DOWNSTREAM %% toplevelvars: /* empty */ | toplevelvars toplevelvar ; toplevelvar: serverstart contents_server | stubstart contents_stub | forwardstart contents_forward | pythonstart contents_py | - rcstart contents_rc | dtstart contents_dt | viewstart - contents_view | - dnscstart contents_dnsc | - cachedbstart contents_cachedb + rcstart contents_rc | dtstart contents_dt | viewstart contents_view | + dnscstart contents_dnsc | cachedbstart contents_cachedb | + authstart contents_auth ; /* server: declaration */ @@ -237,7 +241,8 @@ content_server: server_num_threads | server_verbosity | server_port | server_hide_trustanchor | server_trust_anchor_signaling | server_ipsecmod_enabled | server_ipsecmod_hook | server_ipsecmod_ignore_bogus | server_ipsecmod_max_ttl | - server_ipsecmod_whitelist | server_ipsecmod_strict + server_ipsecmod_whitelist | server_ipsecmod_strict | + server_udp_upstream_without_downstream ; stubstart: VAR_STUB_ZONE { @@ -292,6 +297,26 @@ contents_view: contents_view content_view content_view: view_name | view_local_zone | view_local_data | view_first | view_response_ip | view_response_ip_data | view_local_data_ptr ; +authstart: VAR_AUTH_ZONE + { + struct config_auth* s; + OUTYY(("\nP(auth_zone:)\n")); + s = (struct config_auth*)calloc(1, sizeof(struct config_auth)); + if(s) { + s->next = cfg_parser->cfg->auths; + cfg_parser->cfg->auths = s; + /* defaults for auth zone */ + s->for_downstream = 1; + s->for_upstream = 1; + } else + yyerror("out of memory"); + } + ; +contents_auth: contents_auth content_auth + | ; +content_auth: auth_name | auth_zonefile | auth_master | auth_url | + auth_for_downstream | auth_for_upstream + ; server_num_threads: VAR_NUM_THREADS STRING_ARG { OUTYY(("P(server_num_threads:%s)\n", $2)); @@ -411,7 +436,7 @@ server_client_subnet_opcode: VAR_CLIENT_SUBNET_OPCODE STRING_ARG { #ifdef CLIENT_SUBNET OUTYY(("P(client_subnet_opcode:%s)\n", $2)); - OUTYY(("P(Depricated option, ignoring)\n")); + OUTYY(("P(Deprecated option, ignoring)\n")); #else OUTYY(("P(Compiled without edns subnet option, ignoring)\n")); #endif @@ -606,6 +631,15 @@ server_tcp_upstream: VAR_TCP_UPSTREAM STRING_ARG free($2); } ; +server_udp_upstream_without_downstream: VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM STRING_ARG + { + OUTYY(("P(server_udp_upstream_without_downstream:%s)\n", $2)); + if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) + yyerror("expected yes or no."); + else cfg_parser->cfg->udp_upstream_without_downstream = (strcmp($2, "yes")==0); + free($2); + } + ; server_ssl_upstream: VAR_SSL_UPSTREAM STRING_ARG { OUTYY(("P(server_ssl_upstream:%s)\n", $2)); @@ -1985,6 +2019,57 @@ forward_ssl_upstream: VAR_FORWARD_SSL_UPSTREAM STRING_ARG free($2); } ; +auth_name: VAR_NAME STRING_ARG + { + OUTYY(("P(name:%s)\n", $2)); + if(cfg_parser->cfg->auths->name) + yyerror("auth name override, there must be one name " + "for one auth-zone"); + free(cfg_parser->cfg->auths->name); + cfg_parser->cfg->auths->name = $2; + } + ; +auth_zonefile: VAR_ZONEFILE STRING_ARG + { + OUTYY(("P(zonefile:%s)\n", $2)); + free(cfg_parser->cfg->auths->zonefile); + cfg_parser->cfg->auths->zonefile = $2; + } + ; +auth_master: VAR_MASTER STRING_ARG + { + OUTYY(("P(master:%s)\n", $2)); + if(!cfg_strlist_insert(&cfg_parser->cfg->auths->masters, $2)) + yyerror("out of memory"); + } + ; +auth_url: VAR_URL STRING_ARG + { + OUTYY(("P(url:%s)\n", $2)); + if(!cfg_strlist_insert(&cfg_parser->cfg->auths->urls, $2)) + yyerror("out of memory"); + } + ; +auth_for_downstream: VAR_FOR_DOWNSTREAM STRING_ARG + { + OUTYY(("P(for-downstream:%s)\n", $2)); + if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) + yyerror("expected yes or no."); + else cfg_parser->cfg->auths->for_downstream = + (strcmp($2, "yes")==0); + free($2); + } + ; +auth_for_upstream: VAR_FOR_UPSTREAM STRING_ARG + { + OUTYY(("P(for-upstream:%s)\n", $2)); + if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) + yyerror("expected yes or no."); + else cfg_parser->cfg->auths->for_upstream = + (strcmp($2, "yes")==0); + free($2); + } + ; view_name: VAR_NAME STRING_ARG { OUTYY(("P(name:%s)\n", $2)); @@ -2326,8 +2411,11 @@ contents_dnsc: contents_dnsc content_dnsc content_dnsc: dnsc_dnscrypt_enable | dnsc_dnscrypt_port | dnsc_dnscrypt_provider | dnsc_dnscrypt_secret_key | dnsc_dnscrypt_provider_cert | + dnsc_dnscrypt_provider_cert_rotated | dnsc_dnscrypt_shared_secret_cache_size | - dnsc_dnscrypt_shared_secret_cache_slabs + dnsc_dnscrypt_shared_secret_cache_slabs | + dnsc_dnscrypt_nonce_cache_size | + dnsc_dnscrypt_nonce_cache_slabs ; dnsc_dnscrypt_enable: VAR_DNSCRYPT_ENABLE STRING_ARG { @@ -2359,13 +2447,24 @@ dnsc_dnscrypt_provider: VAR_DNSCRYPT_PROVIDER STRING_ARG dnsc_dnscrypt_provider_cert: VAR_DNSCRYPT_PROVIDER_CERT STRING_ARG { OUTYY(("P(dnsc_dnscrypt_provider_cert:%s)\n", $2)); + if(cfg_strlist_find(cfg_parser->cfg->dnscrypt_provider_cert, $2)) + log_warn("dnscrypt-provider-cert %s is a duplicate", $2); if(!cfg_strlist_insert(&cfg_parser->cfg->dnscrypt_provider_cert, $2)) fatal_exit("out of memory adding dnscrypt-provider-cert"); } ; +dnsc_dnscrypt_provider_cert_rotated: VAR_DNSCRYPT_PROVIDER_CERT_ROTATED STRING_ARG + { + OUTYY(("P(dnsc_dnscrypt_provider_cert_rotated:%s)\n", $2)); + if(!cfg_strlist_insert(&cfg_parser->cfg->dnscrypt_provider_cert_rotated, $2)) + fatal_exit("out of memory adding dnscrypt-provider-cert-rotated"); + } + ; dnsc_dnscrypt_secret_key: VAR_DNSCRYPT_SECRET_KEY STRING_ARG { OUTYY(("P(dnsc_dnscrypt_secret_key:%s)\n", $2)); + if(cfg_strlist_find(cfg_parser->cfg->dnscrypt_secret_key, $2)) + log_warn("dnscrypt-secret-key: %s is a duplicate", $2); if(!cfg_strlist_insert(&cfg_parser->cfg->dnscrypt_secret_key, $2)) fatal_exit("out of memory adding dnscrypt-secret-key"); } @@ -2391,6 +2490,27 @@ dnsc_dnscrypt_shared_secret_cache_slabs: VAR_DNSCRYPT_SHARED_SECRET_CACHE_SLABS free($2); } ; +dnsc_dnscrypt_nonce_cache_size: VAR_DNSCRYPT_NONCE_CACHE_SIZE STRING_ARG + { + OUTYY(("P(dnscrypt_nonce_cache_size:%s)\n", $2)); + if(!cfg_parse_memsize($2, &cfg_parser->cfg->dnscrypt_nonce_cache_size)) + yyerror("memory size expected"); + free($2); + } + ; +dnsc_dnscrypt_nonce_cache_slabs: VAR_DNSCRYPT_NONCE_CACHE_SLABS STRING_ARG + { + OUTYY(("P(dnscrypt_nonce_cache_slabs:%s)\n", $2)); + if(atoi($2) == 0) + yyerror("number expected"); + else { + cfg_parser->cfg->dnscrypt_nonce_cache_slabs = atoi($2); + if(!is_pow2(cfg_parser->cfg->dnscrypt_nonce_cache_slabs)) + yyerror("must be a power of 2"); + } + free($2); + } + ; cachedbstart: VAR_CACHEDB { OUTYY(("\nP(cachedb:)\n")); diff --git a/usr.sbin/unbound/util/data/msgreply.c b/usr.sbin/unbound/util/data/msgreply.c index 6d80cce4cb5..15361769250 100644 --- a/usr.sbin/unbound/util/data/msgreply.c +++ b/usr.sbin/unbound/util/data/msgreply.c @@ -896,6 +896,25 @@ reply_all_rrsets_secure(struct reply_info* rep) return 1; } +struct reply_info* +parse_reply_in_temp_region(sldns_buffer* pkt, struct regional* region, + struct query_info* qi) +{ + struct reply_info* rep; + struct msg_parse* msg; + if(!(msg = regional_alloc(region, sizeof(*msg)))) { + return NULL; + } + memset(msg, 0, sizeof(*msg)); + sldns_buffer_set_position(pkt, 0); + if(parse_packet(pkt, msg, region) != 0) + return 0; + if(!parse_create_msg(pkt, msg, NULL, qi, &rep, region)) { + return 0; + } + return rep; +} + int edns_opt_append(struct edns_data* edns, struct regional* region, uint16_t code, size_t len, uint8_t* data) { @@ -992,6 +1011,9 @@ static int inplace_cb_reply_call_generic( { struct inplace_cb* cb; struct edns_option* opt_list_out = NULL; +#if defined(EXPORT_ALL_SYMBOLS) + (void)type; /* param not used when fptr_ok disabled */ +#endif if(qstate) opt_list_out = qstate->edns_opts_front_out; for(cb=callback_list; cb; cb=cb->next) { diff --git a/usr.sbin/unbound/util/data/msgreply.h b/usr.sbin/unbound/util/data/msgreply.h index acbdd3deb61..60e6438a84a 100644 --- a/usr.sbin/unbound/util/data/msgreply.h +++ b/usr.sbin/unbound/util/data/msgreply.h @@ -285,6 +285,10 @@ int parse_create_msg(struct sldns_buffer* pkt, struct msg_parse* msg, struct alloc_cache* alloc, struct query_info* qinf, struct reply_info** rep, struct regional* region); +/** get msg reply struct (in temp region) */ +struct reply_info* parse_reply_in_temp_region(struct sldns_buffer* pkt, + struct regional* region, struct query_info* qi); + /** * Sorts the ref array. * @param rep: reply info. rrsets must be filled in. @@ -634,7 +638,7 @@ int inplace_cb_edns_back_parsed_call(struct module_env* env, struct module_qstate* qstate); /** - * Call the registered functions in the inplace_cb_query_reponse linked list. + * Call the registered functions in the inplace_cb_query_response linked list. * This function is going to get called after receiving a reply from a * nameserver. * @param env: module environment. diff --git a/usr.sbin/unbound/util/fptr_wlist.c b/usr.sbin/unbound/util/fptr_wlist.c index 81ef487c587..32a6ba02e28 100644 --- a/usr.sbin/unbound/util/fptr_wlist.c +++ b/usr.sbin/unbound/util/fptr_wlist.c @@ -98,6 +98,8 @@ fptr_whitelist_comm_point(comm_point_callback_type *fptr) else if(fptr == &outnet_udp_cb) return 1; else if(fptr == &outnet_tcp_cb) return 1; else if(fptr == &tube_handle_listen) return 1; + else if(fptr == &auth_xfer_probe_udp_callback) return 1; + else if(fptr == &auth_xfer_transfer_tcp_callback) return 1; return 0; } @@ -122,6 +124,8 @@ fptr_whitelist_comm_timer(void (*fptr)(void*)) #ifdef UB_ON_WINDOWS else if(fptr == &wsvc_cron_cb) return 1; #endif + else if(fptr == &auth_xfer_timer) return 1; + else if(fptr == &auth_xfer_probe_timer_callback) return 1; return 0; } @@ -215,6 +219,7 @@ fptr_whitelist_rbtree_cmp(int (*fptr) (const void *, const void *)) else if(fptr == &view_cmp) return 1; else if(fptr == &auth_zone_cmp) return 1; else if(fptr == &auth_data_cmp) return 1; + else if(fptr == &auth_xfer_cmp) return 1; return 0; } @@ -233,6 +238,7 @@ fptr_whitelist_hash_sizefunc(lruhash_sizefunc_type fptr) #endif #ifdef USE_DNSCRYPT else if(fptr == &dnsc_shared_secrets_sizefunc) return 1; + else if(fptr == &dnsc_nonces_sizefunc) return 1; #endif return 0; } @@ -249,6 +255,7 @@ fptr_whitelist_hash_compfunc(lruhash_compfunc_type fptr) else if(fptr == &test_slabhash_compfunc) return 1; #ifdef USE_DNSCRYPT else if(fptr == &dnsc_shared_secrets_compfunc) return 1; + else if(fptr == &dnsc_nonces_compfunc) return 1; #endif return 0; } @@ -265,6 +272,7 @@ fptr_whitelist_hash_delkeyfunc(lruhash_delkeyfunc_type fptr) else if(fptr == &test_slabhash_delkey) return 1; #ifdef USE_DNSCRYPT else if(fptr == &dnsc_shared_secrets_delkeyfunc) return 1; + else if(fptr == &dnsc_nonces_delkeyfunc) return 1; #endif return 0; } @@ -283,6 +291,7 @@ fptr_whitelist_hash_deldatafunc(lruhash_deldatafunc_type fptr) #endif #ifdef USE_DNSCRYPT else if(fptr == &dnsc_shared_secrets_deldatafunc) return 1; + else if(fptr == &dnsc_nonces_deldatafunc) return 1; #endif return 0; } @@ -505,6 +514,8 @@ int fptr_whitelist_mesh_cb(mesh_cb_func_type fptr) else if(fptr == &libworker_bg_done_cb) return 1; else if(fptr == &libworker_event_done_cb) return 1; else if(fptr == &probe_answer_cb) return 1; + else if(fptr == &auth_xfer_probe_lookup_callback) return 1; + else if(fptr == &auth_xfer_transfer_lookup_callback) return 1; return 0; } diff --git a/usr.sbin/unbound/util/iana_ports.inc b/usr.sbin/unbound/util/iana_ports.inc index 58c69fd1c1a..2307f6a02f8 100644 --- a/usr.sbin/unbound/util/iana_ports.inc +++ b/usr.sbin/unbound/util/iana_ports.inc @@ -4896,6 +4896,7 @@ 9026, 9060, 9080, +9081, 9084, 9085, 9086, @@ -5390,6 +5391,7 @@ 33331, 33334, 33434, +33435, 33656, 34249, 34378, diff --git a/usr.sbin/unbound/util/module.h b/usr.sbin/unbound/util/module.h index 6e75539d916..73db994bd7a 100644 --- a/usr.sbin/unbound/util/module.h +++ b/usr.sbin/unbound/util/module.h @@ -166,6 +166,9 @@ struct query_info; struct edns_data; struct regional; struct worker; +struct comm_base; +struct auth_zones; +struct outside_network; struct module_qstate; struct ub_randstate; struct mesh_area; @@ -416,7 +419,7 @@ struct module_env { /** * Kill newly attached sub. If attach_sub returns newq for * initialisation, but that fails, then this routine will cleanup and - * delete the fresly created sub. + * delete the freshly created sub. * @param newq: the new subquery that is no longer needed. * It is removed. */ @@ -445,6 +448,10 @@ struct module_env { struct sldns_buffer* scratch_buffer; /** internal data for daemon - worker thread. */ struct worker* worker; + /** the worker event base */ + struct comm_base* worker_base; + /** the outside network */ + struct outside_network* outnet; /** mesh area with query state dependencies */ struct mesh_area* mesh; /** allocation service */ @@ -468,6 +475,8 @@ struct module_env { struct val_neg_cache* neg_cache; /** the 5011-probe timer (if any) */ struct comm_timer* probe_timer; + /** auth zones */ + struct auth_zones* auth_zones; /** Mapping of forwarding zones to targets. * iterator forwarder information. per-thread, created by worker */ struct iter_forwards* fwds; @@ -608,6 +617,8 @@ struct module_qstate { int no_cache_lookup; /** whether modules should store answer in the cache */ int no_cache_store; + /** whether to refetch a fresh answer on finishing this state*/ + int need_refetch; /** * Attributes of clients that share the qstate that may affect IP-based diff --git a/usr.sbin/unbound/util/net_help.c b/usr.sbin/unbound/util/net_help.c index ce136a337cf..d99a2f974bc 100644 --- a/usr.sbin/unbound/util/net_help.c +++ b/usr.sbin/unbound/util/net_help.c @@ -645,7 +645,7 @@ listen_sslctx_setup(void* ctxt) #endif #if defined(SHA256_DIGEST_LENGTH) && defined(USE_ECDSA) /* if we have sha256, set the cipher list to have no known vulns */ - if(!SSL_CTX_set_cipher_list(ctx, "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256")) + if(!SSL_CTX_set_cipher_list(ctx, "TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-256-GCM-SHA384:TLS13-AES-128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256")) log_crypto_err("could not set cipher list with SSL_CTX_set_cipher_list"); #endif diff --git a/usr.sbin/unbound/util/netevent.c b/usr.sbin/unbound/util/netevent.c index 771cb6bc710..7c0ced83b74 100644 --- a/usr.sbin/unbound/util/netevent.c +++ b/usr.sbin/unbound/util/netevent.c @@ -648,7 +648,7 @@ comm_point_udp_ancil_callback(int fd, short event, void* arg) (void)comm_point_send_udp_msg_if(rep.c, rep.c->buffer, (struct sockaddr*)&rep.addr, rep.addrlen, &rep); } - if(rep.c->fd == -1) /* commpoint closed */ + if(!rep.c || rep.c->fd == -1) /* commpoint closed */ break; } #else @@ -711,7 +711,7 @@ comm_point_udp_callback(int fd, short event, void* arg) (void)comm_point_send_udp_msg(rep.c, buffer, (struct sockaddr*)&rep.addr, rep.addrlen); } - if(rep.c->fd != fd) /* commpoint closed to -1 or reused for + if(!rep.c || rep.c->fd != fd) /* commpoint closed to -1 or reused for another UDP port. Note rep.c cannot be reused with TCP fd. */ break; } @@ -1400,7 +1400,7 @@ comm_point_tcp_handle_write(int fd, struct comm_point* c) if (r == -1) { #if defined(EINPROGRESS) && defined(EWOULDBLOCK) /* Handshake is underway, maybe because no TFO cookie available. - Come back to write the messsage*/ + Come back to write the message*/ if(errno == EINPROGRESS || errno == EWOULDBLOCK) return 1; #endif diff --git a/usr.sbin/unbound/util/shm_side/shm_main.c b/usr.sbin/unbound/util/shm_side/shm_main.c index c0757ed7cde..a783c099b5a 100644 --- a/usr.sbin/unbound/util/shm_side/shm_main.c +++ b/usr.sbin/unbound/util/shm_side/shm_main.c @@ -254,6 +254,8 @@ void shm_main_run(struct worker *worker) if(worker->daemon->dnscenv) { shm_stat->mem.dnscrypt_shared_secret = (long long)slabhash_get_mem( worker->daemon->dnscenv->shared_secrets_cache); + shm_stat->mem.dnscrypt_nonce = (long long)slabhash_get_mem( + worker->daemon->dnscenv->nonces_cache); } #endif shm_stat->mem.val = (long long)mod_get_mem(&worker->env, diff --git a/usr.sbin/unbound/util/storage/slabhash.h b/usr.sbin/unbound/util/storage/slabhash.h index d00983fc155..2ecf6fe719a 100644 --- a/usr.sbin/unbound/util/storage/slabhash.h +++ b/usr.sbin/unbound/util/storage/slabhash.h @@ -107,7 +107,7 @@ void slabhash_clear(struct slabhash* table); * But entry->data is set to NULL before deletion, and put into * the existing entry. The data is then freed. * @param data: the data. - * @param cb_override: if not NULL overrides the cb_arg for deletfunc. + * @param cb_override: if not NULL overrides the cb_arg for deletefunc. */ void slabhash_insert(struct slabhash* table, hashvalue_type hash, struct lruhash_entry* entry, void* data, void* cb_override); diff --git a/usr.sbin/unbound/util/ub_event.c b/usr.sbin/unbound/util/ub_event.c index 3b92be1a302..fba2f248829 100644 --- a/usr.sbin/unbound/util/ub_event.c +++ b/usr.sbin/unbound/util/ub_event.c @@ -427,7 +427,7 @@ ub_winsock_tcp_wouldblock(struct ub_event* ev, int eventbits) void ub_comm_base_now(struct comm_base* cb) { - #ifdef USE_MINI_EVENT +#ifdef USE_MINI_EVENT /** minievent updates the time when it blocks. */ (void)cb; /* nothing to do */ #else /* !USE_MINI_EVENT */ diff --git a/usr.sbin/unbound/util/ub_event.h b/usr.sbin/unbound/util/ub_event.h index 9739e6d833e..b00a9bf6c26 100644 --- a/usr.sbin/unbound/util/ub_event.h +++ b/usr.sbin/unbound/util/ub_event.h @@ -67,7 +67,7 @@ const char* ub_event_get_version(void); /** Return the name, system and method for the pluggable event base */ void ub_get_event_sys(struct ub_event_base*, const char** n, const char** s, const char** m); -/** Return a default event base. In the deamon thess will be the only event +/** Return a default event base. In the daemon this will be the only event * bases used. */ struct ub_event_base* ub_default_event_base(int, time_t*, struct timeval*); diff --git a/usr.sbin/unbound/validator/autotrust.c b/usr.sbin/unbound/validator/autotrust.c index 5bf815b5ec1..7bc5577f4f6 100644 --- a/usr.sbin/unbound/validator/autotrust.c +++ b/usr.sbin/unbound/validator/autotrust.c @@ -1227,17 +1227,20 @@ void autr_write_file(struct module_env* env, struct trust_anchor* tp) * @param ve: validator environment (with options) for verification. * @param tp: trust point to verify with * @param rrset: DNSKEY rrset to verify. + * @param qstate: qstate with region. * @return false on failure, true if verification successful. */ static int verify_dnskey(struct module_env* env, struct val_env* ve, - struct trust_anchor* tp, struct ub_packed_rrset_key* rrset) + struct trust_anchor* tp, struct ub_packed_rrset_key* rrset, + struct module_qstate* qstate) { char* reason = NULL; uint8_t sigalg[ALGO_NEEDS_MAX+1]; int downprot = env->cfg->harden_algo_downgrade; enum sec_status sec = val_verify_DNSKEY_with_TA(env, ve, rrset, - tp->ds_rrset, tp->dnskey_rrset, downprot?sigalg:NULL, &reason); + tp->ds_rrset, tp->dnskey_rrset, downprot?sigalg:NULL, &reason, + qstate); /* sigalg is ignored, it returns algorithms signalled to exist, but * in 5011 there are no other rrsets to check. if downprot is * enabled, then it checks that the DNSKEY is signed with all @@ -1276,7 +1279,8 @@ min_expiry(struct module_env* env, struct packed_rrset_data* dd) /** Is rr self-signed revoked key */ static int rr_is_selfsigned_revoked(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key* dnskey_rrset, size_t i) + struct ub_packed_rrset_key* dnskey_rrset, size_t i, + struct module_qstate* qstate) { enum sec_status sec; char* reason = NULL; @@ -1285,7 +1289,7 @@ rr_is_selfsigned_revoked(struct module_env* env, struct val_env* ve, /* no algorithm downgrade protection necessary, if it is selfsigned * revoked it can be removed. */ sec = dnskey_verify_rrset(env, ve, dnskey_rrset, dnskey_rrset, i, - &reason); + &reason, LDNS_SECTION_ANSWER, qstate); return (sec == sec_status_secure); } @@ -1501,7 +1505,7 @@ init_events(struct trust_anchor* tp) static void check_contains_revoked(struct module_env* env, struct val_env* ve, struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset, - int* changed) + int* changed, struct module_qstate* qstate) { struct packed_rrset_data* dd = (struct packed_rrset_data*) dnskey_rrset->entry.data; @@ -1521,7 +1525,7 @@ check_contains_revoked(struct module_env* env, struct val_env* ve, } if(!ta) continue; /* key not found */ - if(rr_is_selfsigned_revoked(env, ve, dnskey_rrset, i)) { + if(rr_is_selfsigned_revoked(env, ve, dnskey_rrset, i, qstate)) { /* checked if there is an rrsig signed by this key. */ /* same keytag, but stored can be revoked already, so * compare keytags, with +0 or +128(REVOKE flag) */ @@ -2118,7 +2122,8 @@ autr_tp_remove(struct module_env* env, struct trust_anchor* tp, } int autr_process_prime(struct module_env* env, struct val_env* ve, - struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset) + struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset, + struct module_qstate* qstate) { int changed = 0; log_assert(tp && tp->autr); @@ -2154,12 +2159,12 @@ int autr_process_prime(struct module_env* env, struct val_env* ve, verbose(VERB_ALGO, "autotrust: no dnskey rrset"); /* no update of query_failed, because then we would have * to write to disk. But we cannot because we maybe are - * still 'initialising' with DS records, that we cannot write + * still 'initializing' with DS records, that we cannot write * in the full format (which only contains KSKs). */ return 1; /* trust point exists */ } /* check for revoked keys to remove immediately */ - check_contains_revoked(env, ve, tp, dnskey_rrset, &changed); + check_contains_revoked(env, ve, tp, dnskey_rrset, &changed, qstate); if(changed) { verbose(VERB_ALGO, "autotrust: revokedkeys, reassemble"); if(!autr_assemble(tp)) { @@ -2175,7 +2180,7 @@ int autr_process_prime(struct module_env* env, struct val_env* ve, } } /* verify the dnskey rrset and see if it is valid. */ - if(!verify_dnskey(env, ve, tp, dnskey_rrset)) { + if(!verify_dnskey(env, ve, tp, dnskey_rrset, qstate)) { verbose(VERB_ALGO, "autotrust: dnskey did not verify."); /* only increase failure count if this is not the first prime, * this means there was a previous successful probe */ diff --git a/usr.sbin/unbound/validator/autotrust.h b/usr.sbin/unbound/validator/autotrust.h index dbaf5126a0b..c549798f0e3 100644 --- a/usr.sbin/unbound/validator/autotrust.h +++ b/usr.sbin/unbound/validator/autotrust.h @@ -47,6 +47,7 @@ struct val_anchors; struct trust_anchor; struct ub_packed_rrset_key; struct module_env; +struct module_qstate; struct val_env; struct sldns_buffer; @@ -188,12 +189,14 @@ void autr_point_delete(struct trust_anchor* tp); * @param tp: trust anchor to process. * @param dnskey_rrset: DNSKEY rrset probed (can be NULL if bad prime result). * allocated in a region. Has not been validated yet. + * @param qstate: qstate with region. * @return false if trust anchor was revoked completely. * Otherwise logs errors to log, does not change return value. * On errors, likely the trust point has been unchanged. */ int autr_process_prime(struct module_env* env, struct val_env* ve, - struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset); + struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset, + struct module_qstate* qstate); /** * Debug printout of rfc5011 tracked anchors diff --git a/usr.sbin/unbound/validator/val_nsec.c b/usr.sbin/unbound/validator/val_nsec.c index 1e4f440ffc0..4604f3d6d42 100644 --- a/usr.sbin/unbound/validator/val_nsec.c +++ b/usr.sbin/unbound/validator/val_nsec.c @@ -176,7 +176,7 @@ val_nsec_proves_no_ds(struct ub_packed_rrset_key* nsec, static int nsec_verify_rrset(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* nsec, struct key_entry_key* kkey, - char** reason) + char** reason, struct module_qstate* qstate) { struct packed_rrset_data* d = (struct packed_rrset_data*) nsec->entry.data; @@ -185,7 +185,8 @@ nsec_verify_rrset(struct module_env* env, struct val_env* ve, rrset_check_sec_status(env->rrset_cache, nsec, *env->now); if(d->security == sec_status_secure) return 1; - d->security = val_verify_rrset_entry(env, ve, nsec, kkey, reason); + d->security = val_verify_rrset_entry(env, ve, nsec, kkey, reason, + LDNS_SECTION_AUTHORITY, qstate); if(d->security == sec_status_secure) { rrset_update_sec_status(env->rrset_cache, nsec, *env->now); return 1; @@ -196,7 +197,8 @@ nsec_verify_rrset(struct module_env* env, struct val_env* ve, enum sec_status val_nsec_prove_nodata_dsreply(struct module_env* env, struct val_env* ve, struct query_info* qinfo, struct reply_info* rep, - struct key_entry_key* kkey, time_t* proof_ttl, char** reason) + struct key_entry_key* kkey, time_t* proof_ttl, char** reason, + struct module_qstate* qstate) { struct ub_packed_rrset_key* nsec = reply_find_rrset_section_ns( rep, qinfo->qname, qinfo->qname_len, LDNS_RR_TYPE_NSEC, @@ -213,7 +215,7 @@ val_nsec_prove_nodata_dsreply(struct module_env* env, struct val_env* ve, * 1) this is a delegation point and there is no DS * 2) this is not a delegation point */ if(nsec) { - if(!nsec_verify_rrset(env, ve, nsec, kkey, reason)) { + if(!nsec_verify_rrset(env, ve, nsec, kkey, reason, qstate)) { verbose(VERB_ALGO, "NSEC RRset for the " "referral did not verify."); return sec_status_bogus; @@ -242,7 +244,8 @@ val_nsec_prove_nodata_dsreply(struct module_env* env, struct val_env* ve, i++) { if(rep->rrsets[i]->rk.type != htons(LDNS_RR_TYPE_NSEC)) continue; - if(!nsec_verify_rrset(env, ve, rep->rrsets[i], kkey, reason)) { + if(!nsec_verify_rrset(env, ve, rep->rrsets[i], kkey, reason, + qstate)) { verbose(VERB_ALGO, "NSEC for empty non-terminal " "did not verify."); return sec_status_bogus; diff --git a/usr.sbin/unbound/validator/val_nsec.h b/usr.sbin/unbound/validator/val_nsec.h index c031c9a3b85..4e71257bc35 100644 --- a/usr.sbin/unbound/validator/val_nsec.h +++ b/usr.sbin/unbound/validator/val_nsec.h @@ -46,6 +46,7 @@ #include "util/data/packed_rrset.h" struct val_env; struct module_env; +struct module_qstate; struct ub_packed_rrset_key; struct reply_info; struct query_info; @@ -64,6 +65,7 @@ struct key_entry_key; * @param kkey: key entry to use for verification of signatures. * @param proof_ttl: if secure, the TTL of how long this proof lasts. * @param reason: string explaining why bogus. + * @param qstate: qstate with region. * @return security status. * SECURE: proved absence of DS. * INSECURE: proved that this was not a delegation point. @@ -73,7 +75,7 @@ struct key_entry_key; enum sec_status val_nsec_prove_nodata_dsreply(struct module_env* env, struct val_env* ve, struct query_info* qinfo, struct reply_info* rep, struct key_entry_key* kkey, - time_t* proof_ttl, char** reason); + time_t* proof_ttl, char** reason, struct module_qstate* qstate); /** * nsec typemap check, takes an NSEC-type bitmap as argument, checks for type. diff --git a/usr.sbin/unbound/validator/val_nsec3.c b/usr.sbin/unbound/validator/val_nsec3.c index 4d978372aaa..773ed30cde5 100644 --- a/usr.sbin/unbound/validator/val_nsec3.c +++ b/usr.sbin/unbound/validator/val_nsec3.c @@ -1037,7 +1037,7 @@ nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt, "nsec3 is an insecure delegation"); return sec; } - log_nametypeclass(VERB_ALGO, "nsec3 namerror: proven ce=", ce.ce,0,0); + log_nametypeclass(VERB_ALGO, "nsec3 nameerror: proven ce=", ce.ce,0,0); /* At this point, we know that qname does not exist. Now we need * to prove that the wildcard does not exist. */ @@ -1285,7 +1285,7 @@ nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, static int list_is_secure(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key** list, size_t num, - struct key_entry_key* kkey, char** reason) + struct key_entry_key* kkey, char** reason, struct module_qstate* qstate) { struct packed_rrset_data* d; size_t i; @@ -1299,7 +1299,7 @@ list_is_secure(struct module_env* env, struct val_env* ve, if(d->security == sec_status_secure) continue; d->security = val_verify_rrset_entry(env, ve, list[i], kkey, - reason); + reason, LDNS_SECTION_AUTHORITY, qstate); if(d->security != sec_status_secure) { verbose(VERB_ALGO, "NSEC3 did not verify"); return 0; @@ -1312,7 +1312,8 @@ list_is_secure(struct module_env* env, struct val_env* ve, enum sec_status nsec3_prove_nods(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey, char** reason) + struct query_info* qinfo, struct key_entry_key* kkey, char** reason, + struct module_qstate* qstate) { rbtree_type ct; struct nsec3_filter flt; @@ -1325,7 +1326,7 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve, *reason = "no valid NSEC3s"; return sec_status_bogus; /* no valid NSEC3s, bogus */ } - if(!list_is_secure(env, ve, list, num, kkey, reason)) + if(!list_is_secure(env, ve, list, num, kkey, reason, qstate)) return sec_status_bogus; /* not all NSEC3 records secure */ rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */ filter_init(&flt, list, num, qinfo); /* init RR iterator */ diff --git a/usr.sbin/unbound/validator/val_nsec3.h b/usr.sbin/unbound/validator/val_nsec3.h index 27e9f9eac19..7fd37c16905 100644 --- a/usr.sbin/unbound/validator/val_nsec3.h +++ b/usr.sbin/unbound/validator/val_nsec3.h @@ -71,6 +71,7 @@ struct val_env; struct regional; struct module_env; +struct module_qstate; struct ub_packed_rrset_key; struct reply_info; struct query_info; @@ -185,6 +186,7 @@ nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, * @param qinfo: query that is verified for. * @param kkey: key entry that signed the NSEC3s. * @param reason: string for bogus result. + * @param qstate: qstate with region. * @return: * sec_status SECURE of the proposition is proven by the NSEC3 RRs, * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. @@ -194,7 +196,8 @@ nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, enum sec_status nsec3_prove_nods(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey, char** reason); + struct query_info* qinfo, struct key_entry_key* kkey, char** reason, + struct module_qstate* qstate); /** * Prove NXDOMAIN or NODATA. diff --git a/usr.sbin/unbound/validator/val_secalgo.c b/usr.sbin/unbound/validator/val_secalgo.c index e9ec5a5b587..7f5c5181fd2 100644 --- a/usr.sbin/unbound/validator/val_secalgo.c +++ b/usr.sbin/unbound/validator/val_secalgo.c @@ -322,7 +322,7 @@ static int setup_ecdsa_sig(unsigned char** sig, unsigned int* len) { /* convert from two BIGNUMs in the rdata buffer, to ASN notation. - * ASN preable: 30440220 <R 32bytefor256> 0220 <S 32bytefor256> + * ASN preamble: 30440220 <R 32bytefor256> 0220 <S 32bytefor256> * the '20' is the length of that field (=bnsize). i * the '44' is the total remaining length. * if negative, start with leading zero. diff --git a/usr.sbin/unbound/validator/val_sigcrypt.c b/usr.sbin/unbound/validator/val_sigcrypt.c index 25278a8f3ac..cfa3eadcf6a 100644 --- a/usr.sbin/unbound/validator/val_sigcrypt.c +++ b/usr.sbin/unbound/validator/val_sigcrypt.c @@ -485,7 +485,8 @@ int algo_needs_missing(struct algo_needs* n) enum sec_status dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, - uint8_t* sigalg, char** reason) + uint8_t* sigalg, char** reason, sldns_pkt_section section, + struct module_qstate* qstate) { enum sec_status sec; size_t i, num; @@ -512,7 +513,7 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, } for(i=0; i<num; i++) { sec = dnskeyset_verify_rrset_sig(env, ve, *env->now, rrset, - dnskey, i, &sortree, reason); + dnskey, i, &sortree, reason, section, qstate); /* see which algorithm has been fixed up */ if(sec == sec_status_secure) { if(!sigalg) @@ -553,7 +554,8 @@ void algo_needs_reason(struct module_env* env, int alg, char** reason, char* s) 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) + size_t dnskey_idx, char** reason, sldns_pkt_section section, + struct module_qstate* qstate) { enum sec_status sec; size_t i, num, numchecked = 0; @@ -577,7 +579,8 @@ dnskey_verify_rrset(struct module_env* env, struct val_env* ve, buf_canon = 0; sec = dnskey_verify_rrset_sig(env->scratch, env->scratch_buffer, ve, *env->now, rrset, - dnskey, dnskey_idx, i, &sortree, &buf_canon, reason); + dnskey, dnskey_idx, i, &sortree, &buf_canon, reason, + section, qstate); if(sec == sec_status_secure) return sec; numchecked ++; @@ -591,7 +594,8 @@ 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) + struct rbtree_type** sortree, char** reason, sldns_pkt_section section, + struct module_qstate* qstate) { /* find matching keys and check them */ enum sec_status sec = sec_status_bogus; @@ -616,7 +620,7 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve, /* 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); + sig_idx, sortree, &buf_canon, reason, section, qstate); if(sec == sec_status_secure) return sec; } @@ -906,7 +910,7 @@ canonical_sort(struct ub_packed_rrset_key* rrset, struct packed_rrset_data* d, } /** - * Inser canonical owner name into buffer. + * Insert canonical owner name into buffer. * @param buf: buffer to insert into at current position. * @param k: rrset with its owner name. * @param sig: signature with signer name and label count. @@ -1121,12 +1125,15 @@ int rrset_canonical_equal(struct regional* region, * signer name length. * @param sortree: if NULL is passed a new sorted rrset tree is built. * Otherwise it is reused. + * @param section: section of packet where this rrset comes from. + * @param qstate: qstate with region. * @return false on alloc error. */ static int rrset_canonical(struct regional* region, sldns_buffer* buf, struct ub_packed_rrset_key* k, uint8_t* sig, size_t siglen, - struct rbtree_type** sortree) + struct rbtree_type** sortree, sldns_pkt_section section, + struct module_qstate* qstate) { struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data; uint8_t* can_owner = NULL; @@ -1175,6 +1182,20 @@ rrset_canonical(struct regional* region, sldns_buffer* buf, canonicalize_rdata(buf, k, d->rr_len[walk->rr_idx]); } sldns_buffer_flip(buf); + + /* Replace RR owner with canonical owner for NSEC records in authority + * section, to prevent that a wildcard synthesized NSEC can be used in + * the non-existence proves. */ + if(ntohs(k->rk.type) == LDNS_RR_TYPE_NSEC && + section == LDNS_SECTION_AUTHORITY) { + k->rk.dname = regional_alloc_init(qstate->region, can_owner, + can_owner_len); + if(!k->rk.dname) + return 0; + k->rk.dname_len = can_owner_len; + } + + return 1; } @@ -1204,13 +1225,51 @@ sigdate_error(const char* str, int32_t expi, int32_t incep, int32_t now) (unsigned)incep, (unsigned)now); } +/** RFC 1918 comparison, uses unsigned integers, and tries to avoid + * compiler optimization (eg. by avoiding a-b<0 comparisons), + * this routine matches compare_serial(), for SOA serial number checks */ +static int +compare_1918(uint32_t a, uint32_t b) +{ + /* for 32 bit values */ + const uint32_t cutoff = ((uint32_t) 1 << (32 - 1)); + + if (a == b) { + return 0; + } else if ((a < b && b - a < cutoff) || (a > b && a - b > cutoff)) { + return -1; + } else { + return 1; + } +} + +/** if we know that b is larger than a, return the difference between them, + * that is the distance between them. in RFC1918 arith */ +static uint32_t +subtract_1918(uint32_t a, uint32_t b) +{ + /* for 32 bit values */ + const uint32_t cutoff = ((uint32_t) 1 << (32 - 1)); + + if(a == b) + return 0; + if(a < b && b - a < cutoff) { + return b-a; + } + if(a > b && a - b > cutoff) { + return ((uint32_t)0xffffffff) - (a-b-1); + } + /* wrong case, b smaller than a */ + return 0; +} + /** check rrsig dates */ static int check_dates(struct val_env* ve, uint32_t unow, uint8_t* expi_p, uint8_t* incep_p, char** reason) { /* read out the dates */ - int32_t expi, incep, now; + uint32_t expi, incep, now; memmove(&expi, expi_p, sizeof(expi)); memmove(&incep, incep_p, sizeof(incep)); expi = ntohl(expi); @@ -1224,21 +1283,21 @@ check_dates(struct val_env* ve, uint32_t unow, } now = ve->date_override; verbose(VERB_ALGO, "date override option %d", (int)now); - } else now = (int32_t)unow; + } else now = unow; /* check them */ - if(incep - expi > 0) { + if(compare_1918(incep, expi) > 0) { sigdate_error("verify: inception after expiration, " "signature bad", expi, incep, now); *reason = "signature inception after expiration"; return 0; } - if(incep - now > 0) { + if(compare_1918(incep, now) > 0) { /* within skew ? (calc here to avoid calculation normally) */ - int32_t skew = (expi-incep)/10; - if(skew < ve->skew_min) skew = ve->skew_min; - if(skew > ve->skew_max) skew = ve->skew_max; - if(incep - now > skew) { + uint32_t skew = subtract_1918(incep, expi)/10; + if(skew < (uint32_t)ve->skew_min) skew = ve->skew_min; + if(skew > (uint32_t)ve->skew_max) skew = ve->skew_max; + if(subtract_1918(now, incep) > skew) { sigdate_error("verify: signature bad, current time is" " before inception date", expi, incep, now); *reason = "signature before inception date"; @@ -1247,11 +1306,11 @@ check_dates(struct val_env* ve, uint32_t unow, sigdate_error("verify warning suspicious signature inception " " or bad local clock", expi, incep, now); } - if(now - expi > 0) { - int32_t skew = (expi-incep)/10; - if(skew < ve->skew_min) skew = ve->skew_min; - if(skew > ve->skew_max) skew = ve->skew_max; - if(now - expi > skew) { + if(compare_1918(now, expi) > 0) { + uint32_t skew = subtract_1918(incep, expi)/10; + if(skew < (uint32_t)ve->skew_min) skew = ve->skew_min; + if(skew > (uint32_t)ve->skew_max) skew = ve->skew_max; + if(subtract_1918(expi, now) > skew) { sigdate_error("verify: signature expired", expi, incep, now); *reason = "signature expired"; @@ -1318,7 +1377,8 @@ dnskey_verify_rrset_sig(struct regional* region, sldns_buffer* buf, struct val_env* ve, time_t now, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, size_t dnskey_idx, size_t sig_idx, - struct rbtree_type** sortree, int* buf_canon, char** reason) + struct rbtree_type** sortree, int* buf_canon, char** reason, + sldns_pkt_section section, struct module_qstate* qstate) { enum sec_status sec; uint8_t* sig; /* RRSIG rdata */ @@ -1417,7 +1477,7 @@ dnskey_verify_rrset_sig(struct regional* region, sldns_buffer* buf, /* create rrset canonical format in buffer, ready for * signature */ if(!rrset_canonical(region, buf, rrset, sig+2, - 18 + signer_len, sortree)) { + 18 + signer_len, sortree, section, qstate)) { log_err("verify: failed due to alloc error"); return sec_status_unchecked; } diff --git a/usr.sbin/unbound/validator/val_sigcrypt.h b/usr.sbin/unbound/validator/val_sigcrypt.h index 5a975acff4d..755a1d6e126 100644 --- a/usr.sbin/unbound/validator/val_sigcrypt.h +++ b/usr.sbin/unbound/validator/val_sigcrypt.h @@ -44,8 +44,10 @@ #ifndef VALIDATOR_VAL_SIGCRYPT_H #define VALIDATOR_VAL_SIGCRYPT_H #include "util/data/packed_rrset.h" +#include "sldns/pkthdr.h" struct val_env; struct module_env; +struct module_qstate; struct ub_packed_rrset_key; struct rbtree_type; struct regional; @@ -237,13 +239,16 @@ uint16_t dnskey_get_flags(struct ub_packed_rrset_key* k, size_t idx); * @param sigalg: if nonNULL provide downgrade protection otherwise one * algorithm is enough. * @param reason: if bogus, a string returned, fixed or alloced in scratch. + * @param section: section of packet where this rrset comes from. + * @param qstate: qstate with region. * @return SECURE if one key in the set verifies one rrsig. * UNCHECKED on allocation errors, unsupported algorithms, malformed data, * and BOGUS on verification failures (no keys match any signatures). */ enum sec_status dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, - struct ub_packed_rrset_key* dnskey, uint8_t* sigalg, char** reason); + struct ub_packed_rrset_key* dnskey, uint8_t* sigalg, char** reason, + sldns_pkt_section section, struct module_qstate* qstate); /** * verify rrset against one specific dnskey (from rrset) @@ -253,12 +258,15 @@ enum sec_status dnskeyset_verify_rrset(struct module_env* env, * @param dnskey: DNSKEY rrset, keyset. * @param dnskey_idx: which key from the rrset to try. * @param reason: if bogus, a string returned, fixed or alloced in scratch. + * @param section: section of packet where this rrset comes from. + * @param qstate: qstate with region. * @return secure if *this* key signs any of the signatures on rrset. * unchecked on error or and bogus on bad signature. */ 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); + struct ub_packed_rrset_key* dnskey, size_t dnskey_idx, char** reason, + sldns_pkt_section section, struct module_qstate* qstate); /** * verify rrset, with dnskey rrset, for a specific rrsig in rrset @@ -271,13 +279,16 @@ enum sec_status dnskey_verify_rrset(struct module_env* env, * @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 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, * or unchecked on error. */ 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); + struct rbtree_type** sortree, char** reason, sldns_pkt_section section, + struct module_qstate* qstate); /** * verify rrset, with specific dnskey(from set), for a specific rrsig @@ -295,6 +306,8 @@ enum sec_status dnskeyset_verify_rrset_sig(struct module_env* env, * pass false at start. pass old value only for same rrset and same * signature (but perhaps different key) for reuse. * @param reason: if bogus, a string returned, fixed or alloced in scratch. + * @param section: section of packet where this rrset comes from. + * @param qstate: qstate with region. * @return secure if this key signs this signature. unchecked on error or * bogus if it did not validate. */ @@ -302,7 +315,8 @@ enum sec_status dnskey_verify_rrset_sig(struct regional* region, struct sldns_buffer* buf, struct val_env* ve, time_t now, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, size_t dnskey_idx, size_t sig_idx, - struct rbtree_type** sortree, int* buf_canon, char** reason); + struct rbtree_type** sortree, int* buf_canon, char** reason, + sldns_pkt_section section, struct module_qstate* qstate); /** * canonical compare for two tree entries diff --git a/usr.sbin/unbound/validator/val_utils.c b/usr.sbin/unbound/validator/val_utils.c index 973473f9dc8..0eabb7f33ba 100644 --- a/usr.sbin/unbound/validator/val_utils.c +++ b/usr.sbin/unbound/validator/val_utils.c @@ -335,7 +335,8 @@ rrset_get_ttl(struct ub_packed_rrset_key* rrset) enum sec_status val_verify_rrset(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* keys, - uint8_t* sigalg, char** reason) + uint8_t* sigalg, char** reason, sldns_pkt_section section, + struct module_qstate* qstate) { enum sec_status sec; struct packed_rrset_data* d = (struct packed_rrset_data*)rrset-> @@ -357,7 +358,8 @@ val_verify_rrset(struct module_env* env, struct val_env* ve, } log_nametypeclass(VERB_ALGO, "verify rrset", rrset->rk.dname, ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class)); - sec = dnskeyset_verify_rrset(env, ve, rrset, keys, sigalg, reason); + sec = dnskeyset_verify_rrset(env, ve, rrset, keys, sigalg, reason, + section, qstate); verbose(VERB_ALGO, "verify result: %s", sec_status_to_string(sec)); regional_free_all(env->scratch); @@ -390,7 +392,7 @@ val_verify_rrset(struct module_env* env, struct val_env* ve, enum sec_status val_verify_rrset_entry(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, struct key_entry_key* kkey, - char** reason) + char** reason, sldns_pkt_section section, struct module_qstate* qstate) { /* temporary dnskey rrset-key */ struct ub_packed_rrset_key dnskey; @@ -403,7 +405,8 @@ val_verify_rrset_entry(struct module_env* env, struct val_env* ve, dnskey.rk.dname_len = kkey->namelen; dnskey.entry.key = &dnskey; dnskey.entry.data = kd->rrset_data; - sec = val_verify_rrset(env, ve, rrset, &dnskey, kd->algo, reason); + sec = val_verify_rrset(env, ve, rrset, &dnskey, kd->algo, reason, + section, qstate); return sec; } @@ -411,7 +414,8 @@ val_verify_rrset_entry(struct module_env* env, struct val_env* ve, static enum sec_status verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, - struct ub_packed_rrset_key* ds_rrset, size_t ds_idx, char** reason) + struct ub_packed_rrset_key* ds_rrset, size_t ds_idx, char** reason, + struct module_qstate* qstate) { enum sec_status sec = sec_status_bogus; size_t i, num, numchecked = 0, numhashok = 0; @@ -442,7 +446,7 @@ verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve, /* Otherwise, we have a match! Make sure that the DNSKEY * verifies *with this key* */ sec = dnskey_verify_rrset(env, ve, dnskey_rrset, - dnskey_rrset, i, reason); + dnskey_rrset, i, reason, LDNS_SECTION_ANSWER, qstate); if(sec == sec_status_secure) { return sec; } @@ -478,7 +482,8 @@ int val_favorite_ds_algo(struct ub_packed_rrset_key* ds_rrset) enum sec_status val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, - struct ub_packed_rrset_key* ds_rrset, uint8_t* sigalg, char** reason) + struct ub_packed_rrset_key* ds_rrset, uint8_t* sigalg, char** reason, + struct module_qstate* qstate) { /* as long as this is false, we can consider this DS rrset to be * equivalent to no DS rrset. */ @@ -520,7 +525,7 @@ val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve, has_useful_ds = 1; sec = verify_dnskeys_with_ds_rr(env, ve, dnskey_rrset, - ds_rrset, i, reason); + ds_rrset, i, reason, qstate); if(sec == sec_status_secure) { if(!sigalg || algo_needs_set_secure(&needs, (uint8_t)ds_get_key_algo(ds_rrset, i))) { @@ -553,11 +558,12 @@ val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve, struct key_entry_key* val_verify_new_DNSKEYs(struct regional* region, struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, - struct ub_packed_rrset_key* ds_rrset, int downprot, char** reason) + struct ub_packed_rrset_key* ds_rrset, int downprot, char** reason, + struct module_qstate* qstate) { uint8_t sigalg[ALGO_NEEDS_MAX+1]; enum sec_status sec = val_verify_DNSKEY_with_DS(env, ve, - dnskey_rrset, ds_rrset, downprot?sigalg:NULL, reason); + dnskey_rrset, ds_rrset, downprot?sigalg:NULL, reason, qstate); if(sec == sec_status_secure) { return key_entry_create_rrset(region, @@ -579,7 +585,8 @@ enum sec_status val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, struct ub_packed_rrset_key* ta_ds, - struct ub_packed_rrset_key* ta_dnskey, uint8_t* sigalg, char** reason) + struct ub_packed_rrset_key* ta_dnskey, uint8_t* sigalg, char** reason, + struct module_qstate* qstate) { /* as long as this is false, we can consider this anchor to be * equivalent to no anchor. */ @@ -630,7 +637,7 @@ val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve, has_useful_ta = 1; sec = verify_dnskeys_with_ds_rr(env, ve, dnskey_rrset, - ta_ds, i, reason); + ta_ds, i, reason, qstate); if(sec == sec_status_secure) { if(!sigalg || algo_needs_set_secure(&needs, (uint8_t)ds_get_key_algo(ta_ds, i))) { @@ -656,7 +663,7 @@ val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve, has_useful_ta = 1; sec = dnskey_verify_rrset(env, ve, dnskey_rrset, - ta_dnskey, i, reason); + ta_dnskey, i, reason, LDNS_SECTION_ANSWER, qstate); if(sec == sec_status_secure) { if(!sigalg || algo_needs_set_secure(&needs, (uint8_t)dnskey_get_algo(ta_dnskey, i))) { @@ -690,12 +697,12 @@ val_verify_new_DNSKEYs_with_ta(struct regional* region, struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, struct ub_packed_rrset_key* ta_ds_rrset, struct ub_packed_rrset_key* ta_dnskey_rrset, int downprot, - char** reason) + char** reason, struct module_qstate* qstate) { uint8_t sigalg[ALGO_NEEDS_MAX+1]; enum sec_status sec = val_verify_DNSKEY_with_TA(env, ve, dnskey_rrset, ta_ds_rrset, ta_dnskey_rrset, - downprot?sigalg:NULL, reason); + downprot?sigalg:NULL, reason, qstate); if(sec == sec_status_secure) { return key_entry_create_rrset(region, diff --git a/usr.sbin/unbound/validator/val_utils.h b/usr.sbin/unbound/validator/val_utils.h index 649adc2d655..b582472f8fa 100644 --- a/usr.sbin/unbound/validator/val_utils.h +++ b/usr.sbin/unbound/validator/val_utils.h @@ -42,10 +42,12 @@ #ifndef VALIDATOR_VAL_UTILS_H #define VALIDATOR_VAL_UTILS_H #include "util/data/packed_rrset.h" +#include "sldns/pkthdr.h" struct query_info; struct reply_info; struct val_env; struct module_env; +struct module_qstate; struct ub_packed_rrset_key; struct key_entry_key; struct regional; @@ -70,7 +72,7 @@ enum val_classification { /** A NXDOMAIN response. */ VAL_CLASS_NAMEERROR, /** A CNAME/DNAME chain, and the offset is at the end of it, - * but there is no answer here, it can be NAMERROR or NODATA. */ + * but there is no answer here, it can be NAMEERROR or NODATA. */ VAL_CLASS_CNAMENOANSWER, /** A referral, from cache with a nonRD query. */ VAL_CLASS_REFERRAL, @@ -120,11 +122,14 @@ void val_find_signer(enum val_classification subtype, * @param sigalg: if nonNULL provide downgrade protection otherwise one * algorithm is enough. Algo list is constructed in here. * @param reason: reason of failure. Fixed string or alloced in scratch. + * @param section: section of packet where this rrset comes from. + * @param qstate: qstate with region. * @return security status of verification. */ enum sec_status val_verify_rrset(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* keys, - uint8_t* sigalg, char** reason); + uint8_t* sigalg, char** reason, sldns_pkt_section section, + struct module_qstate* qstate); /** * Verify RRset with keys from a keyset. @@ -133,11 +138,14 @@ enum sec_status val_verify_rrset(struct module_env* env, struct val_env* ve, * @param rrset: what to verify * @param kkey: key_entry to verify with. * @param reason: reason of failure. Fixed string or alloced in scratch. + * @param section: section of packet where this rrset comes from. + * @param qstate: qstate with region. * @return security status of verification. */ enum sec_status val_verify_rrset_entry(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, - struct key_entry_key* kkey, char** reason); + struct key_entry_key* kkey, char** reason, sldns_pkt_section section, + struct module_qstate* qstate); /** * Verify DNSKEYs with DS rrset. Like val_verify_new_DNSKEYs but @@ -150,13 +158,15 @@ enum sec_status val_verify_rrset_entry(struct module_env* env, * algorithm is enough. The list of signalled algorithms is returned, * must have enough space for ALGO_NEEDS_MAX+1. * @param reason: reason of failure. Fixed string or alloced in scratch. + * @param qstate: qstate with region. * @return: sec_status_secure if a DS matches. * sec_status_insecure if end of trust (i.e., unknown algorithms). * sec_status_bogus if it fails. */ enum sec_status val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, - struct ub_packed_rrset_key* ds_rrset, uint8_t* sigalg, char** reason); + struct ub_packed_rrset_key* ds_rrset, uint8_t* sigalg, char** reason, + struct module_qstate* qstate); /** * Verify DNSKEYs with DS and DNSKEY rrset. Like val_verify_DNSKEY_with_DS @@ -170,6 +180,7 @@ enum sec_status val_verify_DNSKEY_with_DS(struct module_env* env, * algorithm is enough. The list of signalled algorithms is returned, * must have enough space for ALGO_NEEDS_MAX+1. * @param reason: reason of failure. Fixed string or alloced in scratch. + * @param qstate: qstate with region. * @return: sec_status_secure if a DS matches. * sec_status_insecure if end of trust (i.e., unknown algorithms). * sec_status_bogus if it fails. @@ -177,7 +188,8 @@ enum sec_status val_verify_DNSKEY_with_DS(struct module_env* env, enum sec_status val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, struct ub_packed_rrset_key* ta_ds, - struct ub_packed_rrset_key* ta_dnskey, uint8_t* sigalg, char** reason); + struct ub_packed_rrset_key* ta_dnskey, uint8_t* sigalg, char** reason, + struct module_qstate* qstate); /** * Verify new DNSKEYs with DS rrset. The DS contains hash values that should @@ -192,6 +204,7 @@ enum sec_status val_verify_DNSKEY_with_TA(struct module_env* env, * @param downprot: if true provide downgrade protection otherwise one * algorithm is enough. * @param reason: reason of failure. Fixed string or alloced in scratch. + * @param qstate: qstate with region. * @return a KeyEntry. This will either contain the now trusted * dnskey_rrset, a "null" key entry indicating that this DS * rrset/DNSKEY pair indicate an secure end to the island of trust @@ -205,7 +218,8 @@ enum sec_status val_verify_DNSKEY_with_TA(struct module_env* env, struct key_entry_key* val_verify_new_DNSKEYs(struct regional* region, struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, - struct ub_packed_rrset_key* ds_rrset, int downprot, char** reason); + struct ub_packed_rrset_key* ds_rrset, int downprot, char** reason, + struct module_qstate* qstate); /** @@ -220,6 +234,7 @@ struct key_entry_key* val_verify_new_DNSKEYs(struct regional* region, * @param downprot: if true provide downgrade protection otherwise one * algorithm is enough. * @param reason: reason of failure. Fixed string or alloced in scratch. + * @param qstate: qstate with region. * @return a KeyEntry. This will either contain the now trusted * dnskey_rrset, a "null" key entry indicating that this DS * rrset/DNSKEY pair indicate an secure end to the island of trust @@ -235,7 +250,7 @@ struct key_entry_key* val_verify_new_DNSKEYs_with_ta(struct regional* region, struct ub_packed_rrset_key* dnskey_rrset, struct ub_packed_rrset_key* ta_ds_rrset, struct ub_packed_rrset_key* ta_dnskey_rrset, - int downprot, char** reason); + int downprot, char** reason, struct module_qstate* qstate); /** * Determine if DS rrset is usable for validator or not. @@ -252,7 +267,7 @@ int val_dsset_isusable(struct ub_packed_rrset_key* ds_rrset); * the result of a wildcard expansion. If so, return the name of the * generating wildcard. * - * @param rrset The rrset to chedck. + * @param rrset The rrset to check. * @param wc: the wildcard name, if the rrset was synthesized from a wildcard. * unchanged if not. The wildcard name, without "*." in front, is * returned. This is a pointer into the rrset owner name. diff --git a/usr.sbin/unbound/validator/validator.c b/usr.sbin/unbound/validator/validator.c index 5f4a1eb4ebe..456bffd005f 100644 --- a/usr.sbin/unbound/validator/validator.c +++ b/usr.sbin/unbound/validator/validator.c @@ -572,7 +572,8 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, } /* Verify the answer rrset */ - sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason); + sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason, + LDNS_SECTION_ANSWER, qstate); /* If the (answer) rrset failed to validate, then this * message is BAD. */ if(sec != sec_status_secure) { @@ -601,7 +602,8 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+ chase_reply->ns_numrrsets; i++) { s = chase_reply->rrsets[i]; - sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason); + sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason, + LDNS_SECTION_AUTHORITY, qstate); /* If anything in the authority section fails to be secure, * we have a bad message. */ if(sec != sec_status_secure) { @@ -629,7 +631,7 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, val_find_rrset_signer(s, &sname, &slen); if(sname && query_dname_compare(sname, key_entry->name)==0) (void)val_verify_rrset_entry(env, ve, s, key_entry, - &reason); + &reason, LDNS_SECTION_ADDITIONAL, qstate); /* the additional section can fail to be secure, * it is optional, check signature in case we need * to clean the additional section later. */ @@ -1289,7 +1291,7 @@ validate_cname_noanswer_response(struct module_env* env, struct val_env* ve, uint8_t* ce = NULL; /* for wildcard nodata responses. This is the proven closest encloser. */ uint8_t* wc = NULL; /* for wildcard nodata responses. wildcard nsec */ - int nxdomain_valid_nsec = 0; /* if true, namerror has been proven */ + int nxdomain_valid_nsec = 0; /* if true, nameerror has been proven */ int nxdomain_valid_wnsec = 0; int nsec3s_seen = 0; /* nsec3s seen */ struct ub_packed_rrset_key* s; @@ -2484,7 +2486,7 @@ primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset, /* attempt to verify with trust anchor DS and DNSKEY */ kkey = val_verify_new_DNSKEYs_with_ta(qstate->region, qstate->env, ve, dnskey_rrset, ta->ds_rrset, ta->dnskey_rrset, downprot, - &reason); + &reason, qstate); if(!kkey) { log_err("out of memory: verifying prime TA"); return NULL; @@ -2574,7 +2576,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, /* Verify only returns BOGUS or SECURE. If the rrset is * bogus, then we are done. */ sec = val_verify_rrset_entry(qstate->env, ve, ds, - vq->key_entry, &reason); + vq->key_entry, &reason, LDNS_SECTION_ANSWER, qstate); if(sec != sec_status_secure) { verbose(VERB_DETAIL, "DS rrset in DS response did " "not verify"); @@ -2621,7 +2623,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, /* Try to prove absence of the DS with NSEC */ sec = val_nsec_prove_nodata_dsreply( qstate->env, ve, qinfo, msg->rep, vq->key_entry, - &proof_ttl, &reason); + &proof_ttl, &reason, qstate); switch(sec) { case sec_status_secure: verbose(VERB_DETAIL, "NSEC RRset for the " @@ -2649,7 +2651,8 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, sec = nsec3_prove_nods(qstate->env, ve, msg->rep->rrsets + msg->rep->an_numrrsets, - msg->rep->ns_numrrsets, qinfo, vq->key_entry, &reason); + msg->rep->ns_numrrsets, qinfo, vq->key_entry, &reason, + qstate); switch(sec) { case sec_status_insecure: /* case insecure also continues to unsigned @@ -2710,7 +2713,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, goto return_bogus; } sec = val_verify_rrset_entry(qstate->env, ve, cname, - vq->key_entry, &reason); + vq->key_entry, &reason, LDNS_SECTION_ANSWER, qstate); if(sec == sec_status_secure) { verbose(VERB_ALGO, "CNAME validated, " "proof that DS does not exist"); @@ -2876,7 +2879,7 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq, } downprot = qstate->env->cfg->harden_algo_downgrade; vq->key_entry = val_verify_new_DNSKEYs(qstate->region, qstate->env, - ve, dnskey, vq->ds_rrset, downprot, &reason); + ve, dnskey, vq->ds_rrset, downprot, &reason, qstate); if(!vq->key_entry) { log_err("out of memory in verify new DNSKEYs"); @@ -2952,7 +2955,8 @@ process_prime_response(struct module_qstate* qstate, struct val_qstate* vq, } if(ta->autr) { - if(!autr_process_prime(qstate->env, ve, ta, dnskey_rrset)) { + if(!autr_process_prime(qstate->env, ve, ta, dnskey_rrset, + qstate)) { /* trust anchor revoked, restart with less anchors */ vq->state = VAL_INIT_STATE; vq->trust_anchor_name = NULL; |