STRIP=@STRIP@
CC=@CC@
CPPFLAGS=-I. @CPPFLAGS@
-PYTHON_CPPFLAGS=-I. @PYTHON_CPPFLAGS@
+PYTHON_CPPFLAGS=-I. -I$(srcdir) @PYTHON_CPPFLAGS@
CFLAGS=-DSRCDIR=$(srcdir) @CFLAGS@
LDFLAGS=@LDFLAGS@
LIBS=@LIBS@
test: unittest$(EXEEXT) testbound$(EXEEXT)
./unittest$(EXEEXT)
./testbound$(EXEEXT) -s
- for x in $(srcdir)/testdata/*.rpl; do printf "%s" "$$x "; if ./testbound$(EXEEXT) -p $$x >/dev/null 2>&1; then echo OK; else echo failed; exit 1; fi done
+ for x in $(srcdir)/testdata/*.rpl; do \
+ printf "%s" "$$x "; \
+ if ./testbound$(EXEEXT) -p $$x >/dev/null 2>&1; then \
+ echo OK; \
+ else \
+ echo failed; \
+ ./testbound$(EXEEXT) -p $$x -o -vvvvv; \
+ printf "%s" "$$x "; \
+ echo failed; \
+ exit 1; \
+ fi; \
+ done
@echo test OK
longtest: tests
pyunbound-install:
$(INSTALL) -m 755 -d $(DESTDIR)$(PYTHON_SITE_PKG)
- $(INSTALL) -c -m 644 $(srcdir)/libunbound/python/unbound.py $(DESTDIR)$(PYTHON_SITE_PKG)/unbound.py
+ $(INSTALL) -c -m 644 libunbound/python/unbound.py $(DESTDIR)$(PYTHON_SITE_PKG)/unbound.py
$(LIBTOOL) --mode=install cp _unbound.la $(DESTDIR)$(PYTHON_SITE_PKG)
$(LIBTOOL) --mode=finish $(DESTDIR)$(PYTHON_SITE_PKG)
echo ".so man3/libunbound.3" > $(DESTDIR)$(mandir)/man3/$$mpage.3 ; \
done
$(LIBTOOL) --mode=install cp unbound.h $(DESTDIR)$(includedir)/unbound.h
+ $(INSTALL) -m 755 -d $(DESTDIR)$(libdir)/pkgconfig
+ $(INSTALL) -m 644 contrib/libunbound.pc $(DESTDIR)$(libdir)/pkgconfig
$(LIBTOOL) --mode=install cp libunbound.la $(DESTDIR)$(libdir)
$(LIBTOOL) --mode=finish $(DESTDIR)$(libdir)
$(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)
$(srcdir)/util/regional.h $(srcdir)/util/net_help.h $(srcdir)/util/data/dname.h $(srcdir)/iterator/iterator.h \
$(srcdir)/services/outbound_list.h $(srcdir)/iterator/iter_delegpt.h $(srcdir)/iterator/iter_utils.h \
$(srcdir)/iterator/iter_resptype.h $(srcdir)/iterator/iter_fwd.h $(srcdir)/iterator/iter_hints.h \
- $(srcdir)/sldns/wire2str.h $(srcdir)/sldns/str2wire.h
+ $(srcdir)/sldns/wire2str.h $(srcdir)/sldns/str2wire.h $(srcdir)/util/config_file.h $(srcdir)/services/outside_network.h
daemon.lo daemon.o: $(srcdir)/daemon/daemon.c config.h $(srcdir)/daemon/daemon.h $(srcdir)/util/locks.h \
$(srcdir)/util/log.h $(srcdir)/util/alloc.h $(srcdir)/services/modstack.h \
$(srcdir)/daemon/worker.h $(srcdir)/libunbound/worker.h \
print(sys.version.split()[[0]])"`
fi
- #
- # Check if you have distutils, else fail
- #
- AC_MSG_CHECKING([for the distutils Python package])
- if ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`; then
+ # Check if you have sysconfig
+ AC_MSG_CHECKING([for the sysconfig Python module])
+ if ac_sysconfig_result=`$PYTHON -c "import sysconfig" 2>&1`; then
AC_MSG_RESULT([yes])
- else
+ sysconfig_module="sysconfig"
+ # if yes, use sysconfig, because distutils is deprecated.
+ else
AC_MSG_RESULT([no])
- AC_MSG_ERROR([cannot import Python module "distutils".
-Please check your Python installation. The error was:
-$ac_distutils_result])
- PYTHON_VERSION=""
- fi
+ # if no, try to use distutils
+
+ #
+ # Check if you have distutils, else fail
+ #
+ AC_MSG_CHECKING([for the distutils Python package])
+ if ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ AC_MSG_ERROR([cannot import Python module "distutils".
+ Please check your Python installation. The error was:
+ $ac_distutils_result])
+ PYTHON_VERSION=""
+ fi
+
+ sysconfig_module="distutils.sysconfig"
+ fi
#
# Check for Python include path
#
AC_MSG_CHECKING([for Python include path])
if test -z "$PYTHON_CPPFLAGS"; then
- python_path=`$PYTHON -c "import distutils.sysconfig; \
- print(distutils.sysconfig.get_python_inc());"`
+ if test "$sysconfig_module" = "sysconfig"; then
+ python_path=`$PYTHON -c 'import sysconfig; \
+ print(sysconfig.get_path("include"));'`
+ else
+ python_path=`$PYTHON -c "import distutils.sysconfig; \
+ print(distutils.sysconfig.get_python_inc());"`
+ fi
if test -n "${python_path}"; then
python_path="-I$python_path"
fi
#
AC_MSG_CHECKING([for Python library path])
if test -z "$PYTHON_LDFLAGS"; then
- PYTHON_LDFLAGS=`$PYTHON -c "from distutils.sysconfig import *; \
+ PYTHON_LDFLAGS=`$PYTHON -c "from $sysconfig_module import *; \
print('-L'+get_config_var('LIBDIR')+' -L'+get_config_var('LIBDEST')+' '+get_config_var('BLDLIBRARY'));"`
fi
AC_MSG_RESULT([$PYTHON_LDFLAGS])
AC_SUBST([PYTHON_LDFLAGS])
if test -z "$PYTHON_LIBDIR"; then
- PYTHON_LIBDIR=`$PYTHON -c "from distutils.sysconfig import *; \
+ PYTHON_LIBDIR=`$PYTHON -c "from $sysconfig_module import *; \
print(get_config_var('LIBDIR'));"`
fi
#
AC_MSG_CHECKING([for Python site-packages path])
if test -z "$PYTHON_SITE_PKG"; then
- PYTHON_SITE_PKG=`$PYTHON -c "import distutils.sysconfig; \
- print(distutils.sysconfig.get_python_lib(1,0));"`
+ if test "$sysconfig_module" = "sysconfig"; then
+ PYTHON_SITE_PKG=`$PYTHON -c 'import sysconfig; \
+ print(sysconfig.get_path("platlib"));'`
+ else
+ PYTHON_SITE_PKG=`$PYTHON -c "import distutils.sysconfig; \
+ print(distutils.sysconfig.get_python_lib(1,0));"`
+ fi
fi
AC_MSG_RESULT([$PYTHON_SITE_PKG])
AC_SUBST([PYTHON_SITE_PKG])
# shellcheck disable=SC2006,SC2268 # see below for rationale
-timestamp='2022-01-09'
+timestamp='2022-05-25'
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
;;
x86_64:Linux:*:*)
set_cc_for_build
+ CPU=$UNAME_MACHINE
LIBCABI=$LIBC
if test "$CC_FOR_BUILD" != no_compiler_found; then
- if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \
- (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
- grep IS_X32 >/dev/null
- then
- LIBCABI=${LIBC}x32
- fi
+ ABI=64
+ sed 's/^ //' << EOF > "$dummy.c"
+ #ifdef __i386__
+ ABI=x86
+ #else
+ #ifdef __ILP32__
+ ABI=x32
+ #endif
+ #endif
+EOF
+ cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'`
+ eval "$cc_set_abi"
+ case $ABI in
+ x86) CPU=i686 ;;
+ x32) LIBCABI=${LIBC}x32 ;;
+ esac
fi
- GUESS=$UNAME_MACHINE-pc-linux-$LIBCABI
+ GUESS=$CPU-pc-linux-$LIBCABI
;;
xtensa*:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
BePC:Haiku:*:*) # Haiku running on Intel PC compatible.
GUESS=i586-pc-haiku
;;
- x86_64:Haiku:*:*)
- GUESS=x86_64-unknown-haiku
+ ppc:Haiku:*:*) # Haiku running on Apple PowerPC
+ GUESS=powerpc-apple-haiku
+ ;;
+ *:Haiku:*:*) # Haiku modern gcc (not bound by BeOS compat)
+ GUESS=$UNAME_MACHINE-unknown-haiku
;;
SX-4:SUPER-UX:*:*)
GUESS=sx4-nec-superux$UNAME_RELEASE
/* Define to 1 if you need to in order for `stat' and other things to work. */
#undef _POSIX_SOURCE
+/* defined to use gcc ansi snprintf and sscanf that understands %lld when
+ compiled for windows. */
+#undef __USE_MINGW_ANSI_STDIO
+
/* Define to empty if `const' does not conform to ANSI C. */
#undef const
#include <ws2tcpip.h>
#endif
-#ifndef USE_WINSOCK
+#if !defined(USE_WINSOCK) || !defined(HAVE_SNPRINTF) || defined(SNPRINTF_RET_BROKEN) || defined(__USE_MINGW_ANSI_STDIO)
#define ARG_LL "%ll"
#else
#define ARG_LL "%I64"
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.71 for unbound 1.15.0.
+# Generated by GNU Autoconf 2.71 for unbound 1.16.0.
#
# Report bugs to <unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues>.
#
# Identity of this package.
PACKAGE_NAME='unbound'
PACKAGE_TARNAME='unbound'
-PACKAGE_VERSION='1.15.0'
-PACKAGE_STRING='unbound 1.15.0'
+PACKAGE_VERSION='1.16.0'
+PACKAGE_STRING='unbound 1.16.0'
PACKAGE_BUGREPORT='unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues'
PACKAGE_URL=''
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures unbound 1.15.0 to adapt to many kinds of systems.
+\`configure' configures unbound 1.16.0 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of unbound 1.15.0:";;
+ short | recursive ) echo "Configuration of unbound 1.16.0:";;
esac
cat <<\_ACEOF
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-unbound configure 1.15.0
+unbound configure 1.16.0
generated by GNU Autoconf 2.71
Copyright (C) 2021 Free Software Foundation, Inc.
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by unbound $as_me 1.15.0, which was
+It was created by unbound $as_me 1.16.0, which was
generated by GNU Autoconf 2.71. Invocation command line was
$ $0$ac_configure_args_raw
UNBOUND_VERSION_MAJOR=1
-UNBOUND_VERSION_MINOR=15
+UNBOUND_VERSION_MINOR=16
UNBOUND_VERSION_MICRO=0
LIBUNBOUND_CURRENT=9
-LIBUNBOUND_REVISION=15
+LIBUNBOUND_REVISION=16
LIBUNBOUND_AGE=1
# 1.0.0 had 0:12:0
# 1.0.1 had 0:13:0
# 1.13.2 had 9:13:1
# 1.14.0 had 9:14:1
# 1.15.0 had 9:15:1
+# 1.16.0 had 9:16:1
# Current -- the number of the binary API that we're implementing
# Revision -- which iteration of the implementation of the binary
print(sys.version.split()[0])"`
fi
- #
- # Check if you have distutils, else fail
- #
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5
-printf %s "checking for the distutils Python package... " >&6; }
- if ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`; then
+ # Check if you have sysconfig
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the sysconfig Python module" >&5
+printf %s "checking for the sysconfig Python module... " >&6; }
+ if ac_sysconfig_result=`$PYTHON -c "import sysconfig" 2>&1`; then
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
- else
+ sysconfig_module="sysconfig"
+ # if yes, use sysconfig, because distutils is deprecated.
+ else
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
- as_fn_error $? "cannot import Python module \"distutils\".
-Please check your Python installation. The error was:
-$ac_distutils_result" "$LINENO" 5
- PYTHON_VERSION=""
- fi
+ # if no, try to use distutils
+
+ #
+ # Check if you have distutils, else fail
+ #
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5
+printf %s "checking for the distutils Python package... " >&6; }
+ if ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+ else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+ as_fn_error $? "cannot import Python module \"distutils\".
+ Please check your Python installation. The error was:
+ $ac_distutils_result" "$LINENO" 5
+ PYTHON_VERSION=""
+ fi
+
+ sysconfig_module="distutils.sysconfig"
+ fi
#
# Check for Python include path
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Python include path" >&5
printf %s "checking for Python include path... " >&6; }
if test -z "$PYTHON_CPPFLAGS"; then
- python_path=`$PYTHON -c "import distutils.sysconfig; \
- print(distutils.sysconfig.get_python_inc());"`
+ if test "$sysconfig_module" = "sysconfig"; then
+ python_path=`$PYTHON -c 'import sysconfig; \
+ print(sysconfig.get_path("include"));'`
+ else
+ python_path=`$PYTHON -c "import distutils.sysconfig; \
+ print(distutils.sysconfig.get_python_inc());"`
+ fi
if test -n "${python_path}"; then
python_path="-I$python_path"
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Python library path" >&5
printf %s "checking for Python library path... " >&6; }
if test -z "$PYTHON_LDFLAGS"; then
- PYTHON_LDFLAGS=`$PYTHON -c "from distutils.sysconfig import *; \
+ PYTHON_LDFLAGS=`$PYTHON -c "from $sysconfig_module import *; \
print('-L'+get_config_var('LIBDIR')+' -L'+get_config_var('LIBDEST')+' '+get_config_var('BLDLIBRARY'));"`
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PYTHON_LDFLAGS" >&5
if test -z "$PYTHON_LIBDIR"; then
- PYTHON_LIBDIR=`$PYTHON -c "from distutils.sysconfig import *; \
+ PYTHON_LIBDIR=`$PYTHON -c "from $sysconfig_module import *; \
print(get_config_var('LIBDIR'));"`
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Python site-packages path" >&5
printf %s "checking for Python site-packages path... " >&6; }
if test -z "$PYTHON_SITE_PKG"; then
- PYTHON_SITE_PKG=`$PYTHON -c "import distutils.sysconfig; \
- print(distutils.sysconfig.get_python_lib(1,0));"`
+ if test "$sysconfig_module" = "sysconfig"; then
+ PYTHON_SITE_PKG=`$PYTHON -c 'import sysconfig; \
+ print(sysconfig.get_path("platlib"));'`
+ else
+ PYTHON_SITE_PKG=`$PYTHON -c "import distutils.sysconfig; \
+ print(distutils.sysconfig.get_python_lib(1,0));"`
+ fi
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PYTHON_SITE_PKG" >&5
printf "%s\n" "$PYTHON_SITE_PKG" >&6; }
WIN_CHECKCONF_OBJ_LINK="rsrc_unbound_checkconf.o"
+
+printf "%s\n" "#define __USE_MINGW_ANSI_STDIO 1" >>confdefs.h
+
fi
if test $ac_cv_func_getaddrinfo = no; then
case " $LIBOBJS " in
withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr"
fi
for dir in $withval ; do
- if test -f "$dir/include/libmnl/libmnl.h"; then
+ if test -f "$dir/include/libmnl/libmnl.h" -o -f "$dir/include/libmnl/libmnl/libmnl.h"; then
found_libmnl="yes"
- if test "$dir" != "/usr"; then
- CPPFLAGS="$CPPFLAGS -I$dir/include"
+ extralibmnl=""
+ if test -f "$dir/include/libmnl/libmnl/libmnl.h"; then
+ extralibmnl="/libmnl"
+ fi
+ if test "$dir" != "/usr" -o -n "$extralibmnl"; then
+ CPPFLAGS="$CPPFLAGS -I$dir/include$extralibmnl"
+ fi
+ if test "$dir" != "/usr"; then
LDFLAGS="$LDFLAGS -L$dir/lib"
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: found in $dir" >&5
-version=1.15.0
+version=1.16.0
date=`date +'%b %e, %Y'`
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by unbound $as_me 1.15.0, which was
+This file was extended by unbound $as_me 1.16.0, which was
generated by GNU Autoconf 2.71. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config='$ac_cs_config_escaped'
ac_cs_version="\\
-unbound config.status 1.15.0
+unbound config.status 1.16.0
configured by $0, generated by GNU Autoconf 2.71,
with options \\"\$ac_cs_config\\"
# must be numbers. ac_defun because of later processing
m4_define([VERSION_MAJOR],[1])
-m4_define([VERSION_MINOR],[15])
+m4_define([VERSION_MINOR],[16])
m4_define([VERSION_MICRO],[0])
AC_INIT([unbound],m4_defn([VERSION_MAJOR]).m4_defn([VERSION_MINOR]).m4_defn([VERSION_MICRO]),[unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues],[unbound])
AC_SUBST(UNBOUND_VERSION_MAJOR, [VERSION_MAJOR])
AC_SUBST(UNBOUND_VERSION_MICRO, [VERSION_MICRO])
LIBUNBOUND_CURRENT=9
-LIBUNBOUND_REVISION=15
+LIBUNBOUND_REVISION=16
LIBUNBOUND_AGE=1
# 1.0.0 had 0:12:0
# 1.0.1 had 0:13:0
# 1.13.2 had 9:13:1
# 1.14.0 had 9:14:1
# 1.15.0 had 9:15:1
+# 1.16.0 had 9:16:1
# Current -- the number of the binary API that we're implementing
# Revision -- which iteration of the implementation of the binary
AC_SUBST(WIN_CONTROL_OBJ_LINK)
WIN_CHECKCONF_OBJ_LINK="rsrc_unbound_checkconf.o"
AC_SUBST(WIN_CHECKCONF_OBJ_LINK)
+ AC_DEFINE(__USE_MINGW_ANSI_STDIO, 1, [defined to use gcc ansi snprintf and sscanf that understands %lld when compiled for windows.])
fi
if test $ac_cv_func_getaddrinfo = no; then
AC_LIBOBJ([fake-rfc2553])
withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr"
fi
for dir in $withval ; do
- if test -f "$dir/include/libmnl/libmnl.h"; then
+ if test -f "$dir/include/libmnl/libmnl.h" -o -f "$dir/include/libmnl/libmnl/libmnl.h"; then
found_libmnl="yes"
dnl assume /usr is in default path.
+ extralibmnl=""
+ if test -f "$dir/include/libmnl/libmnl/libmnl.h"; then
+ extralibmnl="/libmnl"
+ fi
+ if test "$dir" != "/usr" -o -n "$extralibmnl"; then
+ CPPFLAGS="$CPPFLAGS -I$dir/include$extralibmnl"
+ fi
if test "$dir" != "/usr"; then
- CPPFLAGS="$CPPFLAGS -I$dir/include"
LDFLAGS="$LDFLAGS -L$dir/lib"
fi
AC_MSG_RESULT(found in $dir)
#include <ws2tcpip.h>
#endif
-#ifndef USE_WINSOCK
+#if !defined(USE_WINSOCK) || !defined(HAVE_SNPRINTF) || defined(SNPRINTF_RET_BROKEN) || defined(__USE_MINGW_ANSI_STDIO)
#define ARG_LL "%ll"
#else
#define ARG_LL "%I64"
if(!acl) return 0;
return sizeof(*acl) + regional_get_mem(acl->region);
}
+
+const char* acl_access_to_str(enum acl_access acl)
+{
+ switch(acl) {
+ case acl_deny: return "deny";
+ case acl_refuse: return "refuse";
+ case acl_deny_non_local: return "deny_non_local";
+ case acl_refuse_non_local: return "refuse_non_local";
+ case acl_allow: return "allow";
+ case acl_allow_snoop: return "allow_snoop";
+ case acl_allow_setrd: return "allow_setrd";
+ default: break;
+ }
+ return "unknown";
+}
+
+void
+log_acl_action(const char* action, struct sockaddr_storage* addr,
+ socklen_t addrlen, enum acl_access acl, struct acl_addr* acladdr)
+{
+ char a[128], n[128];
+ uint16_t port;
+ addr_to_str(addr, addrlen, a, sizeof(a));
+ port = ntohs(((struct sockaddr_in*)addr)->sin_port);
+ if(acladdr) {
+ addr_to_str(&acladdr->node.addr, acladdr->node.addrlen,
+ n, sizeof(n));
+ verbose(VERB_ALGO, "%s query from %s port %d because of "
+ "%s/%d %s", action, a, (int)port, n, acladdr->node.net,
+ acl_access_to_str(acl));
+ } else {
+ verbose(VERB_ALGO, "%s query from %s port %d", action, a,
+ (int)port);
+ }
+}
*/
size_t acl_list_get_mem(struct acl_list* acl);
+/*
+ * Get string for acl access specification
+ * @param acl: access type value
+ * @return string
+ */
+const char* acl_access_to_str(enum acl_access acl);
+
+/* log acl and addr for action */
+void log_acl_action(const char* action, struct sockaddr_storage* addr,
+ socklen_t addrlen, enum acl_access acl, struct acl_addr* acladdr);
+
#endif /* DAEMON_ACL_LIST_H */
#include "services/cache/rrset.h"
#include "services/cache/dns.h"
#include "services/cache/infra.h"
+#include "services/outside_network.h"
#include "util/data/msgreply.h"
#include "util/regional.h"
#include "util/net_help.h"
#include "util/data/dname.h"
+#include "util/config_file.h"
#include "iterator/iterator.h"
#include "iterator/iter_delegpt.h"
#include "iterator/iter_utils.h"
"cache; goes to configured roots\n");
}
/* go up? */
- if(iter_dp_is_useless(&qinfo, BIT_RD, dp)) {
+ if(iter_dp_is_useless(&qinfo, BIT_RD, dp,
+ (worker->env.cfg->do_ip4 && worker->back->num_ip4 != 0),
+ (worker->env.cfg->do_ip6 && worker->back->num_ip6 != 0))) {
print_dp_main(ssl, dp, msg);
print_dp_details(ssl, worker, dp);
if(!ssl_printf(ssl, "cache delegation was "
/** ratelimit for error responses */
#define ERROR_RATELIMIT 100 /* qps */
-/**
+/**
* seconds to add to prefetch leeway. This is a TTL that expires old rrsets
* earlier than they should in order to put the new update into the cache.
* This additional value is to make sure that if not all TTLs are equal in
msg->rep, LDNS_RCODE_SERVFAIL, edns, repinfo, worker->scratchpad,
worker->env.now_tv))
return 0;
+ /* TODO store the reason for the bogus reply in cache
+ * and implement in here instead of the hardcoded EDE */
+ if (worker->env.cfg->ede) {
+ EDNS_OPT_LIST_APPEND_EDE(&edns->opt_list_out,
+ worker->scratchpad, LDNS_EDE_DNSSEC_BOGUS, "");
+ }
error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
&msg->qinfo, id, flags, edns);
if(worker->stats.extended) {
return 1;
if(!respip_rewrite_reply(qinfo, cinfo, rep, encode_repp, &actinfo,
- alias_rrset, 0, worker->scratchpad, az))
+ alias_rrset, 0, worker->scratchpad, az, NULL))
return 0;
/* xxx_deny actions mean dropping the reply, unless the original reply
LDNS_RCODE_SERVFAIL, edns, repinfo, worker->scratchpad,
worker->env.now_tv))
goto bail_out;
+ /* TODO store the reason for the bogus reply in cache
+ * and implement in here instead of the hardcoded EDE */
+ if (worker->env.cfg->ede) {
+ EDNS_OPT_LIST_APPEND_EDE(&edns->opt_list_out,
+ worker->scratchpad, LDNS_EDE_DNSSEC_BOGUS, "");
+ }
error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
qinfo, id, flags, edns);
rrset_array_unlock_touch(worker->env.rrset_cache,
if(!*partial_repp)
goto bail_out;
}
- } else if(!reply_info_answer_encode(qinfo, encode_rep, id, flags,
- repinfo->c->buffer, timenow, 1, worker->scratchpad,
- udpsize, edns, (int)(edns->bits & EDNS_DO), *is_secure_answer)) {
- if(!inplace_cb_reply_servfail_call(&worker->env, qinfo, NULL, NULL,
- LDNS_RCODE_SERVFAIL, edns, repinfo, worker->scratchpad,
- worker->env.now_tv))
- edns->opt_list_inplace_cb_out = NULL;
- error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
- qinfo, id, flags, edns);
+ } else {
+ /* We don't check the global ede as this is a warning, not
+ * an error */
+ if (*is_expired_answer == 1 &&
+ worker->env.cfg->ede_serve_expired && worker->env.cfg->ede) {
+ EDNS_OPT_LIST_APPEND_EDE(&edns->opt_list_out,
+ worker->scratchpad, LDNS_EDE_STALE_ANSWER, "");
+ }
+ if(!reply_info_answer_encode(qinfo, encode_rep, id, flags,
+ repinfo->c->buffer, timenow, 1, worker->scratchpad,
+ udpsize, edns, (int)(edns->bits & EDNS_DO),
+ *is_secure_answer)) {
+ if(!inplace_cb_reply_servfail_call(&worker->env, qinfo,
+ NULL, NULL, LDNS_RCODE_SERVFAIL, edns, repinfo,
+ worker->scratchpad, worker->env.now_tv))
+ edns->opt_list_inplace_cb_out = NULL;
+ error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
+ qinfo, id, flags, edns);
+ }
}
/* cannot send the reply right now, because blocking network syscall
* is bad while holding locks. */
/** Reply to client and perform prefetch to keep cache up to date. */
static void
-reply_and_prefetch(struct worker* worker, struct query_info* qinfo,
- uint16_t flags, struct comm_reply* repinfo, time_t leeway, int noreply)
+reply_and_prefetch(struct worker* worker, struct query_info* qinfo,
+ uint16_t flags, struct comm_reply* repinfo, time_t leeway, int noreply,
+ int rpz_passthru, struct edns_option* opt_list)
{
- /* first send answer to client to keep its latency
+ (void)opt_list;
+ /* first send answer to client to keep its latency
* as small as a cachereply */
if(!noreply) {
if(repinfo->c->tcp_req_info) {
comm_point_send_reply(repinfo);
}
server_stats_prefetch(&worker->stats, worker);
-
+#ifdef CLIENT_SUBNET
+ /* Check if the subnet module is enabled. In that case pass over the
+ * comm_reply information for ECS generation later. The mesh states are
+ * unique when subnet is enabled. */
+ if(modstack_find(&worker->env.mesh->mods, "subnetcache") != -1
+ && worker->env.unique_mesh) {
+ mesh_new_prefetch(worker->env.mesh, qinfo, flags, leeway +
+ PREFETCH_EXPIRY_ADD, rpz_passthru, repinfo, opt_list);
+ return;
+ }
+#endif
/* create the prefetch in the mesh as a normal lookup without
* client addrs waiting, which has the cache blacklisted (to bypass
* the cache and go to the network for the data). */
/* this (potentially) runs the mesh for the new query */
- mesh_new_prefetch(worker->env.mesh, qinfo, flags, leeway +
- PREFETCH_EXPIRY_ADD);
+ mesh_new_prefetch(worker->env.mesh, qinfo, flags, leeway +
+ PREFETCH_EXPIRY_ADD, rpz_passthru, NULL, NULL);
}
/**
static int
deny_refuse(struct comm_point* c, enum acl_access acl,
enum acl_access deny, enum acl_access refuse,
- struct worker* worker, struct comm_reply* repinfo)
+ struct worker* worker, struct comm_reply* repinfo,
+ struct acl_addr* acladdr, int ede)
{
if(acl == deny) {
+ if(verbosity >= VERB_ALGO) {
+ log_acl_action("dropped", &repinfo->addr,
+ repinfo->addrlen, acl, acladdr);
+ log_buf(VERB_ALGO, "dropped", c->buffer);
+ }
comm_point_drop_reply(repinfo);
if(worker->stats.extended)
worker->stats.unwanted_queries++;
return 0;
} else if(acl == refuse) {
- log_addr(VERB_ALGO, "refused query from",
- &repinfo->addr, repinfo->addrlen);
- log_buf(VERB_ALGO, "refuse", c->buffer);
+ size_t opt_rr_mark;
+
+ if(verbosity >= VERB_ALGO) {
+ log_acl_action("refused", &repinfo->addr,
+ repinfo->addrlen, acl, acladdr);
+ log_buf(VERB_ALGO, "refuse", c->buffer);
+ }
+
if(worker->stats.extended)
worker->stats.unwanted_queries++;
if(worker_check_request(c->buffer, worker) == -1) {
comm_point_drop_reply(repinfo);
return 0; /* discard this */
}
- 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);
+ /* worker_check_request() above guarantees that the buffer contains at
+ * least a header and that qdcount == 1
+ */
+ log_assert(sldns_buffer_limit(c->buffer) >= LDNS_HEADER_SIZE
+ && LDNS_QDCOUNT(sldns_buffer_begin(c->buffer)) == 1);
+
+ sldns_buffer_skip(c->buffer, LDNS_HEADER_SIZE); /* skip header */
+
+ /* check additional section is present and that we respond with EDEs */
+ if(LDNS_ARCOUNT(sldns_buffer_begin(c->buffer)) != 1
+ || !ede) {
+ LDNS_QDCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_ANCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_NSCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_QR_SET(sldns_buffer_begin(c->buffer));
+ LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
+ LDNS_RCODE_REFUSED);
+ sldns_buffer_flip(c->buffer);
+ return 1;
+ }
+
+ if (!query_dname_len(c->buffer)) {
+ LDNS_QDCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_ANCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_NSCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_QR_SET(sldns_buffer_begin(c->buffer));
+ LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
+ LDNS_RCODE_FORMERR);
+ sldns_buffer_set_position(c->buffer, LDNS_HEADER_SIZE);
+ sldns_buffer_flip(c->buffer);
+ return 1;
+ }
+ /* space available for query type and class? */
+ if (sldns_buffer_remaining(c->buffer) < 2 * sizeof(uint16_t)) {
+ LDNS_QR_SET(sldns_buffer_begin(c->buffer));
+ LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
+ LDNS_RCODE_FORMERR);
+ LDNS_QDCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_ANCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_NSCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ sldns_buffer_set_position(c->buffer, LDNS_HEADER_SIZE);
+ sldns_buffer_flip(c->buffer);
+ return 1;
+ }
LDNS_QR_SET(sldns_buffer_begin(c->buffer));
LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
LDNS_RCODE_REFUSED);
- sldns_buffer_set_position(c->buffer, LDNS_HEADER_SIZE);
+
+ sldns_buffer_skip(c->buffer, (ssize_t)sizeof(uint16_t)); /* skip qtype */
+
+ sldns_buffer_skip(c->buffer, (ssize_t)sizeof(uint16_t)); /* skip qclass */
+
+ /* The OPT RR to be returned should come directly after
+ * the query, so mark this spot.
+ */
+ opt_rr_mark = sldns_buffer_position(c->buffer);
+
+ /* Skip through the RR records */
+ if(LDNS_ANCOUNT(sldns_buffer_begin(c->buffer)) != 0 ||
+ LDNS_NSCOUNT(sldns_buffer_begin(c->buffer)) != 0) {
+ if(!skip_pkt_rrs(c->buffer,
+ ((int)LDNS_ANCOUNT(sldns_buffer_begin(c->buffer)))+
+ ((int)LDNS_NSCOUNT(sldns_buffer_begin(c->buffer))))) {
+ LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
+ LDNS_RCODE_FORMERR);
+ LDNS_ANCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_NSCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ sldns_buffer_set_position(c->buffer, opt_rr_mark);
+ sldns_buffer_flip(c->buffer);
+ return 1;
+ }
+ }
+ /* Do we have a valid OPT RR here? If not return REFUSED (could be a valid TSIG or something so no FORMERR) */
+ /* domain name must be the root of length 1. */
+ if(sldns_buffer_remaining(c->buffer) < 1 || *sldns_buffer_current(c->buffer) != 0) {
+ LDNS_ANCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_NSCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ sldns_buffer_set_position(c->buffer, opt_rr_mark);
+ sldns_buffer_flip(c->buffer);
+ return 1;
+ } else {
+ sldns_buffer_skip(c->buffer, 1); /* skip root label */
+ }
+ if(sldns_buffer_remaining(c->buffer) < 2 ||
+ sldns_buffer_read_u16(c->buffer) != LDNS_RR_TYPE_OPT) {
+ LDNS_ANCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_NSCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ sldns_buffer_set_position(c->buffer, opt_rr_mark);
+ sldns_buffer_flip(c->buffer);
+ return 1;
+ }
+ /* Write OPT RR directly after the query,
+ * so without the (possibly skipped) Answer and NS RRs
+ */
+ LDNS_ANCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ LDNS_NSCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ sldns_buffer_clear(c->buffer); /* reset write limit */
+ sldns_buffer_set_position(c->buffer, opt_rr_mark);
+
+ /* Check if OPT record can be written
+ * 17 == root label (1) + RR type (2) + UDP Size (2)
+ * + Fields (4) + rdata len (2) + EDE Option code (2)
+ * + EDE Option length (2) + EDE info-code (2)
+ */
+ if (sldns_buffer_available(c->buffer, 17) == 0) {
+ LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
+ sldns_buffer_flip(c->buffer);
+ return 1;
+ }
+
+ LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 1);
+
+ /* root label */
+ sldns_buffer_write_u8(c->buffer, 0);
+ sldns_buffer_write_u16(c->buffer, LDNS_RR_TYPE_OPT);
+ sldns_buffer_write_u16(c->buffer, EDNS_ADVERTISED_SIZE);
+
+ /* write OPT Record TTL Field */
+ sldns_buffer_write_u32(c->buffer, 0);
+
+ /* write rdata len: EDE option + length + info-code */
+ sldns_buffer_write_u16(c->buffer, 6);
+
+ /* write OPTIONS; add EDE option code */
+ sldns_buffer_write_u16(c->buffer, LDNS_EDNS_EDE);
+
+ /* write single EDE option length (for just 1 info-code) */
+ sldns_buffer_write_u16(c->buffer, 2);
+
+ /* write single EDE info-code */
+ sldns_buffer_write_u16(c->buffer, LDNS_EDE_PROHIBITED);
+
sldns_buffer_flip(c->buffer);
+
+ verbose(VERB_ALGO, "attached EDE code: %d", LDNS_EDE_PROHIBITED);
+
return 1;
+
}
return -1;
static int
deny_refuse_all(struct comm_point* c, enum acl_access acl,
- struct worker* worker, struct comm_reply* repinfo)
+ struct worker* worker, struct comm_reply* repinfo,
+ struct acl_addr* acladdr, int ede)
{
- return deny_refuse(c, acl, acl_deny, acl_refuse, worker, repinfo);
+ return deny_refuse(c, acl, acl_deny, acl_refuse, worker, repinfo,
+ acladdr, ede);
}
static int
deny_refuse_non_local(struct comm_point* c, enum acl_access acl,
- struct worker* worker, struct comm_reply* repinfo)
+ struct worker* worker, struct comm_reply* repinfo,
+ struct acl_addr* acladdr, int ede)
{
- return deny_refuse(c, acl, acl_deny_non_local, acl_refuse_non_local, worker, repinfo);
+ return deny_refuse(c, acl, acl_deny_non_local, acl_refuse_non_local,
+ worker, repinfo, acladdr, ede);
}
int
struct lruhash_entry* e;
struct query_info qinfo;
struct edns_data edns;
+ struct edns_option* original_edns_list = NULL;
enum acl_access acl;
struct acl_addr* acladdr;
int rc = 0;
int need_drop = 0;
int is_expired_answer = 0;
int is_secure_answer = 0;
+ int rpz_passthru = 0;
/* We might have to chase a CNAME chain internally, in which case
* we'll have up to two replies and combine them to build a complete
* answer. These variables control this case. */
acladdr = acl_addr_lookup(worker->daemon->acl, &repinfo->addr,
repinfo->addrlen);
acl = acl_get_control(acladdr);
- if((ret=deny_refuse_all(c, acl, worker, repinfo)) != -1)
+
+ if((ret=deny_refuse_all(c, acl, worker, repinfo, acladdr,
+ worker->env.cfg->ede)) != -1)
{
if(ret == 1)
goto send_reply;
if(worker->env.auth_zones &&
rpz_callback_from_worker_request(worker->env.auth_zones,
&worker->env, &qinfo, &edns, c->buffer, worker->scratchpad,
- repinfo, acladdr->taglist, acladdr->taglen, &worker->stats)) {
+ repinfo, acladdr->taglist, acladdr->taglen, &worker->stats,
+ &rpz_passthru)) {
regional_free_all(worker->scratchpad);
if(sldns_buffer_limit(c->buffer) == 0) {
comm_point_drop_reply(repinfo);
/* We've looked in our local zones. If the answer isn't there, we
* might need to bail out based on ACLs now. */
- if((ret=deny_refuse_non_local(c, acl, worker, repinfo)) != -1)
+ if((ret=deny_refuse_non_local(c, acl, worker, repinfo, acladdr,
+ worker->env.cfg->ede)) != -1)
{
regional_free_all(worker->scratchpad);
if(ret == 1)
* ACLs allow the snooping. */
if(!(LDNS_RD_WIRE(sldns_buffer_begin(c->buffer))) &&
acl != acl_allow_snoop ) {
+ if (worker->env.cfg->ede) {
+ EDNS_OPT_LIST_APPEND_EDE(&edns.opt_list_out,
+ worker->scratchpad, LDNS_EDE_NOT_AUTHORITATIVE, "");
+ }
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);
+ sldns_buffer_read_u16_at(c->buffer, 2), &edns);
regional_free_all(worker->scratchpad);
log_addr(VERB_ALGO, "refused nonrec (cache snoop) query from",
&repinfo->addr, repinfo->addrlen);
+
goto send_reply;
}
cinfo = &cinfo_tmp;
}
+ /* Keep the original edns list around. The pointer could change if there is
+ * a cached answer (through the inplace callback function there).
+ * No need to actually copy the contents as they shouldn't change.
+ * Used while prefetching and subnet is enabled. */
+ original_edns_list = edns.opt_list_in;
lookup_cache:
/* Lookup the cache. In case we chase an intermediate CNAME chain
* this is a two-pass operation, and lookup_qinfo is different for
< *worker->env.now)
leeway = 0;
lock_rw_unlock(&e->lock);
+
reply_and_prefetch(worker, lookup_qinfo,
sldns_buffer_read_u16_at(c->buffer, 2),
repinfo, leeway,
- (partial_rep || need_drop));
+ (partial_rep || need_drop),
+ rpz_passthru,
+ original_edns_list);
if(!partial_rep) {
rc = 0;
regional_free_all(worker->scratchpad);
verbose(VERB_ALGO, "answer from the cache failed");
lock_rw_unlock(&e->lock);
}
+
if(!LDNS_RD_WIRE(sldns_buffer_begin(c->buffer))) {
if(answer_norec_from_cache(worker, &qinfo,
*(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
/* grab a work request structure for this new request */
mesh_new_client(worker->env.mesh, &qinfo, cinfo,
sldns_buffer_read_u16_at(c->buffer, 2),
- &edns, repinfo, *(uint16_t*)(void *)sldns_buffer_begin(c->buffer));
+ &edns, repinfo, *(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
+ rpz_passthru);
regional_free_all(worker->scratchpad);
worker_mem_report(worker, NULL);
return 0;
case SIGHUP:
comm_base_exit(worker->base);
break;
+#endif
+#ifdef SIGBREAK
+ case SIGBREAK:
#endif
case SIGINT:
worker->need_to_exit = 1;
if(do_sigs) {
#ifdef SIGHUP
ub_thread_sig_unblock(SIGHUP);
+#endif
+#ifdef SIGBREAK
+ ub_thread_sig_unblock(SIGBREAK);
#endif
ub_thread_sig_unblock(SIGINT);
#ifdef SIGQUIT
|| !comm_signal_bind(worker->comsig, SIGQUIT)
#endif
|| !comm_signal_bind(worker->comsig, SIGTERM)
+#ifdef SIGBREAK
+ || !comm_signal_bind(worker->comsig, SIGBREAK)
+#endif
|| !comm_signal_bind(worker->comsig, SIGINT)) {
log_err("could not create signal handlers");
worker_delete(worker);
7 February 2022: Wouter
- Fix that TCP interface does not use TLS when TLS is also configured.
+27 May 2022: Wouter
+ - Fix #684: [FTBS] configure script error with libmnl on openSUSE 15.3 (and possibly other distributions)
+ - Version is set to 1.16.0 for release. Release tag 1.16.0rc1.
+
+20 May 2022: Wouter
+ - Fix to silence test for ede error output to the console from the
+ test setup script.
+ - Fix ede test to not use default pidfile, and use local interface.
+ - Fix some lint type warnings.
+
+18 May 2022: George
+ - Fix typos in config_set_option for the 'num-threads' and
+ 'ede-serve-expired' options.
+
+15 May 2022: George
+ - Fix #678: [FR] modify behaviour of unbound-control rpz_enable zone,
+ by updating unbound-control's documentation.
+
+12 May 2022: George
+ - Fix #417: prefetch and ECS causing cache corruption when used
+ together.
+
+12 May 2022: Wouter
+ - Merge #677: Allow using system certificates not only on Windows,
+ from pemensik.
+ - For #677: Added tls-system-cert to config parser and documentation.
+
+11 May 2022: Wouter
+ - Fix #673: DNS over TLS: error: SSL_handshake syscall: No route to
+ host.
+
+10 May 2022: George
+ - Fix Python build in non-source directory; based on patch by
+ Michael Tokarev.
+
+6 May 2022: Tom
+ - Merge PR #604: Add basic support for EDE (RFC8914).
+
+28 April 2022: Wouter
+ - Fix #670: SERVFAIL problems with unbound 1.15.0 running on
+ OpenBSD 7.1.
+
+8 April 2022: Wouter
+ - Fix zonemd check to allow unsupported algorithms to load.
+ If there are only unsupported algorithms, or unsupported schemes,
+ and no failed or successful other ZONEMD records, or malformed
+ or bad ZONEMD records, the unsupported records allow the zone load.
+ - Fix zonemd unsupported algo check.
+ - Fix zonemd unsupported algo check reason to not copy to next record,
+ and check for success for debug printout.
+ - Fix zonemd unsupported algo check to print unsupported reason before
+ zeroing it.
+ - Fix zonemd unsupported algo check to set reason to NULL before the
+ check routine, but after malformed checks, to get the correct NULL
+ output when the digest matches.
+
+25 March 2022: Wouter
+ - Fix spelling error in comment in sldns_str2wire_svcparam_key_lookup.
+
+23 March 2022: Wouter
+ - Fix #651: [FR] Better logging for refused queries.
+
+18 March 2022: George
+ - Merge PR #648 from eaglegai: fix -q doesn't work when use with
+ 'unbound-control stats_shm'.
+
+17 March 2022: Wouter
+ - Fix to describe auth-zone and other configuration at the local-zone
+ configuration option, to allow for more broadly view of the options.
+
+16 March 2022: Wouter
+ - Fix to ensure uniform handling of spaces and tabs when parsing RRs.
+
+9 March 2022: Wouter
+ - Merge #644: Make `install-lib` make target install the pkg-config
+ file.
+
+7 March 2022: Wouter
+ - Fix configure for python to use sysutils, because distutils is
+ deprecated. It uses sysutils when available, distutils otherwise.
+
+3 March 2022: Wouter
+ - Fix #637: Integer Overflow in sldns_str2period function.
+ - Fix for #637: fix integer overflow checks in sldns_str2period.
+
+2 March 2022: George
+ - Merge PR #632 from scottrw93: Match cnames in ipset.
+ - Various fixes for #632: variable initialisation, convert the qinfo
+ to str once, accept trailing dot in the local-zone ipset option.
+
+2 March 2022: Wouter
+ - Fix compile warnings for printf ll format on mingw compile.
+
+1 March 2022: Wouter
+ - Fix pythonmod for change in iter_dp_is_useless function prototype.
+
+28 February 2022: George
+ - Fix #630: Unify the RPZ log messages.
+ - Merge #623 from rex4539: Fix typos.
+
+28 February 2022: Wouter
+ - Fix #633: Document unix domain socket support for unbound-control.
+ - Fix for #633: updated fix with new text.
+ - Fix edns client subnet to add the option based on the option list,
+ so that it is not state dependent, after the state fix of #605 for
+ double EDNS options.
+ - Fix for edns client subnet option add fix in removal code, from review.
+
+25 February 2022: Wouter
+ - Fix to detect that no IPv6 support means that IPv6 addresses are
+ useless for delegation point lookups.
+ - update Makefile dependencies.
+ - Fix check interface existence for support detection in remote lookup.
+
+18 February 2022: Wouter
+ - Fix that address not available is squelched from the logs for
+ udp connect failures. It is visible on verbosity 4 and more.
+ - Merge #631 from mollyim: Replace OpenSSL's ERR_PACK with
+ ERR_GET_REASON.
+
+16 February 2022: Wouter
+ - Fix for #628: fix rpz-passthru for qname trigger by localzone type.
+
+15 February 2022: Wouter
+ - Fix #628: A rpz-passthru action is not ending RPZ zone processing.
+
+11 February 2022: Wouter
+ - Fix #624: Unable to stop Unbound in Windows console (does not
+ respond to CTRL+C command).
+ - Fix #618: enabling interface-automatic disables DNS-over-TLS.
+ Adds the option to list interface-automatic-ports.
+ - Remove debug info from #618 fix.
+
+7 February 2022: Wouter
+ - Fix that TCP interface does not use TLS when TLS is also configured.
+
+4 February 2022: Wouter
+ - Fix #412: cache invalidation issue with CNAME+A.
+
3 February 2022: Wouter
- Fix for #611: Integer overflow in sldns_wire2str_pkt_scan.
+ - Tag for 1.15.0rc1 created. That became 1.15.0 on 10 feb 2022.
+ The repository continues with version 1.15.1.
2 February 2022: George
- Merge PR #532 from Shchelk: Fix: buffer overflow bug.
-README for Unbound 1.15.0
+README for Unbound 1.16.0
Copyright 2007 NLnet Labs
http://unbound.net
#
# Example configuration file.
#
-# See unbound.conf(5) man page, version 1.15.0.
+# See unbound.conf(5) man page, version 1.16.0.
#
# this is a comment.
# Socket options are not supported on all platforms. experimental.
# interface-automatic: no
+ # instead of the default port, open additional ports separated by
+ # spaces when interface-automatic is enabled, by listing them here.
+ # interface-automatic-ports: ""
+
# port to answer queries from
# port: 53
# Add system certs to the cert bundle, from the Windows Cert Store
# tls-win-cert: no
+ # and on other systems, the default openssl certificates
+ # tls-system-cert: no
# Pad queries over TLS upstreams
# pad-queries: yes
# the number of servers that will be used in the fast server selection.
# fast-server-num: 3
+ # Enable to attach Extended DNS Error codes (RFC8914) to responses.
+ # ede: no
+
+ # Enable to attach an Extended DNS Error (RFC8914) Code 3 - Stale
+ # Answer as EDNS0 option to expired responses.
+ # Note that the ede option above needs to be enabled for this to work.
+ # ede-serve-expired: no
+
# Specific options for ipsecmod. Unbound needs to be configured with
# --enable-ipsecmod for these to take effect.
#
-.TH "libunbound" "3" "Feb 10, 2022" "NLnet Labs" "unbound 1.15.0"
+.TH "libunbound" "3" "Jun 2, 2022" "NLnet Labs" "unbound 1.16.0"
.\"
.\" libunbound.3 -- unbound library functions manual
.\"
.B ub_ctx_zone_remove,
.B ub_ctx_data_add,
.B ub_ctx_data_remove
-\- Unbound DNS validating resolver 1.15.0 functions.
+\- Unbound DNS validating resolver 1.16.0 functions.
.SH "SYNOPSIS"
.B #include <unbound.h>
.LP
-.TH "unbound-anchor" "8" "Feb 10, 2022" "NLnet Labs" "unbound 1.15.0"
+.TH "unbound-anchor" "8" "Jun 2, 2022" "NLnet Labs" "unbound 1.16.0"
.\"
.\" unbound-anchor.8 -- unbound anchor maintenance utility manual
.\"
-.TH "unbound-checkconf" "8" "Feb 10, 2022" "NLnet Labs" "unbound 1.15.0"
+.TH "unbound-checkconf" "8" "Jun 2, 2022" "NLnet Labs" "unbound 1.16.0"
.\"
.\" unbound-checkconf.8 -- unbound configuration checker manual
.\"
-.TH "unbound-control" "8" "Feb 10, 2022" "NLnet Labs" "unbound 1.15.0"
+.TH "unbound-control" "8" "Jun 2, 2022" "NLnet Labs" "unbound 1.16.0"
.\"
.\" unbound-control.8 -- unbound remote control manual
.\"
ips are dropped before checking the cache.
.TP
.B list_auth_zones
-List the auth zones that are configured. Printed one per line with a
-status, indicating if the zone is expired and current serial number.
+List the auth zones that are configured. Printed one per line with a status,
+indicating if the zone is expired and current serial number. Configured RPZ
+zones are included.
.TP
.B auth_zone_reload \fIzone\fR
-Reload the auth zone from zonefile. The zonefile is read in overwriting
-the current contents of the zone in memory. This changes the auth zone
-contents itself, not the cache contents. Such cache contents exists if
-you set Unbound to validate with for-upstream yes and that can be cleared
-with \fBflush_zone\fR \fIzone\fR.
+Reload the auth zone (or RPZ zone) from zonefile. The zonefile is read in
+overwriting the current contents of the zone in memory. This changes the auth
+zone contents itself, not the cache contents. Such cache contents exists if
+you set Unbound to validate with for-upstream yes and that can be cleared with
+\fBflush_zone\fR \fIzone\fR.
.TP
.B auth_zone_transfer \fIzone\fR
-Transfer the auth zone from master. The auth zone probe sequence is started,
-where the masters are probed to see if they have an updated zone (with the SOA
-serial check). And then the zone is transferred for a newer zone version.
+Transfer the auth zone (or RPZ zone) from master. The auth zone probe sequence
+is started, where the masters are probed to see if they have an updated zone
+(with the SOA serial check). And then the zone is transferred for a newer zone
+version.
.TP
.B rpz_enable \fIzone\fR
Enable the RPZ zone if it had previously been disabled.
-.TH "unbound\-host" "1" "Feb 10, 2022" "NLnet Labs" "unbound 1.15.0"
+.TH "unbound\-host" "1" "Jun 2, 2022" "NLnet Labs" "unbound 1.16.0"
.\"
.\" unbound-host.1 -- unbound DNS lookup utility
.\"
-.TH "unbound" "8" "Feb 10, 2022" "NLnet Labs" "unbound 1.15.0"
+.TH "unbound" "8" "Jun 2, 2022" "NLnet Labs" "unbound 1.16.0"
.\"
.\" unbound.8 -- unbound manual
.\"
.\"
.SH "NAME"
.B unbound
-\- Unbound DNS validating resolver 1.15.0.
+\- Unbound DNS validating resolver 1.16.0.
.SH "SYNOPSIS"
.B unbound
.RB [ \-h ]
-.TH "unbound.conf" "5" "Feb 10, 2022" "NLnet Labs" "unbound 1.15.0"
+.TH "unbound.conf" "5" "Jun 2, 2022" "NLnet Labs" "unbound 1.16.0"
.\"
.\" unbound.conf.5 -- unbound.conf manual
.\"
service on. This feature is experimental, and needs support in your OS for
particular socket options. Default value is no.
.TP
+.B interface\-automatic\-ports: \fI<string>
+List the port numbers that interface-automatic listens on. If empty, the
+default port is listened on. The port numbers are separated by spaces in the
+string. Default is "".
+.IP
+This can be used to have interface automatic to deal with the interface,
+and listen on the normal port number, by including it in the list, and
+also https or dns over tls port numbers by putting them in the list as well.
+.TP
.B outgoing\-interface: \fI<ip address or ip6 netblock>
Interface to use to connect to the network. This interface is used to send
queries to authoritative servers and receive their replies. Can be given
Default is no. Useful in tunneling scenarios. The TLS contains plain DNS in
TCP wireformat. The other server must support this (see
\fBtls\-service\-key\fR).
-If you enable this, also configure a tls\-cert\-bundle or use tls\-win\-cert to
-load CA certs, otherwise the connections cannot be authenticated.
-This option enables TLS for all of them, but if you do not set this you can
-configure TLS specifically for some forward zones with forward\-tls\-upstream. And also with stub\-tls\-upstream.
+If you enable this, also configure a tls\-cert\-bundle or use tls\-win\-cert or
+tls\-system\-cert to load CA certs, otherwise the connections cannot be
+authenticated. This option enables TLS for all of them, but if you do not set
+this you can configure TLS specifically for some forward zones with
+forward\-tls\-upstream. And also with stub\-tls\-upstream.
.TP
.B ssl\-upstream: \fI<yes or no>
Alternate syntax for \fBtls\-upstream\fR. If both are present in the config
Add the system certificates to the cert bundle certificates for authentication.
If no cert bundle, it uses only these certificates. Default is no.
On windows this option uses the certificates from the cert store. Use
-the tls\-cert\-bundle option on other systems.
+the tls\-cert\-bundle option on other systems. On other systems, this option
+enables the system certificates.
+.TP
+.B tls\-system\-cert: \fI<yes or no>
+This the same setting as the tls\-win\-cert setting, under a different name.
+Because it is not windows specific.
.TP
.B tls\-additional\-port: \fI<portnr>
List portnumbers as tls\-additional\-port, and when interfaces are defined,
.IP
If you need more complicated authoritative data, with referrals, wildcards,
CNAME/DNAME support, or DNSSEC authoritative service, setup a stub\-zone for
-it as detailed in the stub zone section below.
+it as detailed in the stub zone section below. A stub\-zone can be used to
+have unbound send queries to another server, an authoritative server, to
+fetch the information. With a forward\-zone, unbound sends queries to a server
+that is a recursive server to fetch the information. With an auth\-zone a
+zone can be loaded from file and used, it can be used like a local\-zone
+for users downstream, or the auth\-zone information can be used to fetch
+information from when resolving like it is an upstream server. The
+forward\-zone and auth\-zone options are described in their sections below.
+If you want to perform filtering of the information that the users can fetch,
+the local\-zone and local\-data statements allow for this, but also the
+rpz functionality can be used, described in the RPZ section.
.TP 10
\h'5'\fIdeny\fR
Do not send an answer, drop the query.
EDNS0 option code for the \fIedns\-client\-string\fR option, from 0 to 65535.
A value from the `Reserved for Local/Experimental` range (65001-65534) should
be used. Default is 65001.
+.TP 5
+.B ede: \fI<yes or no>
+If enabled, Unbound will respond with Extended DNS Error codes (RFC8914).
+These EDEs attach informative error messages to a response for various
+errors. Default is "no".
+
+When the \fBval-log-level\fR option is also set to \fB2\fR, responses with
+Extended DNS Errors concerning DNSSEC failures that are not served from cache,
+will also contain a descriptive text message about the reason for the failure.
+.TP
+.B ede\-serve\-expired: \fI<yes or no>
+If enabled, Unbound will attach an Extended DNS Error (RFC8914) Code 3 - Stale
+Answer as EDNS0 option to the expired response. Note that this will not attach
+the EDE code without setting the global \fBede\fR option to "yes" as well.
+Default is "no".
.SS "Remote Control Options"
In the
.B remote\-control:
If you change this and permissions have been dropped, you must restart
the server for the change to take effect.
.IP
-If you set it to an absolute path, a local socket is used. The local socket
+If you set it to an absolute path, a unix domain socket is used. This socket
does not use the certificates and keys, so those files need not be present.
To restrict access, Unbound sets permissions on the file to the user and
group that is configured, the access bits are set to allow the group members
}
/** Add ecs struct to edns list, after parsing it to wire format. */
-static void
-ecs_opt_list_append(struct ecs_data* ecs, struct edns_option** list,
+void
+subnet_ecs_opt_list_append(struct ecs_data* ecs, struct edns_option** list,
struct module_qstate *qstate)
{
size_t sn_octs, sn_octs_remainder;
/* Address on whitelist or client query contains ECS option, we
* want to sent out ECS. Only add option if it is not already
* set. */
- if(!(sq->subnet_sent)) {
- ecs_opt_list_append(&sq->ecs_server_out,
+ if(!edns_opt_list_find(qstate->edns_opts_back_out,
+ qstate->env->cfg->client_subnet_opcode)) {
+ subnet_ecs_opt_list_append(&sq->ecs_server_out,
&qstate->edns_opts_back_out, qstate);
- sq->subnet_sent = 1;
}
+ sq->subnet_sent = 1;
}
- else if(sq->subnet_sent) {
+ else {
/* Outgoing ECS option is set, but we don't want to sent it to
* this address, remove option. */
- edns_opt_list_remove(&qstate->edns_opts_back_out,
- qstate->env->cfg->client_subnet_opcode);
+ if(edns_opt_list_find(qstate->edns_opts_back_out,
+ qstate->env->cfg->client_subnet_opcode)) {
+ edns_opt_list_remove(&qstate->edns_opts_back_out,
+ qstate->env->cfg->client_subnet_opcode);
+ }
sq->subnet_sent = 0;
}
return 1;
env->unique_mesh = 1;
if(!edns_register_option(env->cfg->client_subnet_opcode,
env->cfg->client_subnet_always_forward /* bypass cache */,
- 0 /* no aggregation */, env)) {
+ 1 /* no aggregation */, env)) {
log_err("subnetcache: could not register opcode");
ecs_whitelist_delete(sn_env->whitelist);
slabhash_delete(sn_env->subnet_msg_cache);
return 1;
}
-static void
+void
subnet_option_from_ss(struct sockaddr_storage *ss, struct ecs_data* ecs,
struct config_file* cfg)
{
verbose(VERB_QUERY, "subnetcache: answered from cache");
qstate->ext_state[id] = module_finished;
- ecs_opt_list_append(&sq->ecs_client_out,
+ subnet_ecs_opt_list_append(&sq->ecs_client_out,
&qstate->edns_opts_front_out, qstate);
return;
}
sq->ecs_server_out.subnet_source_mask =
qstate->env->cfg->max_client_subnet_ipv6;
/* Safe to copy completely, even if the source is limited by the
- * configuration. ecs_opt_list_append() will limit the address.
+ * configuration. subnet_ecs_opt_list_append() will limit the address.
* */
memcpy(&sq->ecs_server_out.subnet_addr,
sq->ecs_client_in.subnet_addr, INET6_SIZE);
qstate->ext_state[id] = eval_response(qstate, id, sq);
if(qstate->ext_state[id] == module_finished &&
qstate->return_msg) {
- ecs_opt_list_append(&sq->ecs_client_out,
+ subnet_ecs_opt_list_append(&sq->ecs_client_out,
&qstate->edns_opts_front_out, qstate);
}
qstate->no_cache_store = sq->started_no_cache_store;
/** mark subnet msg to be deleted */
void subnet_markdel(void* key);
+/** Add ecs struct to edns list, after parsing it to wire format. */
+void subnet_ecs_opt_list_append(struct ecs_data* ecs, struct edns_option** list,
+ struct module_qstate *qstate);
+
+/** Create ecs_data from the sockaddr_storage information. */
+void subnet_option_from_ss(struct sockaddr_storage *ss, struct ecs_data* ecs,
+ struct config_file* cfg);
#endif /* SUBNETMOD_H */
uint8_t done_pside6;
/** the TLS authentication name, (if not NULL) to use. */
char* tls_auth_name;
- /** the port to use; it should mosty be the default 53 but configured
+ /** the port to use; it should mostly be the default 53 but configured
* upstreams can provide nondefault ports. */
int port;
};
int
iter_dp_is_useless(struct query_info* qinfo, uint16_t qflags,
- struct delegpt* dp)
+ struct delegpt* dp, int supports_ipv4, int supports_ipv6)
{
struct delegpt_ns* ns;
+ struct delegpt_addr* a;
/* check:
* o RD qflag is on.
* o no addresses are provided.
*/
if(!(qflags&BIT_RD))
return 0;
- /* either available or unused targets */
- if(dp->usable_list || dp->result_list)
- return 0;
+ /* either available or unused targets,
+ * if they exist, the dp is not useless. */
+ for(a = dp->usable_list; a; a = a->next_usable) {
+ if(!addr_is_ip6(&a->addr, a->addrlen) && supports_ipv4)
+ return 0;
+ else if(addr_is_ip6(&a->addr, a->addrlen) && supports_ipv6)
+ return 0;
+ }
+ for(a = dp->result_list; a; a = a->next_result) {
+ if(!addr_is_ip6(&a->addr, a->addrlen) && supports_ipv4)
+ return 0;
+ else if(addr_is_ip6(&a->addr, a->addrlen) && supports_ipv6)
+ return 0;
+ }
/* see if query is for one of the nameservers, which is glue */
- if( (qinfo->qtype == LDNS_RR_TYPE_A ||
- qinfo->qtype == LDNS_RR_TYPE_AAAA) &&
+ if( ((qinfo->qtype == LDNS_RR_TYPE_A && supports_ipv4) ||
+ (qinfo->qtype == LDNS_RR_TYPE_AAAA && supports_ipv6)) &&
dname_subdomain_c(qinfo->qname, dp->name) &&
delegpt_find_ns(dp, qinfo->qname, qinfo->qname_len))
return 1;
* @param qinfo: query name and type
* @param qflags: query flags with RD flag
* @param dp: delegpt to check.
+ * @param supports_ipv4: if we support ipv4 for lookups to the target.
+ * if not, then the IPv4 addresses are useless.
+ * @param supports_ipv6: if we support ipv6 for lookups to the target.
+ * if not, then the IPv6 addresses are useless.
* @return true if dp is useless.
*/
int iter_dp_is_useless(struct query_info* qinfo, uint16_t qflags,
- struct delegpt* dp);
+ struct delegpt* dp, int supports_ipv4, int supports_ipv6);
/**
* See if qname has DNSSEC needs. This is true if there is a trust anchor above
* same server reply) if useless-checked.
*/
if(iter_dp_is_useless(&qstate->qinfo, qstate->query_flags,
- iq->dp)) {
+ iq->dp, ie->supports_ipv4, ie->supports_ipv6)) {
struct delegpt* retdp = NULL;
if(!can_have_last_resort(qstate->env, iq->dp->name, iq->dp->namelen, iq->qchase.qclass, &retdp)) {
if(retdp) {
int missing;
int toget = 0;
+ iter_mark_cycle_targets(qstate, iq->dp);
+ missing = (int)delegpt_count_missing_targets(iq->dp);
+ log_assert(maxtargets != 0); /* that would not be useful */
+
+ /* Generate target requests. Basically, any missing targets
+ * are queried for here, regardless if it is necessary to do
+ * so to continue processing. */
+ if(maxtargets < 0 || maxtargets > missing)
+ toget = missing;
+ else toget = maxtargets;
+ if(toget == 0) {
+ *num = 0;
+ return 1;
+ }
+
+ /* now that we are sure that a target query is going to be made,
+ * check the limits. */
if(iq->depth == ie->max_dependency_depth)
return 0;
if(iq->depth > 0 && iq->target_count &&
return 0;
}
- iter_mark_cycle_targets(qstate, iq->dp);
- missing = (int)delegpt_count_missing_targets(iq->dp);
- log_assert(maxtargets != 0); /* that would not be useful */
-
- /* Generate target requests. Basically, any missing targets
- * are queried for here, regardless if it is necessary to do
- * so to continue processing. */
- if(maxtargets < 0 || maxtargets > missing)
- toget = missing;
- else toget = maxtargets;
- if(toget == 0) {
- *num = 0;
- return 1;
- }
/* select 'toget' items from the total of 'missing' items */
log_assert(toget <= missing);
iq->response = forged_response;
next_state(iq, FINISHED_STATE);
if(!iter_prepend(iq, qstate->return_msg, qstate->region)) {
- log_err("rpz, prepend rrsets: out of memory");
+ log_err("rpz: prepend rrsets: out of memory");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
return 0;
}
if(!qstate->no_cache_store)
iter_dns_store(qstate->env, &iq->response->qinfo,
- iq->response->rep, 0, qstate->prefetch_leeway,
+ iq->response->rep,
+ iq->qchase.qtype != iq->response->qinfo.qtype,
+ qstate->prefetch_leeway,
iq->dp&&iq->dp->has_parent_side_NS,
qstate->region, qstate->query_flags);
/* close down outstanding requests to be discarded */
iq->response = forged_response;
next_state(iq, FINISHED_STATE);
if(!iter_prepend(iq, qstate->return_msg, qstate->region)) {
- log_err("rpz after cname, prepend rrsets: out of memory");
+ log_err("rpz: after cname, prepend rrsets: out of memory");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
qstate->return_msg->qinfo = qstate->qinfo;
}
/* process new query */
if(!mesh_new_callback(w->env->mesh, &qinfo, qflags, &edns,
- w->back->udp_buff, qid, libworker_fg_done_cb, q)) {
+ w->back->udp_buff, qid, libworker_fg_done_cb, q, 0)) {
free(qinfo.qname);
return UB_NOMEM;
}
if(async_id)
*async_id = q->querynum;
if(!mesh_new_callback(w->env->mesh, &qinfo, qflags, &edns,
- w->back->udp_buff, qid, libworker_event_done_cb, q)) {
+ w->back->udp_buff, qid, libworker_event_done_cb, q, 0)) {
free(qinfo.qname);
return UB_NOMEM;
}
q->w = w;
/* process new query */
if(!mesh_new_callback(w->env->mesh, &qinfo, qflags, &edns,
- w->back->udp_buff, qid, libworker_bg_done_cb, q)) {
+ w->back->udp_buff, qid, libworker_bg_done_cb, q, 0)) {
add_bg_result(w, q, NULL, UB_NOMEM, NULL, 0);
}
free(qinfo.qname);
respip_use_rpz(struct resp_addr* raddr, struct rpz* r,
enum respip_action* action,
struct ub_packed_rrset_key** data, int* rpz_log, char** log_name,
- int* rpz_cname_override, struct regional* region, int* is_rpz)
+ int* rpz_cname_override, struct regional* region, int* is_rpz,
+ int* rpz_passthru)
{
+ if(rpz_passthru && *rpz_passthru)
+ return 0;
if(r->action_override == RPZ_DISABLED_ACTION) {
*is_rpz = 0;
return 1;
*data = r->cname_override;
*rpz_cname_override = 1;
}
+ if(*action == respip_always_transparent /* RPZ_PASSTHRU_ACTION */
+ && rpz_passthru)
+ *rpz_passthru = 1;
*rpz_log = r->log;
if(r->log_name)
if(!(*log_name = regional_strdup(region, r->log_name)))
const struct respip_client_info* cinfo, const struct reply_info* rep,
struct reply_info** new_repp, struct respip_action_info* actinfo,
struct ub_packed_rrset_key** alias_rrset, int search_only,
- struct regional* region, struct auth_zones* az)
+ struct regional* region, struct auth_zones* az, int* rpz_passthru)
{
const uint8_t* ctaglist;
size_t ctaglen;
ipset->tagname, ipset->num_tags);
}
lock_rw_rdlock(&az->rpz_lock);
- for(a = az->rpz_first; a && !raddr; a = a->rpz_az_next) {
+ for(a = az->rpz_first; a && !raddr && !(rpz_passthru && *rpz_passthru); a = a->rpz_az_next) {
lock_rw_rdlock(&a->lock);
r = a->rpz;
if(!r->taglist || taglist_intersect(r->taglist,
r->respip_set, &rrset_id, &rr_id))) {
if(!respip_use_rpz(raddr, r, &action, &data,
&rpz_log, &log_name, &rpz_cname_override,
- region, &rpz_used)) {
+ region, &rpz_used, rpz_passthru)) {
log_err("out of memory");
lock_rw_unlock(&raddr->lock);
lock_rw_unlock(&a->lock);
addr_to_str(&raddr->node.addr,
raddr->node.addrlen,
nm, sizeof(nm));
- verbose(VERB_ALGO, "respip: rpz response-ip trigger %s/%d on %s %s with action %s", nm, raddr->node.net, qn, ip, rpz_action_to_string(respip_action_to_rpz_action(action)));
+ verbose(VERB_ALGO, "respip: rpz: response-ip trigger %s/%d on %s %s with action %s", nm, raddr->node.net, qn, ip, rpz_action_to_string(respip_action_to_rpz_action(action)));
}
/* break to make sure 'a' stays pointed
* to used auth_zone, and keeps lock */
if(!respip_rewrite_reply(&qstate->qinfo,
qstate->client_info, qstate->return_msg->rep,
&new_rep, &actinfo, &alias_rrset, 0,
- qstate->region, qstate->env->auth_zones)) {
+ qstate->region, qstate->env->auth_zones,
+ &qstate->rpz_passthru)) {
goto servfail;
}
if(actinfo.action != respip_none) {
/* see if the target reply would be subject to a response-ip action. */
if(!respip_rewrite_reply(qinfo, cinfo, tgt_rep, &tmp_rep, &actinfo,
- &alias_rrset, 1, region, az))
+ &alias_rrset, 1, region, az, NULL))
return 0;
if(actinfo.action != respip_none) {
log_info("CNAME target of redirect response-ip action would "
respip, sizeof(respip));
if(respip_actinfo->rpz_log) {
txtlen += snprintf(txt+txtlen, sizeof(txt)-txtlen, "%s",
- "RPZ applied ");
+ "rpz: applied ");
if(respip_actinfo->rpz_cname_override)
actionstr = rpz_action_to_string(
RPZ_CNAME_OVERRIDE_ACTION);
* will be set (or intact) accordingly but the modified reply won't be built.
* @param az: auth zones containing RPZ information.
* @param region: allocator to build *new_repp.
+ * @param rpz_passthru: keeps track of query state can have passthru that
+ * stops further rpz processing. Or NULL for cached answer processing.
* @return 1 on success, 0 on error.
*/
int respip_rewrite_reply(const struct query_info* qinfo,
const struct reply_info *rep, struct reply_info** new_repp,
struct respip_action_info* actinfo,
struct ub_packed_rrset_key** alias_rrset,
- int search_only, struct regional* region, struct auth_zones* az);
+ int search_only, struct regional* region, struct auth_zones* az,
+ int* rpz_passthru);
/**
* Get the response-ip function block.
return NULL;
msg->rep->flags = (uint16_t)(BIT_QR | BIT_AA);
msg->rep->authoritative = 1;
+ msg->rep->reason_bogus = LDNS_EDE_NONE;
msg->rep->qdcount = 1;
/* rrsets is NULL, no rrsets yet */
return msg;
struct regional* region = NULL;
struct sldns_buffer* buf = NULL;
uint32_t soa_serial = 0;
+ char* unsupported_reason = NULL;
+ int only_unsupported = 1;
region = env->scratch;
regional_free_all(region);
buf = env->scratch_buffer;
&hashalgo, &hash, &hashlen)) {
/* malformed RR */
*reason = "ZONEMD rdata malformed";
+ only_unsupported = 0;
continue;
}
/* check for duplicates */
* is not allowed. */
*reason = "ZONEMD RRSet contains more than one RR "
"with the same scheme and hash algorithm";
+ only_unsupported = 0;
continue;
}
regional_free_all(region);
if(serial != soa_serial) {
*reason = "ZONEMD serial is wrong";
+ only_unsupported = 0;
continue;
}
+ *reason = NULL;
if(auth_zone_generate_zonemd_check(z, scheme, hashalgo,
hash, hashlen, region, buf, reason)) {
/* success */
+ if(*reason) {
+ if(!unsupported_reason)
+ unsupported_reason = *reason;
+ /* continue to check for valid ZONEMD */
+ if(verbosity >= VERB_ALGO) {
+ char zstr[255+1];
+ dname_str(z->name, zstr);
+ verbose(VERB_ALGO, "auth-zone %s ZONEMD %d %d is unsupported: %s", zstr, (int)scheme, (int)hashalgo, *reason);
+ }
+ *reason = NULL;
+ continue;
+ }
if(verbosity >= VERB_ALGO) {
char zstr[255+1];
dname_str(z->name, zstr);
- verbose(VERB_ALGO, "auth-zone %s ZONEMD hash is correct", zstr);
+ if(!*reason)
+ verbose(VERB_ALGO, "auth-zone %s ZONEMD hash is correct", zstr);
}
return 1;
}
+ only_unsupported = 0;
/* try next one */
}
+ /* have we seen no failures but only unsupported algo,
+ * and one unsupported algorithm, or more. */
+ if(only_unsupported && unsupported_reason) {
+ /* only unsupported algorithms, with valid serial, not
+ * malformed. Did not see supported algorithms, failed or
+ * successful ones. */
+ *reason = unsupported_reason;
+ return 1;
+ }
/* fail, we may have reason */
if(!*reason)
*reason = "no ZONEMD records found";
return 1;
}
-/** process $ORIGIN for http */
+/** process $ORIGIN for http, 0 nothing, 1 done, 2 error */
static int
http_parse_origin(sldns_buffer* buf, struct sldns_file_parse_state* pstate)
{
pstate->origin_len = sizeof(pstate->origin);
s = sldns_str2wire_dname_buf(sldns_strip_ws(line+8),
pstate->origin, &pstate->origin_len);
- if(s) pstate->origin_len = 0;
+ if(s) {
+ pstate->origin_len = 0;
+ return 2;
+ }
return 1;
}
return 0;
}
-/** process $TTL for http */
+/** process $TTL for http, 0 nothing, 1 done, 2 error */
static int
http_parse_ttl(sldns_buffer* buf, struct sldns_file_parse_state* pstate)
{
if(strncmp(line, "$TTL", 4) == 0 &&
isspace((unsigned char)line[4])) {
const char* end = NULL;
+ int overflow = 0;
pstate->default_ttl = sldns_str2period(
- sldns_strip_ws(line+5), &end);
+ sldns_strip_ws(line+5), &end, &overflow);
+ if(overflow) {
+ return 2;
+ }
return 1;
}
return 0;
chunkline_non_comment_RR(struct auth_chunk** chunk, size_t* chunk_pos,
sldns_buffer* buf, struct sldns_file_parse_state* pstate)
{
+ int ret;
while(chunkline_get_line_collated(chunk, chunk_pos, buf)) {
if(chunkline_is_comment_line_or_empty(buf)) {
/* a comment, go to next line */
continue;
}
- if(http_parse_origin(buf, pstate)) {
+ if((ret=http_parse_origin(buf, pstate))!=0) {
+ if(ret == 2)
+ return 0;
continue; /* $ORIGIN has been handled */
}
- if(http_parse_ttl(buf, pstate)) {
+ if((ret=http_parse_ttl(buf, pstate))!=0) {
+ if(ret == 2)
+ return 0;
continue; /* $TTL has been handled */
}
return 1;
struct sldns_file_parse_state pstate;
struct auth_chunk* chunk;
size_t chunk_pos;
+ int ret;
memset(&pstate, 0, sizeof(pstate));
pstate.default_ttl = 3600;
if(xfr->namelen < sizeof(pstate.origin)) {
continue;
}
/* parse line and add RR */
- if(http_parse_origin(scratch_buffer, &pstate)) {
+ if((ret=http_parse_origin(scratch_buffer, &pstate))!=0) {
+ if(ret == 2) {
+ verbose(VERB_ALGO, "error parsing ORIGIN on line [%s:%d] %s",
+ xfr->task_transfer->master->file,
+ pstate.lineno,
+ sldns_buffer_begin(scratch_buffer));
+ return 0;
+ }
continue; /* $ORIGIN has been handled */
}
- if(http_parse_ttl(scratch_buffer, &pstate)) {
+ if((ret=http_parse_ttl(scratch_buffer, &pstate))!=0) {
+ if(ret == 2) {
+ verbose(VERB_ALGO, "error parsing TTL on line [%s:%d] %s",
+ xfr->task_transfer->master->file,
+ pstate.lineno,
+ sldns_buffer_begin(scratch_buffer));
+ return 0;
+ }
continue; /* $TTL has been handled */
}
if(!http_parse_add_rr(xfr, z, scratch_buffer, &pstate)) {
* called straight away */
lock_basic_unlock(&xfr->lock);
if(!mesh_new_callback(env->mesh, &qinfo, qflags, &edns, buf, 0,
- &auth_xfer_transfer_lookup_callback, xfr)) {
+ &auth_xfer_transfer_lookup_callback, xfr, 0)) {
lock_basic_lock(&xfr->lock);
log_err("out of memory lookup up master %s", master->host);
return 0;
* called straight away */
lock_basic_unlock(&xfr->lock);
if(!mesh_new_callback(env->mesh, &qinfo, qflags, &edns, buf, 0,
- &auth_xfer_probe_lookup_callback, xfr)) {
+ &auth_xfer_probe_lookup_callback, xfr, 0)) {
lock_basic_lock(&xfr->lock);
log_err("out of memory lookup up master %s", master->host);
return 0;
{
uint8_t gen[512];
size_t genlen = 0;
+ *reason = NULL;
if(!zonemd_hashalgo_supported(hashalgo)) {
+ /* allow it */
*reason = "unsupported algorithm";
- return 0;
+ return 1;
}
if(!zonemd_scheme_supported(scheme)) {
+ /* allow it */
*reason = "unsupported scheme";
- return 0;
+ return 1;
}
if(hashlen < 12) {
/* the ZONEMD draft requires digests to fail if too small */
auth_zone_log(z->name, VERB_ALGO,
"zonemd: verify %s RRset with DNSKEY", typestr);
}
- sec = dnskeyset_verify_rrset(env, ve, &pk, dnskey, sigalg, why_bogus,
+ sec = dnskeyset_verify_rrset(env, ve, &pk, dnskey, sigalg, why_bogus, NULL,
LDNS_SECTION_ANSWER, NULL);
if(sec == sec_status_secure) {
return 1;
}
/* success! log the success */
- auth_zone_log(z->name, VERB_ALGO, "ZONEMD verification successful");
+ if(reason)
+ auth_zone_log(z->name, VERB_ALGO, "ZONEMD %s", reason);
+ else auth_zone_log(z->name, VERB_ALGO, "ZONEMD verification successful");
if(result) {
- *result = strdup("ZONEMD verification successful");
+ if(reason)
+ *result = strdup(reason);
+ else *result = strdup("ZONEMD verification successful");
if(!*result) log_err("out of memory");
}
}
auth_zone_log(z->name, VERB_QUERY,
"zonemd: verify DNSKEY RRset with trust anchor");
sec = val_verify_DNSKEY_with_TA(env, ve, keystorage, anchor->ds_rrset,
- anchor->dnskey_rrset, NULL, why_bogus, NULL);
+ anchor->dnskey_rrset, NULL, why_bogus, NULL, NULL);
regional_free_all(env->scratch);
if(sec == sec_status_secure) {
/* success */
keystorage->rk.type = htons(LDNS_RR_TYPE_DNSKEY);
keystorage->rk.rrset_class = htons(z->dclass);
auth_zone_log(z->name, VERB_QUERY, "zonemd: verify zone DNSKEY with DS");
+ // @TODO add EDE here? we currently just pass NULL
sec = val_verify_DNSKEY_with_DS(env, ve, keystorage, ds, sigalg,
- why_bogus, NULL);
+ why_bogus, NULL, NULL);
regional_free_all(env->scratch);
if(sec == sec_status_secure) {
/* success */
/* the callback can be called straight away */
lock_rw_unlock(&z->lock);
if(!mesh_new_callback(env->mesh, &qinfo, qflags, &edns, buf, 0,
- &auth_zonemd_dnskey_lookup_callback, z)) {
+ &auth_zonemd_dnskey_lookup_callback, z, 0)) {
lock_rw_wrlock(&z->lock);
log_err("out of memory lookup of %s for zonemd",
(fetch_ds?"DS":"DNSKEY"));
* @param region: temp region for allocs during canonicalisation.
* @param buf: temp buffer during canonicalisation.
* @param reason: string returned with failure reason.
+ * If the hash cannot be checked, but it is allowed, for unknown
+ * algorithms, the routine returns success, and the reason is nonNULL,
+ * with the allowance reason.
* @return false on failure.
*/
int auth_zone_generate_zonemd_check(struct auth_zone* z, int scheme,
return NULL; /* integer overflow protection */
msg->rep->flags = BIT_QR; /* with QR, no AA */
msg->rep->qdcount = 1;
+ msg->rep->reason_bogus = LDNS_EDE_NONE;
msg->rep->rrsets = (struct ub_packed_rrset_key**)
regional_alloc(region,
capacity*sizeof(struct ub_packed_rrset_key*));
sizeof(struct reply_info) - sizeof(struct rrset_ref));
if(!msg->rep)
return NULL;
+ msg->rep->reason_bogus = LDNS_EDE_NONE;
if(num > RR_COUNT_MAX)
return NULL; /* integer overflow protection */
msg->rep->rrsets = (struct ub_packed_rrset_key**)
msg->rep->ar_numrrsets = r->ar_numrrsets;
msg->rep->rrset_count = r->rrset_count;
msg->rep->authoritative = r->authoritative;
+ msg->rep->reason_bogus = r->reason_bogus;
if(!rrset_array_lock(r->ref, r->rrset_count, now_control)) {
return NULL;
}
msg->rep->ns_numrrsets = 0;
msg->rep->ar_numrrsets = 0;
msg->rep->rrset_count = 1;
+ msg->rep->reason_bogus = LDNS_EDE_NONE;
msg->rep->rrsets[0] = packed_rrset_copy_region(rrset, region, now);
if(!msg->rep->rrsets[0]) /* copy CNAME */
return NULL;
msg->rep->ns_numrrsets = 0;
msg->rep->ar_numrrsets = 0;
msg->rep->rrset_count = 1;
+ msg->rep->reason_bogus = LDNS_EDE_NONE;
msg->rep->rrsets[0] = packed_rrset_copy_region(rrset, region, now);
if(!msg->rep->rrsets[0]) /* copy DNAME */
return NULL;
#ifdef USE_TCP_FASTOPEN
#include <netinet/tcp.h>
#endif
+#include <ctype.h>
#include "services/listen_dnsport.h"
#include "services/outside_network.h"
#include "util/netevent.h"
* @param do_auto: use automatic interface detection.
* If enabled, then ifname must be the wildcard name.
* @param do_udp: if udp should be used.
- * @param do_tcp: if udp should be used.
+ * @param do_tcp: if tcp should be used.
* @param hints: for getaddrinfo. family and flags have to be set by caller.
* @param port: Port number to use (as string).
* @param list: list of open ports, appended to, changed to point to list head.
}
/* create ip4 and ip6 ports so that return addresses are nice. */
if(do_auto || num_ifs == 0) {
+ if(do_auto && cfg->if_automatic_ports &&
+ cfg->if_automatic_ports[0]!=0) {
+ char* now = cfg->if_automatic_ports;
+ while(now && *now) {
+ char* after;
+ int extraport;
+ while(isspace((unsigned char)*now))
+ now++;
+ if(!*now)
+ break;
+ after = now;
+ extraport = (int)strtol(now, &after, 10);
+ if(extraport < 0 || extraport > 65535) {
+ log_err("interface-automatic-ports port number out of range, at position %d of '%s'", (int)(now-cfg->if_automatic_ports)+1, cfg->if_automatic_ports);
+ listening_ports_free(list);
+ return NULL;
+ }
+ if(extraport == 0 && now == after) {
+ log_err("interface-automatic-ports could not be parsed, at position %d of '%s'", (int)(now-cfg->if_automatic_ports)+1, cfg->if_automatic_ports);
+ listening_ports_free(list);
+ return NULL;
+ }
+ now = after;
+ snprintf(portbuf, sizeof(portbuf), "%d", extraport);
+ if(do_ip6) {
+ hints.ai_family = AF_INET6;
+ if(!ports_create_if("::0",
+ do_auto, cfg->do_udp, do_tcp,
+ &hints, portbuf, &list,
+ cfg->so_rcvbuf, cfg->so_sndbuf,
+ cfg->ssl_port, cfg->tls_additional_port,
+ cfg->https_port, reuseport, cfg->ip_transparent,
+ cfg->tcp_mss, cfg->ip_freebind,
+ cfg->http_nodelay, cfg->use_systemd,
+ cfg->dnscrypt_port, cfg->ip_dscp)) {
+ listening_ports_free(list);
+ return NULL;
+ }
+ }
+ if(do_ip4) {
+ hints.ai_family = AF_INET;
+ if(!ports_create_if("0.0.0.0",
+ do_auto, cfg->do_udp, do_tcp,
+ &hints, portbuf, &list,
+ cfg->so_rcvbuf, cfg->so_sndbuf,
+ cfg->ssl_port, cfg->tls_additional_port,
+ cfg->https_port, reuseport, cfg->ip_transparent,
+ cfg->tcp_mss, cfg->ip_freebind,
+ cfg->http_nodelay, cfg->use_systemd,
+ cfg->dnscrypt_port, cfg->ip_dscp)) {
+ listening_ports_free(list);
+ return NULL;
+ }
+ }
+ }
+ return list;
+ }
if(do_ip6) {
hints.ai_family = AF_INET6;
if(!ports_create_if(do_auto?"::0":"::1",
static void
local_error_encode(struct query_info* qinfo, struct module_env* env,
struct edns_data* edns, struct comm_reply* repinfo, sldns_buffer* buf,
- struct regional* temp, int rcode, int r)
+ struct regional* temp, int rcode, int r, int ede_code,
+ const char* ede_txt)
{
edns->edns_version = EDNS_ADVERTISED_VERSION;
edns->udp_size = EDNS_ADVERTISED_SIZE;
if(!inplace_cb_reply_local_call(env, qinfo, NULL, NULL,
rcode, edns, repinfo, temp, env->now_tv))
edns->opt_list_inplace_cb_out = NULL;
+
+ if(ede_code != LDNS_EDE_NONE && env->cfg->ede) {
+ edns_opt_list_append_ede(&edns->opt_list_out, temp,
+ ede_code, ede_txt);
+ }
+
error_encode(buf, r, qinfo, *(uint16_t*)sldns_buffer_begin(buf),
sldns_buffer_read_u16_at(buf, 2), edns);
}
qinfo->local_alias = NULL;
local_error_encode(qinfo, env, edns, repinfo,
buf, temp, LDNS_RCODE_YXDOMAIN,
- (LDNS_RCODE_YXDOMAIN|BIT_AA));
+ (LDNS_RCODE_YXDOMAIN|BIT_AA),
+ LDNS_EDE_OTHER,
+ "DNAME expansion became too large");
return 1;
}
memset(&qinfo->local_alias->rrset->entry, 0,
} else if(lz_type == local_zone_refuse
|| lz_type == local_zone_always_refuse) {
local_error_encode(qinfo, env, edns, repinfo, buf, temp,
- LDNS_RCODE_REFUSED, (LDNS_RCODE_REFUSED|BIT_AA));
+ LDNS_RCODE_REFUSED, (LDNS_RCODE_REFUSED|BIT_AA),
+ LDNS_EDE_NONE, NULL);
return 1;
} else if(lz_type == local_zone_static ||
lz_type == local_zone_redirect ||
if(z != NULL && z->soa && z->soa_negative)
return local_encode(qinfo, env, edns, repinfo, buf, temp,
z->soa_negative, 0, rcode);
- local_error_encode(qinfo, env, edns, repinfo, buf, temp, rcode,
- (rcode|BIT_AA));
+ local_error_encode(qinfo, env, edns, repinfo, buf, temp,
+ rcode, (rcode|BIT_AA), LDNS_EDE_NONE, NULL);
return 1;
} else if(lz_type == local_zone_typetransparent
|| lz_type == local_zone_always_transparent) {
return local_encode(qinfo, env, edns, repinfo, buf, temp,
&lrr, 1, LDNS_RCODE_NOERROR);
} else {
+ /* NODATA: No EDE needed */
local_error_encode(qinfo, env, edns, repinfo, buf,
temp, LDNS_RCODE_NOERROR,
- (LDNS_RCODE_NOERROR|BIT_AA));
+ (LDNS_RCODE_NOERROR|BIT_AA), -1, NULL);
}
return 1;
}
if(z != NULL && z->soa && z->soa_negative)
return local_encode(qinfo, env, edns, repinfo, buf, temp,
z->soa_negative, 0, rcode);
+ /* NODATA: No EDE needed */
local_error_encode(qinfo, env, edns, repinfo, buf, temp, rcode,
- (rcode|BIT_AA));
+ (rcode|BIT_AA), LDNS_EDE_NONE, NULL);
return 1;
}
#include "respip/respip.h"
#include "services/listen_dnsport.h"
+#ifdef CLIENT_SUBNET
+#include "edns-subnet/subnetmod.h"
+#include "edns-subnet/edns-subnet.h"
+#endif
+
/** subtract timers and the values do not overflow or become negative */
static void
timeval_subtract(struct timeval* d, const struct timeval* end, const struct timeval* start)
void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
struct respip_client_info* cinfo, uint16_t qflags,
- struct edns_data* edns, struct comm_reply* rep, uint16_t qid)
+ struct edns_data* edns, struct comm_reply* rep, uint16_t qid,
+ int rpz_passthru)
{
struct mesh_state* s = NULL;
int unique = unique_mesh_state(edns->opt_list_in, mesh->env);
}
if(unique)
mesh_state_make_unique(s);
+ s->s.rpz_passthru = rpz_passthru;
/* copy the edns options we got from the front */
if(edns->opt_list_in) {
s->s.edns_opts_front_in = edns_opt_copy_region(edns->opt_list_in,
int
mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
uint16_t qflags, struct edns_data* edns, sldns_buffer* buf,
- uint16_t qid, mesh_cb_func_type cb, void* cb_arg)
+ uint16_t qid, mesh_cb_func_type cb, void* cb_arg, int rpz_passthru)
{
struct mesh_state* s = NULL;
int unique = unique_mesh_state(edns->opt_list_in, mesh->env);
}
if(unique)
mesh_state_make_unique(s);
+ s->s.rpz_passthru = rpz_passthru;
if(edns->opt_list_in) {
s->s.edns_opts_front_in = edns_opt_copy_region(edns->opt_list_in,
s->s.region);
* 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 query_info* qinfo, uint16_t qflags, time_t leeway, int run,
+ int rpz_passthru)
{
struct mesh_state* s = mesh_area_find(mesh, NULL, qinfo,
qflags&(BIT_RD|BIT_CD), 0, 0);
/* move to either the forever or the jostle_list */
if(mesh->num_forever_states < mesh->max_forever_states) {
mesh->num_forever_states ++;
- mesh_list_insert(s, &mesh->forever_first,
+ mesh_list_insert(s, &mesh->forever_first,
&mesh->forever_last);
s->list_select = mesh_forever_list;
} else {
- mesh_list_insert(s, &mesh->jostle_first,
+ mesh_list_insert(s, &mesh->jostle_first,
+ &mesh->jostle_last);
+ s->list_select = mesh_jostle_list;
+ }
+ }
+ s->s.rpz_passthru = rpz_passthru;
+
+ 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);
+}
+
+#ifdef CLIENT_SUBNET
+/* Same logic as mesh_schedule_prefetch but tailored to the subnet module logic
+ * like passing along the comm_reply info. This will be faked into an EDNS
+ * option for processing by the subnet module if the client has not already
+ * attached its own ECS data. */
+static void mesh_schedule_prefetch_subnet(struct mesh_area* mesh,
+ struct query_info* qinfo, uint16_t qflags, time_t leeway, int run,
+ int rpz_passthru, struct comm_reply* rep, struct edns_option* edns_list)
+{
+ struct mesh_state* s = NULL;
+ struct edns_option* opt = NULL;
+#ifdef UNBOUND_DEBUG
+ struct rbnode_type* n;
+#endif
+ if(!mesh_make_new_space(mesh, NULL)) {
+ verbose(VERB_ALGO, "Too many queries. dropped prefetch.");
+ mesh->stats_dropped ++;
+ return;
+ }
+
+ s = mesh_state_create(mesh->env, qinfo, NULL,
+ qflags&(BIT_RD|BIT_CD), 0, 0);
+ if(!s) {
+ log_err("prefetch_subnet mesh_state_create: out of memory");
+ return;
+ }
+ mesh_state_make_unique(s);
+
+ opt = edns_opt_list_find(edns_list, mesh->env->cfg->client_subnet_opcode);
+ if(opt) {
+ /* Use the client's ECS data */
+ if(!edns_opt_list_append(&s->s.edns_opts_front_in, opt->opt_code,
+ opt->opt_len, opt->opt_data, s->s.region)) {
+ log_err("prefetch_subnet edns_opt_list_append: out of memory");
+ return;
+ }
+ } else {
+ /* Fake the ECS data from the client's IP */
+ struct ecs_data ecs;
+ memset(&ecs, 0, sizeof(ecs));
+ subnet_option_from_ss(&rep->addr, &ecs, mesh->env->cfg);
+ if(ecs.subnet_validdata == 0) {
+ log_err("prefetch_subnet subnet_option_from_ss: invalid data");
+ return;
+ }
+ subnet_ecs_opt_list_append(&ecs, &s->s.edns_opts_front_in, &s->s);
+ if(!s->s.edns_opts_front_in) {
+ log_err("prefetch_subnet subnet_ecs_opt_list_append: out of memory");
+ return;
+ }
+ }
+#ifdef UNBOUND_DEBUG
+ n =
+#else
+ (void)
+#endif
+ rbtree_insert(&mesh->all, &s->node);
+ log_assert(n != NULL);
+ /* set detached (it is now) */
+ mesh->num_detached_states++;
+ /* make it ignore the cache */
+ sock_list_insert(&s->s.blacklist, NULL, 0, s->s.region);
+ s->s.prefetch_leeway = leeway;
+
+ if(s->list_select == mesh_no_list) {
+ /* move to either the forever or the jostle_list */
+ if(mesh->num_forever_states < mesh->max_forever_states) {
+ mesh->num_forever_states ++;
+ mesh_list_insert(s, &mesh->forever_first,
+ &mesh->forever_last);
+ s->list_select = mesh_forever_list;
+ } else {
+ mesh_list_insert(s, &mesh->jostle_first,
&mesh->jostle_last);
s->list_select = mesh_jostle_list;
}
}
+ s->s.rpz_passthru = rpz_passthru;
if(!run) {
#ifdef UNBOUND_DEBUG
mesh_run(mesh, s, module_event_new, NULL);
}
+#endif /* CLIENT_SUBNET */
void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo,
- uint16_t qflags, time_t leeway)
+ uint16_t qflags, time_t leeway, int rpz_passthru,
+ struct comm_reply* rep, struct edns_option* opt_list)
{
- mesh_schedule_prefetch(mesh, qinfo, qflags, leeway, 1);
+ (void)opt_list;
+ (void)rep;
+#ifdef CLIENT_SUBNET
+ if(rep)
+ mesh_schedule_prefetch_subnet(mesh, qinfo, qflags, leeway, 1,
+ rpz_passthru, rep, opt_list);
+ else
+#endif
+ mesh_schedule_prefetch(mesh, qinfo, qflags, leeway, 1,
+ rpz_passthru);
}
void mesh_report_reply(struct mesh_area* mesh, struct outbound_entry* e,
(rep->security <= sec_status_bogus ||
rep->security == sec_status_secure_sentinel_fail)) {
rcode = LDNS_RCODE_SERVFAIL;
- if(m->s.env->cfg->stat_extended)
+ if(m->s.env->cfg->stat_extended)
m->s.env->mesh->ans_bogus++;
}
if(rep && rep->security == sec_status_secure)
&r->edns, &r->query_reply, m->s.region, &r->start_time))
r->edns.opt_list_inplace_cb_out = NULL;
}
+ /* Send along EDE BOGUS EDNS0 option when answer is bogus */
+ if(m->s.env->cfg->ede && rcode == LDNS_RCODE_SERVFAIL &&
+ m->s.env->need_to_validate && (!(r->qflags&BIT_CD) ||
+ m->s.env->cfg->ignore_cd) && rep &&
+ (rep->security <= sec_status_bogus ||
+ rep->security == sec_status_secure_sentinel_fail)) {
+ char *reason = m->s.env->cfg->val_log_level >= 2
+ ? errinf_to_str_bogus(&m->s) : NULL;
+
+ /* During validation the EDE code can be received via two
+ * code paths. One code path fills the reply_info EDE, and
+ * the other fills it in the errinf_strlist. These paths
+ * intersect at some points, but where is opaque due to
+ * the complexity of the validator. At the time of writing
+ * we make the choice to prefer the EDE from errinf_strlist
+ * but a compelling reason to do otherwise is just as valid
+ */
+ sldns_ede_code reason_bogus = errinf_to_reason_bogus(&m->s);
+ if ((reason_bogus == LDNS_EDE_DNSSEC_BOGUS &&
+ rep->reason_bogus != LDNS_EDE_NONE) ||
+ reason_bogus == LDNS_EDE_NONE) {
+ reason_bogus = rep->reason_bogus;
+ }
+
+ if(reason_bogus != LDNS_EDE_NONE) {
+ edns_opt_list_append_ede(&r->edns.opt_list_out,
+ m->s.region, reason_bogus, reason);
+ }
+ free(reason);
+ }
error_encode(r_buffer, rcode, &m->s.qinfo, r->qid,
r->qflags, &r->edns);
m->reply_list = NULL;
if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s,
rep, LDNS_RCODE_SERVFAIL, &r->edns, &r->query_reply, m->s.region, &r->start_time))
r->edns.opt_list_inplace_cb_out = NULL;
+ /* internal server error (probably malloc failure) so no
+ * EDE (RFC8914) needed */
error_encode(r_buffer, LDNS_RCODE_SERVFAIL,
&m->s.qinfo, r->qid, r->qflags, &r->edns);
}
struct comm_reply* rep, uint16_t qid, uint16_t qflags,
const struct query_info* qinfo)
{
- struct mesh_reply* r = regional_alloc(s->s.region,
+ struct mesh_reply* r = regional_alloc(s->s.region,
sizeof(struct mesh_reply));
if(!r)
return 0;
if(mstate->s.curmod == 0) {
struct query_info* qinfo = NULL;
uint16_t qflags;
+ int rpz_p = 0;
mesh_query_done(mstate);
mesh_walk_supers(mesh, mstate);
* 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)
+ if(mstate->s.need_refetch) {
mesh_copy_qinfo(mstate, &qinfo, &qflags);
+ rpz_p = mstate->s.rpz_passthru;
+ }
mesh_state_delete(&mstate->s);
if(qinfo) {
mesh_schedule_prefetch(mesh, qinfo, qflags,
- 0, 1);
+ 0, 1, rpz_p);
}
return 0;
}
return 1;
if(!respip_rewrite_reply(qinfo, cinfo, rep, encode_repp, actinfo,
- alias_rrset, 0, qstate->region, az))
+ alias_rrset, 0, qstate->region, az, NULL))
return 0;
/* xxx_deny actions mean dropping the reply, unless the original reply
}
}
+ /* Add EDE Stale Answer (RCF8914). Ignore global ede as this is
+ * warning instead of an error */
+ if (r->edns.edns_present && qstate->env->cfg->ede_serve_expired &&
+ qstate->env->cfg->ede) {
+ edns_opt_list_append_ede(&r->edns.opt_list_out,
+ mstate->s.region, LDNS_EDE_STALE_ANSWER, NULL);
+ }
+
r_buffer = r->query_reply.c->buffer;
if(r->query_reply.c->tcp_req_info)
r_buffer = r->query_reply.c->tcp_req_info->spool_buffer;
* @param edns: edns data from client query.
* @param rep: where to reply to.
* @param qid: query id to reply with.
+ * @param rpz_passthru: if true, the rpz passthru was previously found and
+ * further rpz processing is stopped.
*/
void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
struct respip_client_info* cinfo, uint16_t qflags,
- struct edns_data* edns, struct comm_reply* rep, uint16_t qid);
+ struct edns_data* edns, struct comm_reply* rep, uint16_t qid,
+ int rpz_passthru);
/**
* New query with callback. Create new query state if needed, and
* @param qid: query id to reply with.
* @param cb: callback function.
* @param cb_arg: callback user arg.
+ * @param rpz_passthru: if true, the rpz passthru was previously found and
+ * further rpz processing is stopped.
* @return 0 on error.
*/
int mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
uint16_t qflags, struct edns_data* edns, struct sldns_buffer* buf,
- uint16_t qid, mesh_cb_func_type cb, void* cb_arg);
+ uint16_t qid, mesh_cb_func_type cb, void* cb_arg, int rpz_passthru);
/**
* New prefetch message. Create new query state if needed.
* @param qinfo: query from client.
* @param qflags: flags from client query.
* @param leeway: TTL leeway what to expire earlier for this update.
+ * @param rpz_passthru: if true, the rpz passthru was previously found and
+ * further rpz processing is stopped.
+ * @param rep: comm_reply for the client; to be used when subnet is enabled.
+ * @param opt_list: edns opt_list from the client; to be used when subnet is
+ * enabled.
*/
void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo,
- uint16_t qflags, time_t leeway);
+ uint16_t qflags, time_t leeway, int rpz_passthru,
+ struct comm_reply* rep, struct edns_option* opt_list);
/**
* Handle new event from the wire. A serviced query has returned.
# endif
# ifdef ENETDOWN
case ENETDOWN:
+# endif
+# ifdef EADDRNOTAVAIL
+ case EADDRNOTAVAIL:
# endif
case EPERM:
case EACCES:
node = rbtree_first(&reuse->tree_by_id);
log_assert(node && node != RBTREE_NULL); /* tree not empty */
/* see if select is before first node */
- if(select < tree_by_id_get_id(node))
+ if(select < (unsigned)tree_by_id_get_id(node))
return select;
count += tree_by_id_get_id(node);
/* perhaps select is between nodes */
}
uint32_t
-sldns_str2period(const char *nptr, const char **endptr)
+sldns_str2period(const char *nptr, const char **endptr, int* overflow)
{
int sign = 0;
uint32_t i = 0;
uint32_t seconds = 0;
+ const uint32_t maxint = 0xffffffff;
+ *overflow = 0;
for(*endptr = nptr; **endptr; (*endptr)++) {
switch (**endptr) {
break;
case 's':
case 'S':
+ if(seconds > maxint-i) {
+ *overflow = 1;
+ return 0;
+ }
seconds += i;
i = 0;
break;
case 'm':
case 'M':
+ if(i > maxint/60 || seconds > maxint-(i*60)) {
+ *overflow = 1;
+ return 0;
+ }
seconds += i * 60;
i = 0;
break;
case 'h':
case 'H':
+ if(i > maxint/(60*60) || seconds > maxint-(i*60*60)) {
+ *overflow = 1;
+ return 0;
+ }
seconds += i * 60 * 60;
i = 0;
break;
case 'd':
case 'D':
+ if(i > maxint/(60*60*24) || seconds > maxint-(i*60*60*24)) {
+ *overflow = 1;
+ return 0;
+ }
seconds += i * 60 * 60 * 24;
i = 0;
break;
case 'w':
case 'W':
+ if(i > maxint/(60*60*24*7) || seconds > maxint-(i*60*60*24*7)) {
+ *overflow = 1;
+ return 0;
+ }
seconds += i * 60 * 60 * 24 * 7;
i = 0;
break;
case '7':
case '8':
case '9':
+ if(i > maxint/10 || i*10 > maxint - (**endptr - '0')) {
+ *overflow = 1;
+ return 0;
+ }
i *= 10;
i += (**endptr - '0');
break;
default:
+ if(seconds > maxint-i) {
+ *overflow = 1;
+ return 0;
+ }
seconds += i;
/* disregard signedness */
return seconds;
}
}
+ if(seconds > maxint-i) {
+ *overflow = 1;
+ return 0;
+ }
seconds += i;
/* disregard signedness */
return seconds;
* converts a ttl value (like 5d2h) to a long.
* \param[in] nptr the start of the string
* \param[out] endptr points to the last char in case of error
+ * \param[out] overflow returns if the string causes integer overflow error,
+ * the number is too big, string of digits too long.
* \return the convert duration value
*/
-uint32_t sldns_str2period(const char *nptr, const char **endptr);
+uint32_t sldns_str2period(const char *nptr, const char **endptr, int* overflow);
/**
* Returns the int value of the given (hex) digit
LDNS_EDNS_CLIENT_SUBNET = 8, /* RFC7871 */
LDNS_EDNS_KEEPALIVE = 11, /* draft-ietf-dnsop-edns-tcp-keepalive*/
LDNS_EDNS_PADDING = 12, /* RFC7830 */
+ LDNS_EDNS_EDE = 15, /* RFC8914 */
LDNS_EDNS_CLIENT_TAG = 16 /* draft-bellis-dnsop-edns-tags-01 */
};
typedef enum sldns_enum_edns_option sldns_edns_option;
+enum sldns_enum_ede_code
+{
+ LDNS_EDE_NONE = -1, /* EDE undefined for internal use */
+ LDNS_EDE_OTHER = 0,
+ LDNS_EDE_UNSUPPORTED_DNSKEY_ALG = 1,
+ LDNS_EDE_UNSUPPORTED_DS_DIGEST = 2,
+ LDNS_EDE_STALE_ANSWER = 3,
+ LDNS_EDE_FORGED_ANSWER = 4,
+ LDNS_EDE_DNSSEC_INDETERMINATE = 5,
+ LDNS_EDE_DNSSEC_BOGUS = 6,
+ LDNS_EDE_SIGNATURE_EXPIRED = 7,
+ LDNS_EDE_SIGNATURE_NOT_YET_VALID = 8,
+ LDNS_EDE_DNSKEY_MISSING = 9,
+ LDNS_EDE_RRSIGS_MISSING = 10,
+ LDNS_EDE_NO_ZONE_KEY_BIT_SET = 11,
+ LDNS_EDE_NSEC_MISSING = 12,
+ LDNS_EDE_CACHED_ERROR = 13,
+ LDNS_EDE_NOT_READY = 14,
+ LDNS_EDE_BLOCKED = 15,
+ LDNS_EDE_CENSORED = 16,
+ LDNS_EDE_FILTERED = 17,
+ LDNS_EDE_PROHIBITED = 18,
+ LDNS_EDE_STALE_NXDOMAIN_ANSWER = 19,
+ LDNS_EDE_NOT_AUTHORITATIVE = 20,
+ LDNS_EDE_NOT_SUPPORTED = 21,
+ LDNS_EDE_NO_REACHABLE_AUTHORITY = 22,
+ LDNS_EDE_NETWORK_ERROR = 23,
+ LDNS_EDE_INVALID_DATA = 24,
+};
+typedef enum sldns_enum_ede_code sldns_ede_code;
+
#define LDNS_EDNS_MASK_DO_BIT 0x8000
/** TSIG and TKEY extended rcodes (16bit), 0-15 are the normal rcodes. */
int* not_there, uint32_t* ttl, uint32_t default_ttl)
{
const char* endptr;
+ int overflow;
if(sldns_bget_token(strbuf, token, "\t\n ", token_len) == -1) {
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_TTL,
sldns_buffer_position(strbuf));
}
- *ttl = (uint32_t) sldns_str2period(token, &endptr);
+ *ttl = (uint32_t) sldns_str2period(token, &endptr, &overflow);
+ if(overflow) {
+ return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_INTEGER_OVERFLOW,
+ sldns_buffer_position(strbuf));
+ }
if (strlen(token) > 0 && !isdigit((unsigned char)token[0])) {
*not_there = 1;
/* skip spaces */
while(sldns_buffer_remaining(strbuf) > 0 &&
- *(sldns_buffer_current(strbuf)) == ' ') {
+ (*(sldns_buffer_current(strbuf)) == ' ' ||
+ *(sldns_buffer_current(strbuf)) == '\t')) {
sldns_buffer_skip(strbuf, 1);
}
/* add space */
/* when addlen < 2, the token buffer is full considering the NULL byte
* from strlen and will lead to buffer overflow with the second
- * assignement below. */
+ * assignment below. */
if(addlen < 2) return 0;
token[*token_strlen] = ' ';
token[++(*token_strlen)] = 0;
,sldns_str2wire_svcparam_key_cmp);
- /* The code below revolves around sematic errors in the SVCParam set.
+ /* The code below revolves around semantic errors in the SVCParam set.
* So long as we do not distinguish between running Unbound as a primary
* or as a secondary, we default to secondary behavior and we ignore the
- * sematic errors. */
+ * semantic errors. */
#ifdef SVCB_SEMANTIC_ERRORS
{
/* unknown RR data */
if(token_strlen>=2 && strncmp(token, "\\#", 2) == 0 &&
- !quoted && (token_strlen == 2 || token[2]==' ')) {
+ !quoted && (token_strlen == 2 || token[2]==' ' ||
+ token[2]=='\t')) {
was_unknown_rr_format = 1;
if((status=rrinternal_parse_unknown(strbuf, token,
token_len, rr, rr_len, &rr_cur_len,
return s;
} else if(strncmp(line, "$TTL", 4) == 0 && isspace((unsigned char)line[4])) {
const char* end = NULL;
+ int overflow = 0;
strlcpy((char*)rr, line, *len);
*len = 0;
*dname_len = 0;
if(!parse_state) return LDNS_WIREPARSE_ERR_OK;
parse_state->default_ttl = sldns_str2period(
- sldns_strip_ws(line+5), &end);
+ sldns_strip_ws(line+5), &end, &overflow);
+ if(overflow)
+ return LDNS_WIREPARSE_ERR_SYNTAX_INTEGER_OVERFLOW;
} else if (strncmp(line, "$INCLUDE", 8) == 0) {
strlcpy((char*)rr, line, *len);
*len = 0;
if (!strncmp(key, "mandatory", sizeof("mandatory")-1))
return SVCB_KEY_MANDATORY;
if (!strncmp(key, "echconfig", sizeof("echconfig")-1))
- return SVCB_KEY_ECH; /* allow "echconfig as well as "ech" */
+ return SVCB_KEY_ECH; /* allow "echconfig" as well as "ech" */
break;
case sizeof("alpn")-1:
*/
qsort((void *)(rd + 4), count, sizeof(uint16_t), sldns_network_uint16_cmp);
- /* The code below revolves around sematic errors in the SVCParam set.
+ /* The code below revolves around semantic errors in the SVCParam set.
* So long as we do not distinguish between running Unbound as a primary
* or as a secondary, we default to secondary behavior and we ignore the
* semantic errors. */
if (*val_in == '"') {
val_in++;
while (*val_in != '"'
- && (unsigned)(val_out - unescaped_val + 1) < sizeof(unescaped_val)
+ && (size_t)(val_out - unescaped_val + 1) < sizeof(unescaped_val)
&& sldns_parse_char( (uint8_t*) val_out, &val_in)) {
val_out++;
}
} else {
- while ((unsigned)(val_out - unescaped_val + 1) < sizeof(unescaped_val)
+ while ((size_t)(val_out - unescaped_val + 1) < sizeof(unescaped_val)
&& sldns_parse_char( (uint8_t*) val_out, &val_in)) {
val_out++;
}
int sldns_str2wire_period_buf(const char* str, uint8_t* rd, size_t* len)
{
const char* end;
- uint32_t p = sldns_str2period(str, &end);
+ int overflow;
+ uint32_t p = sldns_str2period(str, &end, &overflow);
if(*end != 0)
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_PERIOD, end-str);
+ if(overflow)
+ return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_INTEGER_OVERFLOW,
+ end-str);
if(*len < 4)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
sldns_write_uint32(rd, p);
{ 8, "edns-client-subnet" },
{ 11, "edns-tcp-keepalive"},
{ 12, "Padding" },
+ { 15, "EDE"},
{ 0, NULL}
};
sldns_lookup_table* sldns_edns_options = sldns_edns_options_data;
}
}
+/** check interface-automatic-ports */
+static void
+ifautomaticportschecks(char* ifautomaticports)
+{
+ char* now = ifautomaticports;
+ while(now && *now) {
+ char* after;
+ int extraport;
+ while(isspace((unsigned char)*now))
+ now++;
+ if(!*now)
+ break;
+ after = now;
+ extraport = (int)strtol(now, &after, 10);
+ if(extraport < 0 || extraport > 65535)
+ fatal_exit("interface-automatic-ports: port out of range at position %d in '%s'", (int)(now-ifautomaticports)+1, ifautomaticports);
+ if(extraport == 0 && now == after)
+ fatal_exit("interface-automatic-ports: parse error at position %d in '%s'", (int)(now-ifautomaticports)+1, ifautomaticports);
+ now = after;
+ }
+}
+
/** check acl ips */
static void
aclchecks(struct config_file* cfg)
warn_hosts("stub-host", cfg->stubs);
warn_hosts("forward-host", cfg->forwards);
interfacechecks(cfg);
+ ifautomaticportschecks(cfg->if_automatic_ports);
aclchecks(cfg);
tcpconnlimitchecks(cfg);
printf(" ratelimit_list [+a] list ratelimited domains\n");
printf(" ip_ratelimit_list [+a] list ratelimited ip addresses\n");
printf(" +a list all, also not ratelimited\n");
- printf(" list_auth_zones list auth zones\n");
- printf(" auth_zone_reload zone reload auth zone from zonefile\n");
- printf(" auth_zone_transfer zone transfer auth zone from master\n");
+ printf(" list_auth_zones list auth zones (includes RPZ zones)\n");
+ printf(" auth_zone_reload zone reload auth zone (or RPZ zone) from zonefile\n");
+ printf(" auth_zone_transfer zone transfer auth zone (or RPZ zone) from master\n");
printf(" view_list_local_zones view list local-zones in view\n");
printf(" view_list_local_data view list local-data RRs in view\n");
printf(" view_local_zone view name type add local-zone in view\n");
#endif /* HAVE_SHMGET */
/** print statistics from shm memory segment */
-static void print_stats_shm(const char* cfgfile)
+static void print_stats_shm(const char* cfgfile, int quiet)
{
#ifdef HAVE_SHMGET
struct config_file* cfg;
fatal_exit("shmat(%d): %s", id_arr, strerror(errno));
}
- /* print the stats */
- do_stats_shm(cfg, stats, shm_stat);
+
+ if(!quiet) {
+ /* print the stats */
+ do_stats_shm(cfg, stats, shm_stat);
+ }
/* shutdown */
shmdt(shm_stat);
{
unsigned long err;
err = ERR_peek_error();
- if (ERR_GET_LIB(err) == ERR_LIB_SYS) {
+ if(ERR_GET_LIB(err) == ERR_LIB_SYS) {
fprintf(stderr, "error: %s\n%s: %s\n",
s, path, ERR_reason_error_string(err));
exit(1);
#endif
if(!SSL_CTX_use_certificate_chain_file(ctx,c_cert))
ssl_path_err("Error setting up SSL_CTX client cert", c_cert);
- if (!SSL_CTX_use_PrivateKey_file(ctx,c_key,SSL_FILETYPE_PEM))
+ if(!SSL_CTX_use_PrivateKey_file(ctx,c_key,SSL_FILETYPE_PEM))
ssl_path_err("Error setting up SSL_CTX client key", c_key);
- if (!SSL_CTX_check_private_key(ctx))
+ if(!SSL_CTX_check_private_key(ctx))
ssl_err("Error setting up SSL_CTX client key");
- if (SSL_CTX_load_verify_locations(ctx, s_cert, NULL) != 1)
+ if(SSL_CTX_load_verify_locations(ctx, s_cert, NULL) != 1)
ssl_path_err("Error setting up SSL_CTX verify, server cert",
s_cert);
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
if(first_line && strncmp(buf, "error", 5) == 0) {
printf("%s", buf);
was_error = 1;
- } else if (!quiet)
+ } else if(!quiet) {
printf("%s", buf);
+ }
first_line = 0;
}
#endif
}
if(argc >= 1 && strcmp(argv[0], "stats_shm")==0) {
- print_stats_shm(cfgfile);
+ print_stats_shm(cfgfile, quiet);
return 0;
}
check_args_for_listcmd(argc, argv);
cfg->use_systemd = 0;
cfg->do_daemonize = 1;
cfg->if_automatic = 0;
+ cfg->if_automatic_ports = NULL;
cfg->so_rcvbuf = 0;
cfg->so_sndbuf = 0;
cfg->so_reuseport = REUSEPORT_DEFAULT;
cfg->serve_expired_ttl_reset = 0;
cfg->serve_expired_reply_ttl = 30;
cfg->serve_expired_client_timeout = 0;
+ cfg->ede_serve_expired = 0;
cfg->serve_original_ttl = 0;
cfg->zonemd_permissive_mode = 0;
cfg->add_holddown = 30*24*3600;
cfg->ipset_name_v4 = NULL;
cfg->ipset_name_v6 = NULL;
#endif
+ cfg->ede = 0;
return cfg;
error_exit:
config_delete(cfg);
else if(atoi(val) == 0)
return 0;
else cfg->stat_interval = atoi(val);
- } else if(strcmp(opt, "num_threads:") == 0) {
+ } else if(strcmp(opt, "num-threads:") == 0) {
/* not supported, library must have 1 thread in bgworker */
return 0;
} else if(strcmp(opt, "outgoing-port-permit:") == 0) {
else S_STR("ssl-cert-bundle:", tls_cert_bundle)
else S_STR("tls-cert-bundle:", tls_cert_bundle)
else S_YNO("tls-win-cert:", tls_win_cert)
+ else S_YNO("tls-system-cert:", tls_win_cert)
else S_STRLIST("additional-ssl-port:", tls_additional_port)
else S_STRLIST("additional-tls-port:", tls_additional_port)
else S_STRLIST("tls-additional-ports:", tls_additional_port)
else S_YNO("http-nodelay:", http_nodelay)
else S_YNO("http-notls-downstream:", http_notls_downstream)
else S_YNO("interface-automatic:", if_automatic)
+ else S_STR("interface-automatic-ports:", if_automatic_ports)
else S_YNO("use-systemd:", use_systemd)
else S_YNO("do-daemonize:", do_daemonize)
else S_NUMBER_NONZERO("port:", port)
else if(strcmp(opt, "serve-expired-reply-ttl:") == 0)
{ IS_NUMBER_OR_ZERO; cfg->serve_expired_reply_ttl = atoi(val); SERVE_EXPIRED_REPLY_TTL=(time_t)cfg->serve_expired_reply_ttl;}
else S_NUMBER_OR_ZERO("serve-expired-client-timeout:", serve_expired_client_timeout)
+ else S_YNO("ede:", ede)
+ else S_YNO("ede-serve-expired:", ede_serve_expired)
else S_YNO("serve-original-ttl:", serve_original_ttl)
else S_STR("val-nsec3-keysize-iterations:", val_nsec3_key_iterations)
else S_YNO("zonemd-permissive-mode:", zonemd_permissive_mode)
else O_IFC(opt, "interface", num_ifs, ifs)
else O_IFC(opt, "outgoing-interface", num_out_ifs, out_ifs)
else O_YNO(opt, "interface-automatic", if_automatic)
+ else O_STR(opt, "interface-automatic-ports", if_automatic_ports)
else O_DEC(opt, "port", port)
else O_DEC(opt, "outgoing-range", outgoing_num_ports)
else O_DEC(opt, "outgoing-num-tcp", outgoing_num_tcp)
else O_STR(opt, "ssl-cert-bundle", tls_cert_bundle)
else O_STR(opt, "tls-cert-bundle", tls_cert_bundle)
else O_YNO(opt, "tls-win-cert", tls_win_cert)
+ else O_YNO(opt, "tls-system-cert", tls_win_cert)
else O_LST(opt, "additional-ssl-port", tls_additional_port)
else O_LST(opt, "additional-tls-port", tls_additional_port)
else O_LST(opt, "tls-additional-ports", tls_additional_port)
else O_YNO(opt, "serve-expired-ttl-reset", serve_expired_ttl_reset)
else O_DEC(opt, "serve-expired-reply-ttl", serve_expired_reply_ttl)
else O_DEC(opt, "serve-expired-client-timeout", serve_expired_client_timeout)
+ else O_YNO(opt, "ede", ede)
+ else O_YNO(opt, "ede-serve-expired", ede_serve_expired)
else O_YNO(opt, "serve-original-ttl", serve_original_ttl)
else O_STR(opt, "val-nsec3-keysize-iterations",val_nsec3_key_iterations)
else O_YNO(opt, "zonemd-permissive-mode", zonemd_permissive_mode)
free(cfg->directory);
free(cfg->logfile);
free(cfg->pidfile);
+ free(cfg->if_automatic_ports);
free(cfg->target_fetch_policy);
free(cfg->ssl_service_key);
free(cfg->ssl_service_pem);
while(*ip_end && isspace((unsigned char)*ip_end))
ip_end++;
if(name>ip_end) {
- snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%.*s",
+ snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%.*s",
(int)(name-ip_end), ip_end);
}
snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), " PTR %s", name);
}
#endif /* UB_ON_WINDOWS */
-void errinf(struct module_qstate* qstate, const char* str)
-{
- struct config_strlist* p;
- if((qstate->env->cfg->val_log_level < 2 && !qstate->env->cfg->log_servfail) || !str)
- return;
- p = (struct config_strlist*)regional_alloc(qstate->region, sizeof(*p));
- if(!p) {
- log_err("malloc failure in validator-error-info string");
- return;
- }
- p->next = NULL;
- p->str = regional_strdup(qstate->region, str);
- if(!p->str) {
- log_err("malloc failure in validator-error-info string");
- return;
- }
- /* add at end */
- if(qstate->errinf) {
- struct config_strlist* q = qstate->errinf;
- while(q->next)
- q = q->next;
- q->next = p;
- } else qstate->errinf = p;
-}
-
-void errinf_origin(struct module_qstate* qstate, struct sock_list *origin)
-{
- struct sock_list* p;
- if(qstate->env->cfg->val_log_level < 2 && !qstate->env->cfg->log_servfail)
- return;
- for(p=origin; p; p=p->next) {
- char buf[256];
- if(p == origin)
- snprintf(buf, sizeof(buf), "from ");
- else snprintf(buf, sizeof(buf), "and ");
- if(p->len == 0)
- snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf),
- "cache");
- else
- addr_to_str(&p->addr, p->len, buf+strlen(buf),
- sizeof(buf)-strlen(buf));
- errinf(qstate, buf);
- }
-}
-
-char* errinf_to_str_bogus(struct module_qstate* qstate)
-{
- char buf[20480];
- char* p = buf;
- size_t left = sizeof(buf);
- struct config_strlist* s;
- char dname[LDNS_MAX_DOMAINLEN+1];
- char t[16], c[16];
- sldns_wire2str_type_buf(qstate->qinfo.qtype, t, sizeof(t));
- sldns_wire2str_class_buf(qstate->qinfo.qclass, c, sizeof(c));
- dname_str(qstate->qinfo.qname, dname);
- snprintf(p, left, "validation failure <%s %s %s>:", dname, t, c);
- left -= strlen(p); p += strlen(p);
- if(!qstate->errinf)
- snprintf(p, left, " misc failure");
- else for(s=qstate->errinf; s; s=s->next) {
- snprintf(p, left, " %s", s->str);
- left -= strlen(p); p += strlen(p);
- }
- p = strdup(buf);
- if(!p)
- log_err("malloc failure in errinf_to_str");
- return p;
-}
-
-char* errinf_to_str_servfail(struct module_qstate* qstate)
-{
- char buf[20480];
- char* p = buf;
- size_t left = sizeof(buf);
- struct config_strlist* s;
- char dname[LDNS_MAX_DOMAINLEN+1];
- char t[16], c[16];
- sldns_wire2str_type_buf(qstate->qinfo.qtype, t, sizeof(t));
- sldns_wire2str_class_buf(qstate->qinfo.qclass, c, sizeof(c));
- dname_str(qstate->qinfo.qname, dname);
- snprintf(p, left, "SERVFAIL <%s %s %s>:", dname, t, c);
- left -= strlen(p); p += strlen(p);
- if(!qstate->errinf)
- snprintf(p, left, " misc failure");
- else for(s=qstate->errinf; s; s=s->next) {
- snprintf(p, left, " %s", s->str);
- left -= strlen(p); p += strlen(p);
- }
- p = strdup(buf);
- if(!p)
- log_err("malloc failure in errinf_to_str");
- return p;
-}
-
-void errinf_rrset(struct module_qstate* qstate, struct ub_packed_rrset_key *rr)
-{
- char buf[1024];
- char dname[LDNS_MAX_DOMAINLEN+1];
- char t[16], c[16];
- if((qstate->env->cfg->val_log_level < 2 && !qstate->env->cfg->log_servfail) || !rr)
- return;
- sldns_wire2str_type_buf(ntohs(rr->rk.type), t, sizeof(t));
- sldns_wire2str_class_buf(ntohs(rr->rk.rrset_class), c, sizeof(c));
- dname_str(rr->rk.dname, dname);
- snprintf(buf, sizeof(buf), "for <%s %s %s>", dname, t, c);
- errinf(qstate, buf);
-}
-
-void errinf_dname(struct module_qstate* qstate, const char* str, uint8_t* dname)
-{
- char b[1024];
- char buf[LDNS_MAX_DOMAINLEN+1];
- if((qstate->env->cfg->val_log_level < 2 && !qstate->env->cfg->log_servfail) || !str || !dname)
- return;
- dname_str(dname, buf);
- snprintf(b, sizeof(b), "%s %s", str, buf);
- errinf(qstate, b);
-}
-
int options_remote_is_address(struct config_file* cfg)
{
if(!cfg->remote_control_enable) return 0;
#ifndef UTIL_CONFIG_FILE_H
#define UTIL_CONFIG_FILE_H
+#include "sldns/rrdef.h"
struct config_stub;
struct config_auth;
struct config_view;
/** automatic interface for incoming messages. Uses ipv6 remapping,
* and recvmsg/sendmsg ancillary data to detect interfaces, boolean */
int if_automatic;
+ /** extra ports to open if if_automatic enabled, or NULL for default */
+ char* if_automatic_ports;
/** SO_RCVBUF size to set on port 53 UDP socket */
size_t so_rcvbuf;
/** SO_SNDBUF size to set on port 53 UDP socket */
/** serve expired entries only after trying to update the entries and this
* timeout (in milliseconds) is reached */
int serve_expired_client_timeout;
+ /** serve EDE code 3 - Stale Answer (RFC8914) for expired entries */
+ int ede_serve_expired;
/** serve original TTLs rather than decrementing ones */
int serve_original_ttl;
/** nsec3 maximum iterations per key size, string */
char* ipset_name_v4;
char* ipset_name_v6;
#endif
+ /** respond with Extended DNS Errors (RFC8914) */
+ int ede;
};
/** from cfg username, after daemonize setup performed */
*/
char* cfg_ptr_reverse(char* str);
-/**
- * Append text to the error info for validation.
- * @param qstate: query state.
- * @param str: copied into query region and appended.
- * Failures to allocate are logged.
- */
-void errinf(struct module_qstate* qstate, const char* str);
-
-/**
- * Append text to error info: from 1.2.3.4
- * @param qstate: query state.
- * @param origin: sock list with origin of trouble.
- * Every element added.
- * If NULL: nothing is added.
- * if 0len element: 'from cache' is added.
- */
-void errinf_origin(struct module_qstate* qstate, struct sock_list *origin);
-
-/**
- * Append text to error info: for RRset name type class
- * @param qstate: query state.
- * @param rr: rrset_key.
- */
-void errinf_rrset(struct module_qstate* qstate, struct ub_packed_rrset_key *rr);
-
-/**
- * Append text to error info: str dname
- * @param qstate: query state.
- * @param str: explanation string
- * @param dname: the dname.
- */
-void errinf_dname(struct module_qstate* qstate, const char* str,
- uint8_t* dname);
-
-/**
- * Create error info in string. For validation failures.
- * @param qstate: query state.
- * @return string or NULL on malloc failure (already logged).
- * This string is malloced and has to be freed by caller.
- */
-char* errinf_to_str_bogus(struct module_qstate* qstate);
-
-/**
- * Create error info in string. For other servfails.
- * @param qstate: query state.
- * @return string or NULL on malloc failure (already logged).
- * This string is malloced and has to be freed by caller.
- */
-char* errinf_to_str_servfail(struct module_qstate* qstate);
-
/**
* Used during options parsing
*/
ssl-cert-bundle{COLON} { YDVAR(1, VAR_TLS_CERT_BUNDLE) }
tls-cert-bundle{COLON} { YDVAR(1, VAR_TLS_CERT_BUNDLE) }
tls-win-cert{COLON} { YDVAR(1, VAR_TLS_WIN_CERT) }
+tls-system-cert{COLON} { YDVAR(1, VAR_TLS_WIN_CERT) }
additional-ssl-port{COLON} { YDVAR(1, VAR_TLS_ADDITIONAL_PORT) }
additional-tls-port{COLON} { YDVAR(1, VAR_TLS_ADDITIONAL_PORT) }
tls-additional-ports{COLON} { YDVAR(1, VAR_TLS_ADDITIONAL_PORT) }
ip-address{COLON} { YDVAR(1, VAR_INTERFACE) }
outgoing-interface{COLON} { YDVAR(1, VAR_OUTGOING_INTERFACE) }
interface-automatic{COLON} { YDVAR(1, VAR_INTERFACE_AUTOMATIC) }
+interface-automatic-ports{COLON} { YDVAR(1, VAR_INTERFACE_AUTOMATIC_PORTS) }
so-rcvbuf{COLON} { YDVAR(1, VAR_SO_RCVBUF) }
so-sndbuf{COLON} { YDVAR(1, VAR_SO_SNDBUF) }
so-reuseport{COLON} { YDVAR(1, VAR_SO_REUSEPORT) }
serve-expired-ttl-reset{COLON} { YDVAR(1, VAR_SERVE_EXPIRED_TTL_RESET) }
serve-expired-reply-ttl{COLON} { YDVAR(1, VAR_SERVE_EXPIRED_REPLY_TTL) }
serve-expired-client-timeout{COLON} { YDVAR(1, VAR_SERVE_EXPIRED_CLIENT_TIMEOUT) }
+ede-serve-expired{COLON} { YDVAR(1, VAR_EDE_SERVE_EXPIRED) }
serve-original-ttl{COLON} { YDVAR(1, VAR_SERVE_ORIGINAL_TTL) }
fake-dsa{COLON} { YDVAR(1, VAR_FAKE_DSA) }
fake-sha1{COLON} { YDVAR(1, VAR_FAKE_SHA1) }
edns-client-string{COLON} { YDVAR(2, VAR_EDNS_CLIENT_STRING) }
edns-client-string-opcode{COLON} { YDVAR(1, VAR_EDNS_CLIENT_STRING_OPCODE) }
nsid{COLON} { YDVAR(1, VAR_NSID ) }
+ede{COLON} { YDVAR(1, VAR_EDE ) }
<INITIAL,val>{NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++; }
/* Quoted strings. Strip leading and ending quotes */
%token VAR_ACCESS_CONTROL_TAG_DATA VAR_VIEW VAR_ACCESS_CONTROL_VIEW
%token VAR_VIEW_FIRST VAR_SERVE_EXPIRED VAR_SERVE_EXPIRED_TTL
%token VAR_SERVE_EXPIRED_TTL_RESET VAR_SERVE_EXPIRED_REPLY_TTL
-%token VAR_SERVE_EXPIRED_CLIENT_TIMEOUT VAR_SERVE_ORIGINAL_TTL VAR_FAKE_DSA
+%token VAR_SERVE_EXPIRED_CLIENT_TIMEOUT VAR_EDE_SERVE_EXPIRED
+%token VAR_SERVE_ORIGINAL_TTL VAR_FAKE_DSA
%token VAR_FAKE_SHA1 VAR_LOG_IDENTITY VAR_HIDE_TRUSTANCHOR
%token VAR_HIDE_HTTP_USER_AGENT VAR_HTTP_USER_AGENT
%token VAR_TRUST_ANCHOR_SIGNALING VAR_AGGRESSIVE_NSEC VAR_USE_SYSTEMD
%token VAR_DYNLIB VAR_DYNLIB_FILE VAR_EDNS_CLIENT_STRING
%token VAR_EDNS_CLIENT_STRING_OPCODE VAR_NSID
%token VAR_ZONEMD_PERMISSIVE_MODE VAR_ZONEMD_CHECK VAR_ZONEMD_REJECT_ABSENCE
-%token VAR_RPZ_SIGNAL_NXDOMAIN_RA
+%token VAR_RPZ_SIGNAL_NXDOMAIN_RA VAR_INTERFACE_AUTOMATIC_PORTS VAR_EDE
%%
toplevelvars: /* empty */ | toplevelvars toplevelvar ;
server_serve_expired |
server_serve_expired_ttl | server_serve_expired_ttl_reset |
server_serve_expired_reply_ttl | server_serve_expired_client_timeout |
- server_serve_original_ttl | server_fake_dsa |
+ server_ede_serve_expired | server_serve_original_ttl | server_fake_dsa |
server_log_identity | server_use_systemd |
server_response_ip_tag | server_response_ip | server_response_ip_data |
server_shm_enable | server_shm_key | server_fake_sha1 |
server_tls_use_sni | server_edns_client_string |
server_edns_client_string_opcode | server_nsid |
server_zonemd_permissive_mode | server_max_reuse_tcp_queries |
- server_tcp_reuse_timeout | server_tcp_auth_query_timeout
+ server_tcp_reuse_timeout | server_tcp_auth_query_timeout |
+ server_interface_automatic_ports | server_ede
;
stubstart: VAR_STUB_ZONE
free($2);
}
;
+server_interface_automatic_ports: VAR_INTERFACE_AUTOMATIC_PORTS STRING_ARG
+ {
+ OUTYY(("P(server_interface_automatic_ports:%s)\n", $2));
+ free(cfg_parser->cfg->if_automatic_ports);
+ cfg_parser->cfg->if_automatic_ports = $2;
+ }
+ ;
server_do_ip4: VAR_DO_IP4 STRING_ARG
{
OUTYY(("P(server_do_ip4:%s)\n", $2));
free($2);
}
;
+server_ede_serve_expired: VAR_EDE_SERVE_EXPIRED STRING_ARG
+ {
+ OUTYY(("P(server_ede_serve_expired:%s)\n", $2));
+ if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+ yyerror("expected yes or no.");
+ else cfg_parser->cfg->ede_serve_expired = (strcmp($2, "yes")==0);
+ free($2);
+ }
+ ;
server_serve_original_ttl: VAR_SERVE_ORIGINAL_TTL STRING_ARG
{
OUTYY(("P(server_serve_original_ttl:%s)\n", $2));
&& strcmp($3, "noview")!=0
&& strcmp($3, "inform")!=0 && strcmp($3, "inform_deny")!=0
&& strcmp($3, "inform_redirect") != 0
- && strcmp($3, "ipset") != 0) {
+ && strcmp($3, "ipset") != 0) {
yyerror("local-zone type: expected static, deny, "
"refuse, redirect, transparent, "
"typetransparent, inform, inform_deny, "
free($3);
#ifdef USE_IPSET
} else if(strcmp($3, "ipset")==0) {
+ size_t len = strlen($2);
+ /* Make sure to add the trailing dot.
+ * These are str compared to domain names. */
+ if($2[len-1] != '.') {
+ if(!($2 = realloc($2, len+2))) {
+ fatal_exit("out of memory adding local-zone");
+ }
+ $2[len] = '.';
+ $2[len+1] = 0;
+ }
if(!cfg_strlist_insert(&cfg_parser->cfg->
local_zones_ipset, $2))
fatal_exit("out of memory adding local-zone");
yyerror("option code must be in interval [0, 65535]");
else cfg_parser->cfg->edns_client_string_opcode = atoi($2);
free($2);
-
+ }
+ ;
+server_ede: VAR_EDE STRING_ARG
+ {
+ OUTYY(("P(server_ede:%s)\n", $2));
+ if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+ yyerror("expected yes or no.");
+ else cfg_parser->cfg->ede = (strcmp($2, "yes")==0);
+ free($2);
}
;
stub_name: VAR_NAME STRING_ARG
free($3);
#ifdef USE_IPSET
} else if(strcmp($3, "ipset")==0) {
+ size_t len = strlen($2);
+ /* Make sure to add the trailing dot.
+ * These are str compared to domain names. */
+ if($2[len-1] != '.') {
+ if(!($2 = realloc($2, len+2))) {
+ fatal_exit("out of memory adding local-zone");
+ }
+ $2[len] = '.';
+ $2[len+1] = 0;
+ }
if(!cfg_strlist_insert(&cfg_parser->cfg->views->
local_zones_ipset, $2))
fatal_exit("out of memory adding local-zone");
}
/** skip RRs from packet */
-static int
+int
skip_pkt_rrs(sldns_buffer* pkt, int num)
{
int i;
}
}
}
+
int parse_extract_edns_from_response_msg(struct msg_parse* msg,
struct edns_data* edns, struct regional* region);
+/**
+ * Skip RRs from packet
+ * @param pkt: the packet. position at start must be right after the query
+ * section. At end, right after EDNS data or no movement if failed.
+ * @param num: Limit of the number of records we want to parse.
+ * @return: 0 on success, 1 on failure.
+ */
+int skip_pkt_rrs(struct sldns_buffer* pkt, int num);
+
/**
* If EDNS data follows a query section, extract it and initialize edns struct.
* @param pkt: the packet. position at start must be right after the query
rep->ar_numrrsets = ar;
rep->rrset_count = total;
rep->security = sec;
+ rep->reason_bogus = LDNS_EDE_NONE;
rep->authoritative = 0;
/* array starts after the refs */
if(region)
return rep;
}
+int edns_opt_list_append_ede(struct edns_option** list, struct regional* region,
+ sldns_ede_code code, const char *txt)
+{
+ struct edns_option** prevp;
+ struct edns_option* opt;
+ size_t txt_len = txt ? strlen(txt) : 0;
+
+ /* allocate new element */
+ opt = (struct edns_option*)regional_alloc(region, sizeof(*opt));
+ if(!opt)
+ return 0;
+ opt->next = NULL;
+ opt->opt_code = LDNS_EDNS_EDE;
+ opt->opt_len = txt_len + sizeof(uint16_t);
+ opt->opt_data = regional_alloc(region, txt_len + sizeof(uint16_t));
+ if(!opt->opt_data)
+ return 0;
+ sldns_write_uint16(opt->opt_data, (uint16_t)code);
+ if (txt_len)
+ memmove(opt->opt_data + 2, txt, txt_len);
+
+ /* append at end of list */
+ prevp = list;
+ while(*prevp != NULL)
+ prevp = &((*prevp)->next);
+ verbose(VERB_ALGO, "attached EDE code: %d with message: %s", code, txt);
+ *prevp = opt;
+ return 1;
+}
+
int edns_opt_list_append(struct edns_option** list, uint16_t code, size_t len,
uint8_t* data, struct regional* region)
{
#define UTIL_DATA_MSGREPLY_H
#include "util/storage/lruhash.h"
#include "util/data/packed_rrset.h"
+#include "sldns/rrdef.h"
struct sldns_buffer;
struct comm_reply;
struct alloc_cache;
*/
enum sec_status security;
+ /**
+ * EDE (rfc8914) code with reason for DNSSEC bogus status.
+ */
+ sldns_ede_code reason_bogus;
+
/**
* Number of RRsets in each section.
* The answer section. Add up the RRs in every RRset to calculate
* @return false on failure.
*/
int edns_opt_list_append(struct edns_option** list, uint16_t code, size_t len,
- uint8_t* data, struct regional* region);
+ uint8_t* data, struct regional* region);
+
+/**
+ * Append edns EDE option to edns options list
+ * @param LIST: the edns option list to append the edns option to.
+ * @param REGION: region to allocate the new edns option.
+ * @param CODE: the EDE code.
+ * @param TXT: Additional text for the option
+ */
+#define EDNS_OPT_LIST_APPEND_EDE(LIST, REGION, CODE, TXT) \
+ do { \
+ struct { \
+ uint16_t code; \
+ char text[sizeof(TXT) - 1]; \
+ } ede = { htons(CODE), TXT }; \
+ verbose(VERB_ALGO, "attached EDE code: %d with" \
+ " message: %s", CODE, TXT); \
+ edns_opt_list_append((LIST), LDNS_EDNS_EDE, \
+ sizeof(uint16_t) + sizeof(TXT) - 1, \
+ (void *)&ede, (REGION)); \
+ } while(0)
+
+/**
+ * Append edns EDE option to edns options list
+ * @param list: the edns option list to append the edns option to.
+ * @param region: region to allocate the new edns option.
+ * @param code: the EDE code.
+ * @param txt: Additional text for the option
+ * @return false on failure.
+ */
+int edns_opt_list_append_ede(struct edns_option** list, struct regional* region,
+ sldns_ede_code code, const char *txt);
/**
* Remove any option found on the edns option list that matches the code.
#include "config.h"
#include "util/module.h"
#include "sldns/wire2str.h"
+#include "util/config_file.h"
+#include "util/regional.h"
+#include "util/data/dname.h"
+#include "util/net_help.h"
const char*
strextstate(enum module_ext_state s)
return "bad_event_value";
}
+void errinf(struct module_qstate* qstate, const char* str)
+{
+ errinf_ede(qstate, str, LDNS_EDE_NONE);
+}
+
+void errinf_ede(struct module_qstate* qstate,
+ const char* str, sldns_ede_code reason_bogus)
+{
+ struct errinf_strlist* p;
+ if((qstate->env->cfg->val_log_level < 2 && !qstate->env->cfg->log_servfail) || !str)
+ return;
+ p = (struct errinf_strlist*)regional_alloc(qstate->region, sizeof(*p));
+ if(!p) {
+ log_err("malloc failure in validator-error-info string");
+ return;
+ }
+ p->next = NULL;
+ p->str = regional_strdup(qstate->region, str);
+ p->reason_bogus = reason_bogus;
+ if(!p->str) {
+ log_err("malloc failure in validator-error-info string");
+ return;
+ }
+ /* add at end */
+ if(qstate->errinf) {
+ struct errinf_strlist* q = qstate->errinf;
+ while(q->next)
+ q = q->next;
+ q->next = p;
+ } else qstate->errinf = p;
+}
+
+void errinf_origin(struct module_qstate* qstate, struct sock_list *origin)
+{
+ struct sock_list* p;
+ if(qstate->env->cfg->val_log_level < 2 && !qstate->env->cfg->log_servfail)
+ return;
+ for(p=origin; p; p=p->next) {
+ char buf[256];
+ if(p == origin)
+ snprintf(buf, sizeof(buf), "from ");
+ else snprintf(buf, sizeof(buf), "and ");
+ if(p->len == 0)
+ snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf),
+ "cache");
+ else
+ addr_to_str(&p->addr, p->len, buf+strlen(buf),
+ sizeof(buf)-strlen(buf));
+ errinf(qstate, buf);
+ }
+}
+
+char* errinf_to_str_bogus(struct module_qstate* qstate)
+{
+ char buf[20480];
+ char* p = buf;
+ size_t left = sizeof(buf);
+ struct errinf_strlist* s;
+ char dname[LDNS_MAX_DOMAINLEN+1];
+ char t[16], c[16];
+ sldns_wire2str_type_buf(qstate->qinfo.qtype, t, sizeof(t));
+ sldns_wire2str_class_buf(qstate->qinfo.qclass, c, sizeof(c));
+ dname_str(qstate->qinfo.qname, dname);
+ snprintf(p, left, "validation failure <%s %s %s>:", dname, t, c);
+ left -= strlen(p); p += strlen(p);
+ if(!qstate->errinf)
+ snprintf(p, left, " misc failure");
+ else for(s=qstate->errinf; s; s=s->next) {
+ snprintf(p, left, " %s", s->str);
+ left -= strlen(p); p += strlen(p);
+ }
+ p = strdup(buf);
+ if(!p)
+ log_err("malloc failure in errinf_to_str");
+ return p;
+}
+
+sldns_ede_code errinf_to_reason_bogus(struct module_qstate* qstate)
+{
+ struct errinf_strlist* s;
+ for(s=qstate->errinf; s; s=s->next) {
+ if (s->reason_bogus != LDNS_EDE_NONE) {
+ return s->reason_bogus;
+ }
+ }
+ return LDNS_EDE_NONE;
+}
+
+char* errinf_to_str_servfail(struct module_qstate* qstate)
+{
+ char buf[20480];
+ char* p = buf;
+ size_t left = sizeof(buf);
+ struct errinf_strlist* s;
+ char dname[LDNS_MAX_DOMAINLEN+1];
+ char t[16], c[16];
+ sldns_wire2str_type_buf(qstate->qinfo.qtype, t, sizeof(t));
+ sldns_wire2str_class_buf(qstate->qinfo.qclass, c, sizeof(c));
+ dname_str(qstate->qinfo.qname, dname);
+ snprintf(p, left, "SERVFAIL <%s %s %s>:", dname, t, c);
+ left -= strlen(p); p += strlen(p);
+ if(!qstate->errinf)
+ snprintf(p, left, " misc failure");
+ else for(s=qstate->errinf; s; s=s->next) {
+ snprintf(p, left, " %s", s->str);
+ left -= strlen(p); p += strlen(p);
+ }
+ p = strdup(buf);
+ if(!p)
+ log_err("malloc failure in errinf_to_str");
+ return p;
+}
+
+void errinf_rrset(struct module_qstate* qstate, struct ub_packed_rrset_key *rr)
+{
+ char buf[1024];
+ char dname[LDNS_MAX_DOMAINLEN+1];
+ char t[16], c[16];
+ if((qstate->env->cfg->val_log_level < 2 && !qstate->env->cfg->log_servfail) || !rr)
+ return;
+ sldns_wire2str_type_buf(ntohs(rr->rk.type), t, sizeof(t));
+ sldns_wire2str_class_buf(ntohs(rr->rk.rrset_class), c, sizeof(c));
+ dname_str(rr->rk.dname, dname);
+ snprintf(buf, sizeof(buf), "for <%s %s %s>", dname, t, c);
+ errinf(qstate, buf);
+}
+
+void errinf_dname(struct module_qstate* qstate, const char* str, uint8_t* dname)
+{
+ char b[1024];
+ char buf[LDNS_MAX_DOMAINLEN+1];
+ if((qstate->env->cfg->val_log_level < 2 && !qstate->env->cfg->log_servfail) || !str || !dname)
+ return;
+ dname_str(dname, buf);
+ snprintf(b, sizeof(b), "%s %s", str, buf);
+ errinf(qstate, b);
+}
+
int
edns_known_options_init(struct module_env* env)
{
/** Maximum number of known edns options */
#define MAX_KNOWN_EDNS_OPTS 256
+struct errinf_strlist {
+ /** next item in list */
+ struct errinf_strlist* next;
+ /** config option string */
+ char* str;
+ /** EDE code companion to the error str */
+ int reason_bogus;
+};
+
enum inplace_cb_list_type {
/* Inplace callbacks for when a resolved reply is ready to be sent to the
* front.*/
/** region for this query. Cleared when query process finishes. */
struct regional* region;
/** failure reason information if val-log-level is high */
- struct config_strlist* errinf;
-
+ struct errinf_strlist* errinf;
/** which module is executing */
int curmod;
/** module states */
/** Extended result of response-ip action processing, mainly
* for logging purposes. */
struct respip_action_info* respip_action_info;
+ /** if the query is rpz passthru, no further rpz processing for it */
+ int rpz_passthru;
/** whether the reply should be dropped */
int is_drop;
*/
const char* strmodulevent(enum module_ev e);
+/**
+ * Append text to the error info for validation.
+ * @param qstate: query state.
+ * @param str: copied into query region and appended.
+ * Failures to allocate are logged.
+ */
+void errinf(struct module_qstate* qstate, const char* str);
+void errinf_ede(struct module_qstate* qstate, const char* str,
+ sldns_ede_code reason_bogus);
+
+/**
+ * Append text to error info: from 1.2.3.4
+ * @param qstate: query state.
+ * @param origin: sock list with origin of trouble.
+ * Every element added.
+ * If NULL: nothing is added.
+ * if 0len element: 'from cache' is added.
+ */
+void errinf_origin(struct module_qstate* qstate, struct sock_list *origin);
+
+/**
+ * Append text to error info: for RRset name type class
+ * @param qstate: query state.
+ * @param rr: rrset_key.
+ */
+void errinf_rrset(struct module_qstate* qstate, struct ub_packed_rrset_key *rr);
+
+/**
+ * Append text to error info: str dname
+ * @param qstate: query state.
+ * @param str: explanation string
+ * @param dname: the dname.
+ */
+void errinf_dname(struct module_qstate* qstate, const char* str,
+ uint8_t* dname);
+
+/**
+ * Create error info in string. For validation failures.
+ * @param qstate: query state.
+ * @return string or NULL on malloc failure (already logged).
+ * This string is malloced and has to be freed by caller.
+ */
+char* errinf_to_str_bogus(struct module_qstate* qstate);
+/**
+ * Check the sldns_ede_code of the qstate.
+ * @param qstate: query state.
+ * @return LDNS_EDE_DNSSEC_BOGUS by default, or the first explicitly set
+ * sldns_ede_code.
+ */
+sldns_ede_code errinf_to_reason_bogus(struct module_qstate* qstate);
+
+/**
+ * Create error info in string. For other servfails.
+ * @param qstate: query state.
+ * @return string or NULL on malloc failure (already logged).
+ * This string is malloced and has to be freed by caller.
+ */
+char* errinf_to_str_servfail(struct module_qstate* qstate);
+
/**
* Initialize the edns known options by allocating the required space.
* @param env: the module environment.
}
}
#else
- (void)wincert;
+ if(wincert) {
+ if(!SSL_CTX_set_default_verify_paths(ctx)) {
+ log_crypto_err("error in default_verify_paths");
+ SSL_CTX_free(ctx);
+ return NULL;
+ }
+ }
#endif
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
}
{
if(verbosity >= VERB_QUERY)
return 0; /* only squelch on low verbosity */
- /* this is very specific, we could filter on ERR_GET_REASON()
- * (the third element in ERR_PACK) */
- if(err == ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_GET_RECORD, SSL_R_HTTPS_PROXY_REQUEST) ||
- err == ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_GET_RECORD, SSL_R_HTTP_REQUEST) ||
- err == ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_GET_RECORD, SSL_R_WRONG_VERSION_NUMBER) ||
- err == ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_READ_BYTES, SSL_R_SSLV3_ALERT_BAD_CERTIFICATE)
+ if(ERR_GET_LIB(err) == ERR_LIB_SSL &&
+ (ERR_GET_REASON(err) == SSL_R_HTTPS_PROXY_REQUEST ||
+ ERR_GET_REASON(err) == SSL_R_HTTP_REQUEST ||
+ ERR_GET_REASON(err) == SSL_R_WRONG_VERSION_NUMBER ||
+ ERR_GET_REASON(err) == SSL_R_SSLV3_ALERT_BAD_CERTIFICATE
#ifdef SSL_F_TLS_POST_PROCESS_CLIENT_HELLO
- || err == ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_POST_PROCESS_CLIENT_HELLO, SSL_R_NO_SHARED_CIPHER)
+ || ERR_GET_REASON(err) == SSL_R_NO_SHARED_CIPHER
#endif
#ifdef SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO
- || err == ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO, SSL_R_UNKNOWN_PROTOCOL)
- || err == ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO, SSL_R_UNSUPPORTED_PROTOCOL)
+ || ERR_GET_REASON(err) == SSL_R_UNKNOWN_PROTOCOL
+ || ERR_GET_REASON(err) == SSL_R_UNSUPPORTED_PROTOCOL
# ifdef SSL_R_VERSION_TOO_LOW
- || err == ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO, SSL_R_VERSION_TOO_LOW)
+ || ERR_GET_REASON(err) == SSL_R_VERSION_TOO_LOW
# endif
#endif
- )
+ ))
return 1;
return 0;
}
if(errno == ECONNRESET && verbosity < 2)
return 0; /* silence reset by peer */
#endif
+ if(!tcp_connect_errno_needs_log(
+ (struct sockaddr*)&c->repinfo.addr,
+ c->repinfo.addrlen))
+ return 0; /* silence connect failures that
+ show up because after connect this is the
+ first system call that accesses the socket */
if(errno != 0)
log_err("SSL_handshake syscall: %s",
strerror(errno));
remainbufferlen = sldns_buffer_capacity(c->buffer) -
sldns_buffer_limit(c->buffer);
if(remainbufferlen+got_now >= c->tcp_byte_count ||
- remainbufferlen >= (c->ssl?16384:2048)) {
+ remainbufferlen >= (size_t)(c->ssl?16384:2048)) {
size_t total = sldns_buffer_limit(c->buffer);
sldns_buffer_clear(c->buffer);
sldns_buffer_set_position(c->buffer, total);
#else
llvalue = (unsigned long long)tp;
#endif
-#ifndef USE_WINSOCK
- snprintf(tempf, sizeof(tempf), "%s.%d-%d-%llx", fname, (int)getpid(),
+ snprintf(tempf, sizeof(tempf), "%s.%d-%d-" ARG_LL "x", fname, (int)getpid(),
env->worker?*(int*)env->worker:0, llvalue);
-#else
- snprintf(tempf, sizeof(tempf), "%s.%d-%d-%I64x", fname, (int)getpid(),
- env->worker?*(int*)env->worker:0, llvalue);
-#endif
#endif /* S_SPLINT_S */
verbose(VERB_ALGO, "autotrust: write to disk: %s", tempf);
out = fopen(tempf, "w");
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,
- qstate);
+ NULL, 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
/* 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, LDNS_SECTION_ANSWER, qstate);
+ &reason, NULL, LDNS_SECTION_ANSWER, qstate);
return (sec == sec_status_secure);
}
qinfo.qclass);
if(!mesh_new_callback(env->mesh, &qinfo, qflags, &edns, buf, 0,
- &probe_answer_cb, env)) {
+ &probe_answer_cb, env, 0)) {
log_err("out of memory making 5011 probe");
}
}
if(d->security == sec_status_secure)
return 1;
d->security = val_verify_rrset_entry(env, ve, nsec, kkey, reason,
- LDNS_SECTION_AUTHORITY, qstate);
+ NULL, LDNS_SECTION_AUTHORITY, qstate);
if(d->security == sec_status_secure) {
rrset_update_sec_status(env->rrset_cache, nsec, *env->now);
return 1;
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 module_qstate* qstate)
+ struct key_entry_key* kkey, char** reason, sldns_ede_code *reason_bogus,
+ struct module_qstate* qstate)
{
struct packed_rrset_data* d;
size_t i;
if(d->security == sec_status_secure)
continue;
d->security = val_verify_rrset_entry(env, ve, list[i], kkey,
- reason, LDNS_SECTION_AUTHORITY, qstate);
+ reason, reason_bogus, LDNS_SECTION_AUTHORITY, qstate);
if(d->security != sec_status_secure) {
verbose(VERB_ALGO, "NSEC3 did not verify");
return 0;
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 module_qstate* qstate)
+ sldns_ede_code* reason_bogus, struct module_qstate* qstate)
{
rbtree_type ct;
struct nsec3_filter flt;
*reason = "no valid NSEC3s";
return sec_status_bogus; /* no valid NSEC3s, bogus */
}
- if(!list_is_secure(env, ve, list, num, kkey, reason, qstate))
+ if(!list_is_secure(env, ve, list, num, kkey, reason, reason_bogus, qstate)) {
+ *reason = "not all NSEC3 records secure";
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 */
if(!flt.zone) {
#define VALIDATOR_VAL_NSEC3_H
#include "util/rbtree.h"
#include "util/data/packed_rrset.h"
+#include "sldns/rrdef.h"
struct val_env;
struct regional;
struct module_env;
* @param qinfo: query that is verified for.
* @param kkey: key entry that signed the NSEC3s.
* @param reason: string for bogus result.
+ * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure.
* @param qstate: qstate with region.
* @return:
* sec_status SECURE of the proposition is proven by the NSEC3 RRs,
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 module_qstate* qstate);
+ sldns_ede_code* reason_bogus, struct module_qstate* qstate);
/**
* Prove NXDOMAIN or NODATA.
return 0;
}
+static enum sec_status
+dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve,
+ time_t now, struct ub_packed_rrset_key* rrset,
+ struct ub_packed_rrset_key* dnskey, size_t sig_idx,
+ struct rbtree_type** sortree,
+ char** reason, sldns_ede_code *reason_bogus,
+ sldns_pkt_section section, struct module_qstate* qstate);
+
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, sldns_pkt_section section,
- struct module_qstate* qstate)
+ uint8_t* sigalg, char** reason, sldns_ede_code *reason_bogus,
+ sldns_pkt_section section, struct module_qstate* qstate)
{
enum sec_status sec;
size_t i, num;
verbose(VERB_QUERY, "rrset failed to verify due to a lack of "
"signatures");
*reason = "no signatures";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_RRSIGS_MISSING;
return sec_status_bogus;
}
if(algo_needs_num_missing(&needs) == 0) {
verbose(VERB_QUERY, "zone has no known algorithms");
*reason = "zone has no known algorithms";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_UNSUPPORTED_DNSKEY_ALG;
return sec_status_insecure;
}
}
for(i=0; i<num; i++) {
sec = dnskeyset_verify_rrset_sig(env, ve, *env->now, rrset,
- dnskey, i, &sortree, reason, section, qstate);
+ dnskey, i, &sortree, reason, reason_bogus,
+ section, qstate);
/* see which algorithm has been fixed up */
if(sec == sec_status_secure) {
if(!sigalg)
enum sec_status
dnskey_verify_rrset(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
- size_t dnskey_idx, char** reason, sldns_pkt_section section,
- struct module_qstate* qstate)
+ size_t dnskey_idx, char** reason, sldns_ede_code *reason_bogus,
+ sldns_pkt_section section, struct module_qstate* qstate)
{
enum sec_status sec;
size_t i, num, numchecked = 0;
verbose(VERB_QUERY, "rrset failed to verify due to a lack of "
"signatures");
*reason = "no signatures";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_RRSIGS_MISSING;
return sec_status_bogus;
}
for(i=0; i<num; i++) {
tag != rrset_get_sig_keytag(rrset, i))
continue;
buf_canon = 0;
- sec = dnskey_verify_rrset_sig(env->scratch,
+ sec = dnskey_verify_rrset_sig(env->scratch,
env->scratch_buffer, ve, *env->now, rrset,
dnskey, dnskey_idx, i, &sortree, &buf_canon, reason,
- section, qstate);
+ reason_bogus, section, qstate);
if(sec == sec_status_secure)
return sec;
numchecked ++;
return sec_status_bogus;
}
-enum sec_status
-dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve,
- time_t now, struct ub_packed_rrset_key* rrset,
- struct ub_packed_rrset_key* dnskey, size_t sig_idx,
- struct rbtree_type** sortree, char** reason, sldns_pkt_section section,
- struct module_qstate* qstate)
+static enum sec_status
+dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve,
+ time_t now, struct ub_packed_rrset_key* rrset,
+ struct ub_packed_rrset_key* dnskey, size_t sig_idx,
+ struct rbtree_type** sortree,
+ char** reason, sldns_ede_code *reason_bogus,
+ sldns_pkt_section section, struct module_qstate* qstate)
{
/* find matching keys and check them */
enum sec_status sec = sec_status_bogus;
int buf_canon = 0;
verbose(VERB_ALGO, "verify sig %d %d", (int)tag, algo);
if(!dnskey_algo_id_is_supported(algo)) {
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_UNSUPPORTED_DNSKEY_ALG;
verbose(VERB_QUERY, "verify sig: unknown algorithm");
return sec_status_insecure;
}
-
+
for(i=0; i<num; i++) {
/* see if key matches keytag and algo */
if(algo != dnskey_get_algo(dnskey, i) ||
numchecked ++;
/* see if key verifies */
- sec = dnskey_verify_rrset_sig(env->scratch,
- env->scratch_buffer, ve, now, rrset, dnskey, i,
- sig_idx, sortree, &buf_canon, reason, section, qstate);
+ sec = dnskey_verify_rrset_sig(env->scratch,
+ env->scratch_buffer, ve, now, rrset, dnskey, i,
+ sig_idx, sortree, &buf_canon, reason, reason_bogus,
+ section, qstate);
if(sec == sec_status_secure)
return sec;
}
if(numchecked == 0) {
*reason = "signatures from unknown keys";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_DNSKEY_MISSING;
verbose(VERB_QUERY, "verify: could not find appropriate key");
return sec_status_bogus;
}
/** check rrsig dates */
static int
-check_dates(struct val_env* ve, uint32_t unow,
- uint8_t* expi_p, uint8_t* incep_p, char** reason)
+check_dates(struct val_env* ve, uint32_t unow, uint8_t* expi_p,
+ uint8_t* incep_p, char** reason, sldns_ede_code *reason_bogus)
{
/* read out the dates */
uint32_t expi, incep, now;
sigdate_error("verify: inception after expiration, "
"signature bad", expi, incep, now);
*reason = "signature inception after expiration";
+ if(reason_bogus){
+ /* from RFC8914 on Signature Not Yet Valid: The resolver
+ * attempted to perform DNSSEC validation, but no
+ * signatures are presently valid and at least some are
+ * not yet valid. */
+ *reason_bogus = LDNS_EDE_SIGNATURE_NOT_YET_VALID;
+ }
+
return 0;
}
if(compare_1982(incep, now) > 0) {
sigdate_error("verify: signature bad, current time is"
" before inception date", expi, incep, now);
*reason = "signature before inception date";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_SIGNATURE_NOT_YET_VALID;
return 0;
}
sigdate_error("verify warning suspicious signature inception "
sigdate_error("verify: signature expired", expi,
incep, now);
*reason = "signature expired";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_SIGNATURE_EXPIRED;
return 0;
}
sigdate_error("verify warning suspicious signature expiration "
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_ede_code *reason_bogus,
sldns_pkt_section section, struct module_qstate* qstate)
{
enum sec_status sec;
if(siglen < 2+20) {
verbose(VERB_QUERY, "verify: signature too short");
*reason = "signature too short";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
if(!(dnskey_get_flags(dnskey, dnskey_idx) & DNSKEY_BIT_ZSK)) {
verbose(VERB_QUERY, "verify: dnskey without ZSK flag");
*reason = "dnskey without ZSK flag";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_NO_ZONE_KEY_BIT_SET;
return sec_status_bogus;
}
/* RFC 4034 says DNSKEY PROTOCOL MUST be 3 */
verbose(VERB_QUERY, "verify: dnskey has wrong key protocol");
*reason = "dnskey has wrong protocolnumber";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
if(!signer_len) {
verbose(VERB_QUERY, "verify: malformed signer name");
*reason = "signer name malformed";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus; /* signer name invalid */
}
if(!dname_subdomain_c(rrset->rk.dname, signer)) {
verbose(VERB_QUERY, "verify: signer name is off-tree");
*reason = "signer name off-tree";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus; /* signer name offtree */
}
sigblock = (unsigned char*)signer+signer_len;
if(siglen < 2+18+signer_len+1) {
verbose(VERB_QUERY, "verify: too short, no signature data");
*reason = "signature too short, no signature data";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus; /* sig rdf is < 1 byte */
}
sigblock_len = (unsigned int)(siglen - 2 - 18 - signer_len);
log_nametypeclass(VERB_QUERY, "the key name is",
dnskey->rk.dname, 0, 0);
*reason = "signer name mismatches key name";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
if(memcmp(sig+2, &rrset->rk.type, 2) != 0) {
verbose(VERB_QUERY, "verify: wrong type covered");
*reason = "signature covers wrong type";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
/* verify keytag and sig algo (possibly again) */
if((int)sig[2+2] != dnskey_get_algo(dnskey, dnskey_idx)) {
verbose(VERB_QUERY, "verify: wrong algorithm");
*reason = "signature has wrong algorithm";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
ktag = htons(dnskey_calc_keytag(dnskey, dnskey_idx));
if(memcmp(sig+2+16, &ktag, 2) != 0) {
verbose(VERB_QUERY, "verify: wrong keytag");
*reason = "signature has wrong keytag";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
if((int)sig[2+3] > dname_signame_label_count(rrset->rk.dname)) {
verbose(VERB_QUERY, "verify: labelcount out of range");
*reason = "signature labelcount out of range";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
/* verify inception, expiration dates
* Do this last so that if you ignore expired-sigs the
* rest is sure to be OK. */
- if(!check_dates(ve, now, sig+2+8, sig+2+12, reason)) {
+ if(!check_dates(ve, now, sig+2+8, sig+2+12,
+ reason, reason_bogus)) {
return sec_status_bogus;
}
}
#define VALIDATOR_VAL_SIGCRYPT_H
#include "util/data/packed_rrset.h"
#include "sldns/pkthdr.h"
+#include "sldns/rrdef.h"
struct val_env;
struct module_env;
struct module_qstate;
* @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 reason_bogus: EDE (RFC8914) code paired with the reason of failure.
* @param section: section of packet where this rrset comes from.
* @param qstate: qstate with region.
* @return SECURE if one key in the set verifies one rrsig.
*/
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_ede_code *reason_bogus,
sldns_pkt_section section, struct module_qstate* qstate);
+
/**
* verify rrset against one specific dnskey (from rrset)
* @param env: module environment, scratch space is used.
* @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 reason_bogus: EDE (RFC8914) code paired with the reason of failure.
* @param section: section of packet where this rrset comes from.
* @param qstate: qstate with region.
* @return secure if *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,
+enum sec_status dnskey_verify_rrset(struct module_env* env, struct val_env* ve,
+ struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
+ size_t dnskey_idx, char** reason, sldns_ede_code *reason_bogus,
sldns_pkt_section section, struct module_qstate* qstate);
-/**
- * verify rrset, with dnskey rrset, for a specific rrsig in rrset
- * @param env: module environment, scratch space is used.
- * @param ve: validator environment, date settings.
- * @param now: current time for validation (can be overridden).
- * @param rrset: to be validated.
- * @param dnskey: DNSKEY rrset, keyset to try.
- * @param sig_idx: which signature to try to validate.
- * @param sortree: reused sorted order. Stored in region. Pass NULL at start,
- * and for a new rrset.
- * @param reason: if bogus, a string returned, fixed or alloced in scratch.
- * @param 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, sldns_pkt_section section,
- struct module_qstate* qstate);
-
/**
* verify rrset, with specific dnskey(from set), for a specific rrsig
* @param region: scratch region used for temporary allocation.
* 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 reason_bogus: EDE (8914) code paired with the reason of failure.
* @param section: section of packet where this rrset comes from.
* @param qstate: qstate with region.
* @return secure if this key signs this signature. unchecked on error or
* bogus if it did not validate.
*/
-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,
- sldns_pkt_section section, struct module_qstate* qstate);
+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, sldns_ede_code *reason_bogus,
+ sldns_pkt_section section, struct module_qstate* qstate);
/**
* canonical compare for two tree entries
return d->ttl;
}
-enum sec_status
+static 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, sldns_pkt_section section,
- struct module_qstate* qstate)
+ uint8_t* sigalg, char** reason, sldns_ede_code *reason_bogus,
+ sldns_pkt_section section, struct module_qstate* qstate)
{
enum sec_status sec;
struct packed_rrset_data* d = (struct packed_rrset_data*)rrset->
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,
- section, qstate);
+ reason_bogus, section, qstate);
verbose(VERB_ALGO, "verify result: %s", sec_status_to_string(sec));
regional_free_all(env->scratch);
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, sldns_pkt_section section, struct module_qstate* qstate)
+ char** reason, sldns_ede_code *reason_bogus,
+ sldns_pkt_section section, struct module_qstate* qstate)
{
/* temporary dnskey rrset-key */
struct ub_packed_rrset_key dnskey;
dnskey.entry.key = &dnskey;
dnskey.entry.data = kd->rrset_data;
sec = val_verify_rrset(env, ve, rrset, &dnskey, kd->algo, reason,
- section, qstate);
+ reason_bogus, section, qstate);
return sec;
}
/** verify that a DS RR hashes to a key and that key signs the set */
static enum sec_status
-verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve,
- struct ub_packed_rrset_key* dnskey_rrset,
+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 module_qstate* qstate)
+ sldns_ede_code *reason_bogus, struct module_qstate* qstate)
{
enum sec_status sec = sec_status_bogus;
size_t i, num, numchecked = 0, numhashok = 0, numsizesupp = 0;
/* 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, LDNS_SECTION_ANSWER, qstate);
+ sec = dnskey_verify_rrset(env, ve, dnskey_rrset, dnskey_rrset,
+ i, reason, reason_bogus, LDNS_SECTION_ANSWER, qstate);
if(sec == sec_status_secure) {
return sec;
}
return digest_algo;
}
-enum sec_status
+// @TODO change the use of this function to _ede function in authzone.c:8111
+enum sec_status
val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* dnskey_rrset,
struct ub_packed_rrset_key* ds_rrset, uint8_t* sigalg, char** reason,
- struct module_qstate* qstate)
+ sldns_ede_code *reason_bogus, struct module_qstate* qstate)
{
/* as long as this is false, we can consider this DS rrset to be
* equivalent to no DS rrset. */
}
sec = verify_dnskeys_with_ds_rr(env, ve, dnskey_rrset,
- ds_rrset, i, reason, qstate);
+ ds_rrset, i, reason, reason_bogus, qstate);
if(sec == sec_status_insecure)
continue;
return sec_status_bogus;
}
-struct key_entry_key*
+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 module_qstate* qstate)
+ sldns_ede_code *reason_bogus, 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, qstate);
+ enum sec_status sec = val_verify_DNSKEY_with_DS(env, ve,
+ dnskey_rrset, ds_rrset, downprot?sigalg:NULL, reason,
+ reason_bogus, qstate);
if(sec == sec_status_secure) {
return key_entry_create_rrset(region,
BOGUS_KEY_TTL, *env->now);
}
-enum sec_status
+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 module_qstate* qstate)
+ sldns_ede_code *reason_bogus, struct module_qstate* qstate)
{
/* as long as this is false, we can consider this anchor to be
* equivalent to no anchor. */
verbose(VERB_QUERY, "DNSKEY RRset did not match DS RRset "
"by name");
*reason = "DNSKEY RRset did not match DS RRset by name";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_DNSKEY_MISSING;
return sec_status_bogus;
}
if(ta_dnskey && (dnskey_rrset->rk.dname_len != ta_dnskey->rk.dname_len
verbose(VERB_QUERY, "DNSKEY RRset did not match anchor RRset "
"by name");
*reason = "DNSKEY RRset did not match anchor RRset by name";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_DNSKEY_MISSING;
return sec_status_bogus;
}
continue;
sec = verify_dnskeys_with_ds_rr(env, ve, dnskey_rrset,
- ta_ds, i, reason, qstate);
+ ta_ds, i, reason, reason_bogus, qstate);
if(sec == sec_status_insecure)
continue;
has_useful_ta = 1;
sec = dnskey_verify_rrset(env, ve, dnskey_rrset,
- ta_dnskey, i, reason, LDNS_SECTION_ANSWER, qstate);
+ ta_dnskey, i, reason, NULL, LDNS_SECTION_ANSWER, qstate);
if(sec == sec_status_secure) {
if(!sigalg || algo_needs_set_secure(&needs,
(uint8_t)dnskey_get_algo(ta_dnskey, i))) {
struct key_entry_key*
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 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, struct module_qstate* qstate)
+ char** reason, sldns_ede_code *reason_bogus, struct module_qstate* qstate)
{
uint8_t sigalg[ALGO_NEEDS_MAX+1];
- enum sec_status sec = val_verify_DNSKEY_with_TA(env, ve,
+ enum sec_status sec = val_verify_DNSKEY_with_TA(env, ve,
dnskey_rrset, ta_ds_rrset, ta_dnskey_rrset,
- downprot?sigalg:NULL, reason, qstate);
+ downprot?sigalg:NULL, reason, reason_bogus, qstate);
if(sec == sec_status_secure) {
- return key_entry_create_rrset(region,
+ return key_entry_create_rrset(region,
dnskey_rrset->rk.dname, dnskey_rrset->rk.dname_len,
ntohs(dnskey_rrset->rk.rrset_class), dnskey_rrset,
downprot?sigalg:NULL, *env->now);
} else if(sec == sec_status_insecure) {
return key_entry_create_null(region, dnskey_rrset->rk.dname,
- dnskey_rrset->rk.dname_len,
+ dnskey_rrset->rk.dname_len,
ntohs(dnskey_rrset->rk.rrset_class),
rrset_get_ttl(dnskey_rrset), *env->now);
}
BOGUS_KEY_TTL, *env->now);
}
-int
+int
val_dsset_isusable(struct ub_packed_rrset_key* ds_rrset)
{
size_t i;
if(lt) snprintf(aerr, sizeof(aerr), "%s", lt->name);
else snprintf(aerr, sizeof(aerr), "%d",
(int)ds_get_key_algo(ds_rrset, 0));
+
verbose(VERB_ALGO, "DS unsupported, hash %s %s, "
"key algorithm %s %s", herr,
(ds_digest_algo_is_supported(ds_rrset, 0)?
#define VALIDATOR_VAL_UTILS_H
#include "util/data/packed_rrset.h"
#include "sldns/pkthdr.h"
+#include "sldns/rrdef.h"
struct query_info;
struct reply_info;
struct val_env;
struct query_info* qinf, struct reply_info* rep,
size_t cname_skip, uint8_t** signer_name, size_t* signer_len);
-/**
- * Verify RRset with keys
- * @param env: module environment (scratch buffer)
- * @param ve: validator environment (verification settings)
- * @param rrset: what to verify
- * @param keys: dnskey rrset to verify with.
- * @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, sldns_pkt_section section,
- struct module_qstate* qstate);
-
/**
* Verify RRset with keys from a keyset.
* @param env: module environment (scratch buffer)
* @param rrset: what to verify
* @param kkey: key_entry to verify with.
* @param reason: reason of failure. Fixed string or alloced in scratch.
+ * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure.
* @param section: section of packet where this rrset comes from.
* @param qstate: qstate with region.
* @return 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, sldns_pkt_section section,
- struct module_qstate* qstate);
+ struct key_entry_key* kkey, char** reason, sldns_ede_code *reason_bogus,
+ sldns_pkt_section section, struct module_qstate* qstate);
/**
* Verify DNSKEYs with DS rrset. Like val_verify_new_DNSKEYs but
* 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 reason_bogus: EDE (RFC8914) code paired with the reason of failure.
* @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 module_qstate* qstate);
+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,
+ sldns_ede_code *reason_bogus, struct module_qstate* qstate);
/**
* Verify DNSKEYs with DS and DNSKEY rrset. Like val_verify_DNSKEY_with_DS
* 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 reason_bogus: EDE (RFC8914) code paired with the reason of failure.
* @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_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 module_qstate* qstate);
+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,
+ sldns_ede_code *reason_bogus, struct module_qstate* qstate);
/**
* Verify new DNSKEYs with DS rrset. The DS contains hash values that should
* @param downprot: if true provide downgrade protection otherwise one
* algorithm is enough.
* @param reason: reason of failure. Fixed string or alloced in scratch.
+ * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure.
* @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.
* if downprot is set, a key entry with an algo list is made.
*/
-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 module_qstate* qstate);
-
+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,
+ sldns_ede_code *reason_bogus, struct module_qstate* qstate);
/**
* Verify rrset with trust anchor: DS and DNSKEY rrset.
* @param downprot: if true provide downgrade protection otherwise one
* algorithm is enough.
* @param reason: reason of failure. Fixed string or alloced in scratch.
+ * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure.
* @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
* if downprot is set, a key entry with an algo list is made.
*/
struct key_entry_key* 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, struct module_qstate* qstate);
+ 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, sldns_ede_code *reason_bogus, struct module_qstate* qstate);
/**
* Determine if DS rrset is usable for validator or not.
struct val_qstate* vq, int id, int rcode, struct dns_msg* msg,
struct query_info* qinfo, struct sock_list* origin);
+
+/* Updates the suplied EDE (RFC8914) code selectively so we don't loose
+ * a more specific code
+ */
+static void
+update_reason_bogus(struct reply_info* rep, sldns_ede_code reason_bogus)
+{
+ if (rep->reason_bogus == LDNS_EDE_DNSSEC_BOGUS ||
+ rep->reason_bogus == LDNS_EDE_NONE) {
+ rep->reason_bogus = reason_bogus;
+ }
+}
+
+
/** fill up nsec3 key iterations config entry */
static int
fill_nsec3_iter(struct val_env* ve, char* s, int c)
vq->orig_msg->rep->flags = (uint16_t)(qstate->return_rcode&0xf)
|BIT_QR|BIT_RA|(qstate->query_flags|(BIT_CD|BIT_RD));
vq->orig_msg->rep->qdcount = 1;
+ vq->orig_msg->rep->reason_bogus = LDNS_EDE_NONE;
} else {
vq->orig_msg = qstate->return_msg;
}
enum sec_status sec;
int dname_seen = 0;
char* reason = NULL;
+ sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
/* validate the ANSWER section */
for(i=0; i<chase_reply->an_numrrsets; i++) {
/* Verify the answer rrset */
sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason,
- LDNS_SECTION_ANSWER, qstate);
+ &reason_bogus, LDNS_SECTION_ANSWER, qstate);
/* If the (answer) rrset failed to validate, then this
* message is BAD. */
if(sec != sec_status_secure) {
log_nametypeclass(VERB_QUERY, "validator: response "
"has failed ANSWER rrset:", s->rk.dname,
ntohs(s->rk.type), ntohs(s->rk.rrset_class));
- errinf(qstate, reason);
+ errinf_ede(qstate, reason, reason_bogus);
if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME)
errinf(qstate, "for CNAME");
else if(ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME)
errinf(qstate, "for DNAME");
errinf_origin(qstate, qstate->reply_origin);
chase_reply->security = sec_status_bogus;
+ update_reason_bogus(chase_reply, reason_bogus);
+
return 0;
}
chase_reply->ns_numrrsets; i++) {
s = chase_reply->rrsets[i];
sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason,
- LDNS_SECTION_AUTHORITY, qstate);
+ &reason_bogus, LDNS_SECTION_AUTHORITY, qstate);
/* If anything in the authority section fails to be secure,
* we have a bad message. */
if(sec != sec_status_secure) {
log_nametypeclass(VERB_QUERY, "validator: response "
"has failed AUTHORITY rrset:", s->rk.dname,
ntohs(s->rk.type), ntohs(s->rk.rrset_class));
- errinf(qstate, reason);
+ errinf_ede(qstate, reason, reason_bogus);
errinf_origin(qstate, qstate->reply_origin);
errinf_rrset(qstate, s);
chase_reply->security = sec_status_bogus;
+ update_reason_bogus(chase_reply, reason_bogus);
return 0;
}
}
/* only validate rrs that have signatures with the key */
/* leave others unchecked, those get removed later on too */
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, LDNS_SECTION_ADDITIONAL, qstate);
+ &reason, NULL, 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. */
"inconsistent wildcard sigs:", s->rk.dname,
ntohs(s->rk.type), ntohs(s->rk.rrset_class));
chase_reply->security = sec_status_bogus;
+ update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
if(wc && !wc_cached && env->cfg->aggressive_nsec) {
"expansion and did not prove original data "
"did not exist");
chase_reply->security = sec_status_bogus;
+ update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
if(verbosity >= VERB_ALGO)
log_dns_msg("Failed NODATA", qchase, chase_reply);
chase_reply->security = sec_status_bogus;
+ update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
verbose(VERB_QUERY, "NameError response has failed to prove: "
"qname does not exist");
chase_reply->security = sec_status_bogus;
+ update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
/* Be lenient with RCODE in NSEC NameError responses */
validate_nodata_response(env, ve, qchase, chase_reply, kkey);
if (chase_reply->security == sec_status_secure)
verbose(VERB_QUERY, "NameError response has failed to prove: "
"covering wildcard does not exist");
chase_reply->security = sec_status_bogus;
+ update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
/* Be lenient with RCODE in NSEC NameError responses */
validate_nodata_response(env, ve, qchase, chase_reply, kkey);
if (chase_reply->security == sec_status_secure)
if(qchase->qtype != LDNS_RR_TYPE_ANY) {
log_err("internal error: ANY validation called for non-ANY");
chase_reply->security = sec_status_bogus;
+ update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
s->rk.dname, ntohs(s->rk.type),
ntohs(s->rk.rrset_class));
chase_reply->security = sec_status_bogus;
+ update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
}
"expansion and did not prove original data "
"did not exist");
chase_reply->security = sec_status_bogus;
+ update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
"inconsistent wildcard sigs:", s->rk.dname,
ntohs(s->rk.type), ntohs(s->rk.rrset_class));
chase_reply->security = sec_status_bogus;
+ update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
"wildcarded DNAME:", s->rk.dname,
ntohs(s->rk.type), ntohs(s->rk.rrset_class));
chase_reply->security = sec_status_bogus;
+ update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
"expansion and did not prove original data "
"did not exist");
chase_reply->security = sec_status_bogus;
+ update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
verbose(VERB_QUERY, "CNAMEchain to noanswer proves that name "
"exists and not exists, bogus");
chase_reply->security = sec_status_bogus;
+ update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
if(!nodata_valid_nsec && !nxdomain_valid_nsec && nsec3s_seen) {
if(verbosity >= VERB_ALGO)
log_dns_msg("Failed CNAMEnoanswer", qchase, chase_reply);
chase_reply->security = sec_status_bogus;
+ update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
verbose(VERB_ALGO, "restart count exceeded");
return val_error(qstate, id);
}
+
+ /* correctly initialize reason_bogus */
+ update_reason_bogus(vq->chase_reply, LDNS_EDE_DNSSEC_BOGUS);
+
verbose(VERB_ALGO, "validator classification %s",
val_classification_to_string(subtype));
if(subtype == VAL_CLASS_REFERRAL &&
verbose(VERB_QUERY, "unsigned parent zone denies"
" trust anchor, indeterminate");
vq->chase_reply->security = sec_status_indeterminate;
+ update_reason_bogus(vq->chase_reply, LDNS_EDE_DNSSEC_INDETERMINATE);
vq->state = VAL_FINISHED_STATE;
return 1;
}
if(vq->key_entry == NULL && anchor == NULL) {
/*response isn't under a trust anchor, so we cannot validate.*/
vq->chase_reply->security = sec_status_indeterminate;
+ update_reason_bogus(vq->chase_reply, LDNS_EDE_DNSSEC_INDETERMINATE);
/* go to finished state to cache this result */
vq->state = VAL_FINISHED_STATE;
return 1;
vq->state = VAL_FINISHED_STATE;
return 1;
} else if(key_entry_isbad(vq->key_entry)) {
+ sldns_ede_code ede = LDNS_EDE_DNSSEC_BOGUS;
+
+ /* the key could have a more spefic EDE than just bogus */
+ if(key_entry_get_reason_bogus(vq->key_entry) != LDNS_EDE_NONE) {
+ ede = key_entry_get_reason_bogus(vq->key_entry);
+ }
+
/* key is bad, chain is bad, reply is bogus */
errinf_dname(qstate, "key for validation", vq->key_entry->name);
- errinf(qstate, "is marked as invalid");
+ errinf_ede(qstate, "is marked as invalid", ede);
if(key_entry_get_reason(vq->key_entry)) {
errinf(qstate, "because of a previous");
errinf(qstate, key_entry_get_reason(vq->key_entry));
}
+
/* no retries, stop bothering the authority until timeout */
vq->restart_count = ve->max_restart;
vq->chase_reply->security = sec_status_bogus;
+ update_reason_bogus(vq->chase_reply, ede);
vq->state = VAL_FINISHED_STATE;
return 1;
}
vq->empty_DS_name) == 0) {
/* do not query for empty_DS_name again */
verbose(VERB_ALGO, "Cannot retrieve DS for signature");
- errinf(qstate, "no signatures");
+ errinf_ede(qstate, "no signatures", LDNS_EDE_RRSIGS_MISSING);
errinf_origin(qstate, qstate->reply_origin);
vq->chase_reply->security = sec_status_bogus;
+ update_reason_bogus(vq->chase_reply, LDNS_EDE_RRSIGS_MISSING);
vq->state = VAL_FINISHED_STATE;
return 1;
}
"of trust to keys for", vq->key_entry->name,
LDNS_RR_TYPE_DNSKEY, vq->key_entry->key_class);
vq->chase_reply->security = sec_status_bogus;
- errinf(qstate, "while building chain of trust");
+
+ update_reason_bogus(vq->chase_reply, LDNS_EDE_DNSKEY_MISSING);
+ errinf_ede(qstate, "while building chain of trust",
+ LDNS_EDE_DNSKEY_MISSING);
if(vq->restart_count >= ve->max_restart)
key_cache_insert(ve->kcache, vq->key_entry, qstate);
return 1;
"signer name", &vq->qchase);
verbose(VERB_DETAIL, "Could not establish validation of "
"INSECURE status of unsigned response.");
- errinf(qstate, "no signatures");
+ errinf_ede(qstate, "no signatures", LDNS_EDE_RRSIGS_MISSING);
errinf_origin(qstate, qstate->reply_origin);
vq->chase_reply->security = sec_status_bogus;
+ update_reason_bogus(vq->chase_reply, LDNS_EDE_RRSIGS_MISSING);
return 1;
}
subtype = val_classify_response(qstate->query_flags, &qstate->qinfo,
vq->orig_msg->rep, vq->rrset_skip);
/* store overall validation result in orig_msg */
- if(vq->rrset_skip == 0)
+ if(vq->rrset_skip == 0) {
vq->orig_msg->rep->security = vq->chase_reply->security;
- else if(subtype != VAL_CLASS_REFERRAL ||
+ update_reason_bogus(vq->orig_msg->rep, vq->chase_reply->reason_bogus);
+ } else if(subtype != VAL_CLASS_REFERRAL ||
vq->rrset_skip < vq->orig_msg->rep->an_numrrsets +
vq->orig_msg->rep->ns_numrrsets) {
/* ignore sec status of additional section if a referral
* type message skips there and
* use the lowest security status as end result. */
- if(vq->chase_reply->security < vq->orig_msg->rep->security)
+ if(vq->chase_reply->security < vq->orig_msg->rep->security) {
vq->orig_msg->rep->security =
vq->chase_reply->security;
+ update_reason_bogus(vq->orig_msg->rep, vq->chase_reply->reason_bogus);
+ }
}
if(subtype == VAL_CLASS_REFERRAL) {
&vq->rrset_skip)) {
verbose(VERB_ALGO, "validator: failed to chase CNAME");
vq->orig_msg->rep->security = sec_status_bogus;
+ update_reason_bogus(vq->orig_msg->rep, LDNS_EDE_DNSSEC_BOGUS);
} else {
/* restart process for new qchase at rrset_skip */
log_query_info(VERB_ALGO, "validator: chased to",
* queries. If we get here, it is bogus or an internal error */
if(qstate->qinfo.qclass == LDNS_RR_CLASS_ANY) {
verbose(VERB_ALGO, "cannot validate classANY: bogus");
- if(qstate->return_msg)
+ if(qstate->return_msg) {
qstate->return_msg->rep->security =
sec_status_bogus;
+ update_reason_bogus(qstate->return_msg->rep, LDNS_EDE_DNSSEC_BOGUS);
+ }
qstate->ext_state[id] = module_finished;
return;
}
struct key_entry_key* kkey = NULL;
enum sec_status sec = sec_status_unchecked;
char* reason = NULL;
+ sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
int downprot = qstate->env->cfg->harden_algo_downgrade;
if(!dnskey_rrset) {
"could not fetch DNSKEY rrset",
ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass);
if(qstate->env->cfg->harden_dnssec_stripped) {
- errinf(qstate, "no DNSKEY rrset");
+ errinf_ede(qstate, "no DNSKEY rrset", LDNS_EDE_DNSKEY_MISSING);
kkey = key_entry_create_bad(qstate->region, ta->name,
ta->namelen, ta->dclass, BOGUS_KEY_TTL,
*qstate->env->now);
/* 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, qstate);
+ &reason, &reason_bogus, qstate);
if(!kkey) {
log_err("out of memory: verifying prime TA");
return NULL;
/* NOTE: in this case, we should probably reject the trust
* anchor for longer, perhaps forever. */
if(qstate->env->cfg->harden_dnssec_stripped) {
- errinf(qstate, reason);
+ errinf_ede(qstate, reason, reason_bogus);
kkey = key_entry_create_bad(qstate->region, ta->name,
ta->namelen, ta->dclass, BOGUS_KEY_TTL,
*qstate->env->now);
{
struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
char* reason = NULL;
+ sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
enum val_classification subtype;
if(rcode != LDNS_RCODE_NOERROR) {
char rc[16];
/* errors here pretty much break validation */
verbose(VERB_DETAIL, "DS response was error, thus bogus");
errinf(qstate, rc);
- errinf(qstate, "no DS");
+ errinf_ede(qstate, "no DS", LDNS_EDE_NETWORK_ERROR);
+
goto return_bogus;
}
if(!ds) {
log_warn("internal error: POSITIVE DS response was "
"missing DS.");
- errinf(qstate, "no DS record");
+ errinf_ede(qstate, "no DS record", LDNS_EDE_DNSSEC_BOGUS);
goto return_bogus;
}
/* 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, LDNS_SECTION_ANSWER, qstate);
+ sec = val_verify_rrset_entry(qstate->env, ve, ds,
+ vq->key_entry, &reason, &reason_bogus, LDNS_SECTION_ANSWER, qstate);
if(sec != sec_status_secure) {
verbose(VERB_DETAIL, "DS rrset in DS response did "
"not verify");
- errinf(qstate, reason);
+ errinf_ede(qstate, reason, reason_bogus);
goto return_bogus;
}
if(!val_dsset_isusable(ds)) {
/* If they aren't usable, then we treat it like
* there was no DS. */
+
+ // @TODO add EDE Unsupported DS Digest Type
+
*ke = key_entry_create_null(qstate->region,
qinfo->qname, qinfo->qname_len, qinfo->qclass,
ub_packed_rrset_ttl(ds), *qstate->env->now);
/* make sure there are NSECs or NSEC3s with signatures */
if(!val_has_signed_nsecs(msg->rep, &reason)) {
verbose(VERB_ALGO, "no NSECs: %s", reason);
- errinf(qstate, reason);
+ errinf_ede(qstate, reason, LDNS_EDE_NSEC_MISSING);
goto return_bogus;
}
sec = nsec3_prove_nods(qstate->env, ve,
msg->rep->rrsets + msg->rep->an_numrrsets,
msg->rep->ns_numrrsets, qinfo, vq->key_entry, &reason,
- qstate);
+ &reason_bogus, qstate);
switch(sec) {
case sec_status_insecure:
/* case insecure also continues to unsigned
case sec_status_bogus:
verbose(VERB_DETAIL, "NSEC3s for the "
"referral did not prove no DS.");
- errinf(qstate, reason);
+ errinf_ede(qstate, reason, reason_bogus);
goto return_bogus;
case sec_status_unchecked:
default:
goto return_bogus;
}
sec = val_verify_rrset_entry(qstate->env, ve, cname,
- vq->key_entry, &reason, LDNS_SECTION_ANSWER, qstate);
+ vq->key_entry, &reason, NULL, LDNS_SECTION_ANSWER, qstate);
if(sec == sec_status_secure) {
verbose(VERB_ALGO, "CNAME validated, "
"proof that DS does not exist");
struct ub_packed_rrset_key* dnskey = NULL;
int downprot;
char* reason = NULL;
+ sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
if(rcode == LDNS_RCODE_NOERROR)
dnskey = reply_find_answer_rrset(qinfo, msg->rep);
/* bad response */
verbose(VERB_DETAIL, "Missing DNSKEY RRset in response to "
"DNSKEY query.");
+
if(vq->restart_count < ve->max_restart) {
val_blacklist(&vq->chain_blacklist, qstate->region,
origin, 1);
log_err("alloc failure in missing dnskey response");
/* key_entry is NULL for failure in Validate */
}
- errinf(qstate, "No DNSKEY record");
+ errinf_ede(qstate, "No DNSKEY record", LDNS_EDE_DNSKEY_MISSING);
errinf_origin(qstate, origin);
errinf_dname(qstate, "for key", qinfo->qname);
vq->state = VAL_VALIDATE_STATE;
}
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, qstate);
+ ve, dnskey, vq->ds_rrset, downprot, &reason, &reason_bogus, qstate);
if(!vq->key_entry) {
log_err("out of memory in verify new DNSKEYs");
}
verbose(VERB_DETAIL, "Did not match a DS to a DNSKEY, "
"thus bogus.");
- errinf(qstate, reason);
+ errinf_ede(qstate, reason, reason_bogus);
errinf_origin(qstate, origin);
errinf_dname(qstate, "for key", qinfo->qname);
}
int32_t skew_max;
/** max number of query restarts, number of IPs to probe */
- int32_t max_restart;
+ int max_restart;
/** TTL for bogus data; used instead of untrusted TTL from data.
* Bogus data will not be verified more often than this interval.