iterator/iter_hints.c iterator/iter_priv.c iterator/iter_resptype.c \
iterator/iter_scrub.c iterator/iter_utils.c services/listen_dnsport.c \
services/localzone.c services/mesh.c services/modstack.c services/view.c \
-services/rpz.c \
+services/rpz.c util/rfc_1982.c \
services/outbound_list.c services/outside_network.c util/alloc.c \
util/config_file.c util/configlexer.c util/configparser.c \
util/shm_side/shm_main.c services/authzone.c \
util/fptr_wlist.c util/locks.c util/log.c util/mini_event.c util/module.c \
util/netevent.c util/net_help.c util/random.c util/rbtree.c util/regional.c \
-util/rtt.c util/edns.c util/storage/dnstree.c util/storage/lookup3.c \
+util/rtt.c util/siphash.c util/edns.c util/storage/dnstree.c util/storage/lookup3.c \
util/storage/lruhash.c util/storage/slabhash.c util/tcp_conn_limit.c \
-util/timehist.c util/tube.c util/proxy_protocol.c \
+util/timehist.c util/tube.c util/proxy_protocol.c util/timeval_func.c \
util/ub_event.c util/ub_event_pluggable.c util/winsock_event.c \
validator/autotrust.c validator/val_anchor.c validator/validator.c \
validator/val_kcache.c validator/val_kentry.c validator/val_neg.c \
iter_donotq.lo iter_fwd.lo iter_hints.lo iter_priv.lo iter_resptype.lo \
iter_scrub.lo iter_utils.lo localzone.lo mesh.lo modstack.lo view.lo \
outbound_list.lo alloc.lo config_file.lo configlexer.lo configparser.lo \
-fptr_wlist.lo edns.lo locks.lo log.lo mini_event.lo module.lo net_help.lo \
+fptr_wlist.lo siphash.lo edns.lo locks.lo log.lo mini_event.lo module.lo net_help.lo \
random.lo rbtree.lo regional.lo rtt.lo dnstree.lo lookup3.lo lruhash.lo \
slabhash.lo tcp_conn_limit.lo timehist.lo tube.lo winsock_event.lo \
-autotrust.lo val_anchor.lo rpz.lo proxy_protocol.lo \
+autotrust.lo val_anchor.lo rpz.lo rfc_1982.lo proxy_protocol.lo \
validator.lo val_kcache.lo val_kentry.lo val_neg.lo val_nsec3.lo val_nsec.lo \
val_secalgo.lo val_sigcrypt.lo val_utils.lo dns64.lo $(CACHEDB_OBJ) authzone.lo \
$(SUBNET_OBJ) $(PYTHONMOD_OBJ) $(CHECKLOCK_OBJ) $(DNSTAP_OBJ) $(DNSCRYPT_OBJ) \
-$(IPSECMOD_OBJ) $(IPSET_OBJ) $(DYNLIBMOD_OBJ) respip.lo
+$(IPSECMOD_OBJ) $(IPSET_OBJ) $(DYNLIBMOD_OBJ) respip.lo timeval_func.lo
COMMON_OBJ_WITHOUT_UB_EVENT=$(COMMON_OBJ_WITHOUT_NETCALL) netevent.lo listen_dnsport.lo \
outside_network.lo
COMMON_OBJ=$(COMMON_OBJ_WITHOUT_UB_EVENT) ub_event.lo
CHECKCONF_OBJ_LINK=$(CHECKCONF_OBJ) $(COMMON_OBJ_ALL_SYMBOLS) $(SLDNS_OBJ) \
$(COMPAT_OBJ) @WIN_CHECKCONF_OBJ_LINK@
CONTROL_SRC=smallapp/unbound-control.c
-CONTROL_OBJ=unbound-control.lo
+CONTROL_OBJ=unbound-control.lo
CONTROL_OBJ_LINK=$(CONTROL_OBJ) worker_cb.lo $(COMMON_OBJ_ALL_SYMBOLS) \
$(SLDNS_OBJ) $(COMPAT_OBJ) @WIN_CONTROL_OBJ_LINK@
HOST_SRC=smallapp/unbound-host.c
dynlibmod.lo dynlibdmod.o: $(srcdir)/dynlibmod/dynlibmod.c config.h $(srcdir)/dynlibmod/dynlibmod.h
cachedb.lo cachedb.o: $(srcdir)/cachedb/cachedb.c config.h $(srcdir)/cachedb/cachedb.h
redis.lo redis.o: $(srcdir)/cachedb/redis.c config.h $(srcdir)/cachedb/redis.h
+timeval_func.lo timeval_func.o: $(srcdir)/util/timeval_func.c $(srcdir)/util/timeval_func.h
# dnscrypt
dnscrypt.lo dnscrypt.o: $(srcdir)/dnscrypt/dnscrypt.c config.h \
echo "#include \"util/configyyrename.h\"" >> $@ ;\
$(LEX) -t $(srcdir)/util/configlexer.lex >> $@ ;\
fi
+ @if test ! -f $@; then echo "No $@ : need flex and bison to compile from source repository"; exit 1; fi
util/configparser.c util/configparser.h: $(srcdir)/util/configparser.y
@-if test ! -d util; then mkdir -p util; fi
rm -f doc/example.conf doc/libunbound.3 doc/unbound-anchor.8 doc/unbound-checkconf.8 doc/unbound-control.8 doc/unbound.8 doc/unbound.conf.5 doc/unbound-host.1
rm -f smallapp/unbound-control-setup.sh dnstap/dnstap_config.h dnscrypt/dnscrypt_config.h contrib/libunbound.pc contrib/unbound.socket contrib/unbound.service
rm -f $(TEST_BIN)
- rm -f Makefile
+ rm -f Makefile
maintainer-clean: distclean
rm -f util/configlexer.c util/configparser.c util/configparser.h
$(INSTALL) -c -m 644 doc/unbound.conf.5 $(DESTDIR)$(mandir)/man5
$(INSTALL) -c -m 644 doc/unbound-host.1 $(DESTDIR)$(mandir)/man1
$(INSTALL) -c -m 755 unbound-control-setup $(DESTDIR)$(sbindir)/unbound-control-setup
- if test ! -e $(DESTDIR)$(configfile); then $(INSTALL) -d `dirname $(DESTDIR)$(configfile)`; $(INSTALL) -c -m 644 doc/example.conf $(DESTDIR)$(configfile); fi
+ if test ! -e "$(DESTDIR)$(configfile)"; then $(INSTALL) -d `dirname "$(DESTDIR)$(configfile)"`; $(INSTALL) -c -m 644 doc/example.conf "$(DESTDIR)$(configfile)"; fi
pythonmod-uninstall:
rm -f -- $(DESTDIR)$(PYTHON_SITE_PKG)/unboundmodule.py
rm -f -- $(DESTDIR)$(includedir)/unbound.h
$(LIBTOOL) --mode=uninstall rm -f $(DESTDIR)$(libdir)/libunbound.la
@echo
- @echo "You still need to remove "`dirname $(DESTDIR)$(configfile)`" , $(DESTDIR)$(configfile) by hand"
+ @echo "You still need to remove "`dirname "$(DESTDIR)$(configfile)"`" , $(DESTDIR)$(configfile) by hand"
iana_update:
curl -o port-numbers.tmp https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml --compressed
- if file port-numbers.tmp | grep 'gzip' >/dev/null; then zcat port-numbers.tmp; else cat port-numbers.tmp; fi | awk '/<record>/ {p=0;} /<protocol>udp/ {p=1;} /<protocol>[^u]/ {p=0;} /Decomissioned|Decommissioned|Removed|De-registered|unassigned|Unassigned|Reserved/ {u=1;} /<number>/ { if(u==1) {u=0;} else { if(p==1) { match($$0,/[0-9]+/); print substr($$0, RSTART, RLENGTH) ","}}}' | sort -nu > util/iana_ports.inc
+ if file port-numbers.tmp | grep 'gzip' >/dev/null; then zcat port-numbers.tmp; else cat port-numbers.tmp; fi | awk '/<record>/ {p=0;} /<protocol>udp/ {p=1;} /<protocol>[^u]/ {p=0;} /Decomissioned|Decommissioned|Removed|De-registered|unassigned|Unassigned|Reserved/ {u=1;} /<number>/ { if(u==1) {u=0;} else { if(p==1) { match($$0,/[0-9]+/); print substr($$0, RSTART, RLENGTH) ","}}}' | sort -nu > util/iana_ports.inc
rm -f port-numbers.tmp
# dependency generation
outbound_list.lo outbound_list.o: $(srcdir)/services/outbound_list.c config.h \
$(srcdir)/services/outbound_list.h $(srcdir)/services/outside_network.h $(srcdir)/util/rbtree.h \
$(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
-
+
outside_network.lo outside_network.o: $(srcdir)/services/outside_network.c config.h \
$(srcdir)/services/outside_network.h $(srcdir)/util/rbtree.h $(srcdir)/util/netevent.h \
$(srcdir)/dnscrypt/dnscrypt.h \
configlexer.lo configlexer.o: util/configlexer.c config.h $(srcdir)/util/configyyrename.h \
$(srcdir)/util/config_file.h util/configparser.h
configparser.lo configparser.o: util/configparser.c config.h $(srcdir)/util/configyyrename.h \
- $(srcdir)/util/config_file.h $(srcdir)/util/net_help.h $(srcdir)/util/log.h
+ $(srcdir)/util/config_file.h $(srcdir)/util/net_help.h $(srcdir)/util/log.h $(srcdir)/sldns/str2wire.h \
+ $(srcdir)/sldns/rrdef.h
shm_main.lo shm_main.o: $(srcdir)/util/shm_side/shm_main.c config.h $(srcdir)/util/shm_side/shm_main.h \
$(srcdir)/libunbound/unbound.h $(srcdir)/daemon/daemon.h $(srcdir)/util/locks.h $(srcdir)/util/log.h \
$(srcdir)/util/alloc.h $(srcdir)/services/modstack.h \
$(srcdir)/services/view.h $(srcdir)/util/config_file.h $(srcdir)/services/authzone.h $(srcdir)/respip/respip.h \
$(srcdir)/services/cache/rrset.h $(srcdir)/util/storage/slabhash.h $(srcdir)/services/cache/infra.h \
$(srcdir)/util/rtt.h $(srcdir)/validator/validator.h $(srcdir)/validator/val_utils.h $(srcdir)/util/fptr_wlist.h \
- $(srcdir)/util/tube.h
+ $(srcdir)/util/tube.h $(srcdir)/util/timeval_func.h
authzone.lo authzone.o: $(srcdir)/services/authzone.c config.h $(srcdir)/services/authzone.h \
$(srcdir)/util/rbtree.h $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/services/mesh.h $(srcdir)/util/netevent.h \
$(srcdir)/dnscrypt/dnscrypt.h $(srcdir)/util/data/msgparse.h \
$(srcdir)/services/modstack.h $(srcdir)/services/rpz.h $(srcdir)/services/localzone.h $(srcdir)/services/view.h \
$(srcdir)/sldns/sbuffer.h $(srcdir)/util/config_file.h $(srcdir)/services/authzone.h $(srcdir)/daemon/stats.h \
$(srcdir)/util/timehist.h $(srcdir)/libunbound/unbound.h $(srcdir)/respip/respip.h $(srcdir)/sldns/str2wire.h \
- $(srcdir)/dnstap/dnstap.h $(srcdir)/services/listen_dnsport.h
+ $(srcdir)/dnstap/dnstap.h $(srcdir)/services/listen_dnsport.h $(srcdir)/util/timeval_func.h
proxy_protocol.lo proxy_protocol.o: $(srcdir)/util/proxy_protocol.c config.h \
$(srcdir)/util/proxy_protocol.h $(srcdir)/sldns/sbuffer.h
net_help.lo net_help.o: $(srcdir)/util/net_help.c config.h $(srcdir)/util/net_help.h $(srcdir)/util/log.h \
$(srcdir)/services/outbound_list.h $(srcdir)/util/data/msgreply.h $(srcdir)/util/storage/lruhash.h \
$(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/module.h \
$(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h
+siphash.lo siphash.o: $(srcdir)/util/siphash.c
+rfc_1982.lo rfc_1982.o: $(srcdir)/util/rfc_1982.c
edns.lo edns.o: $(srcdir)/util/edns.c config.h $(srcdir)/util/edns.h $(srcdir)/util/storage/dnstree.h \
$(srcdir)/util/rbtree.h $(srcdir)/util/config_file.h $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/util/net_help.h $(srcdir)/util/log.h $(srcdir)/util/regional.h \
$(srcdir)/util/storage/dnstree.h $(srcdir)/util/rbtree.h $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/util/random.h $(srcdir)/respip/respip.h \
$(srcdir)/services/localzone.h $(srcdir)/services/view.h $(srcdir)/sldns/sbuffer.h \
- $(srcdir)/services/outside_network.h
+ $(srcdir)/services/outside_network.h
unitmsgparse.lo unitmsgparse.o: $(srcdir)/testcode/unitmsgparse.c config.h $(srcdir)/util/log.h \
$(srcdir)/testcode/unitmain.h $(srcdir)/util/data/msgparse.h $(srcdir)/util/storage/lruhash.h \
$(srcdir)/util/locks.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/data/msgreply.h \
worker.lo worker.o: $(srcdir)/daemon/worker.c config.h $(srcdir)/util/log.h $(srcdir)/util/net_help.h \
$(srcdir)/util/random.h $(srcdir)/daemon/worker.h $(srcdir)/libunbound/worker.h $(srcdir)/sldns/sbuffer.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h \
- $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
+ $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h $(srcdir)/util/timeval_func.h \
$(srcdir)/util/alloc.h $(srcdir)/util/data/msgreply.h $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h \
$(srcdir)/sldns/rrdef.h $(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h $(srcdir)/libunbound/unbound.h \
$(srcdir)/util/module.h $(srcdir)/dnstap/dnstap.h $(srcdir)/daemon/daemon.h \
$(srcdir)/daemon/remote.h $(srcdir)/libunbound/worker.h $(srcdir)/sldns/sbuffer.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h \
$(srcdir)/util/config_file.h $(srcdir)/sldns/keyraw.h $(srcdir)/daemon/unbound.c $(srcdir)/daemon/daemon.h \
- $(srcdir)/util/alloc.h $(srcdir)/services/modstack.h \
+ $(srcdir)/util/alloc.h $(srcdir)/util/timeval_func.h $(srcdir)/services/modstack.h \
$(srcdir)/util/storage/slabhash.h $(srcdir)/services/listen_dnsport.h $(srcdir)/services/cache/rrset.h \
$(srcdir)/services/cache/infra.h $(srcdir)/util/storage/dnstree.h $(srcdir)/util/rtt.h \
$(srcdir)/util/data/msgreply.h $(srcdir)/util/fptr_wlist.h $(srcdir)/util/module.h \
worker.lo worker.o: $(srcdir)/daemon/worker.c config.h $(srcdir)/util/log.h $(srcdir)/util/net_help.h \
$(srcdir)/util/random.h $(srcdir)/daemon/worker.h $(srcdir)/libunbound/worker.h $(srcdir)/sldns/sbuffer.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h \
- $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
+ $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h $(srcdir)/util/timeval_func.h \
$(srcdir)/util/alloc.h $(srcdir)/util/data/msgreply.h $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h \
$(srcdir)/sldns/rrdef.h $(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h $(srcdir)/libunbound/unbound.h \
$(srcdir)/util/module.h $(srcdir)/dnstap/dnstap.h $(srcdir)/daemon/daemon.h \
$(srcdir)/validator/val_kcache.h $(srcdir)/validator/val_neg.h
replay.lo replay.o: $(srcdir)/testcode/replay.c config.h $(srcdir)/util/log.h $(srcdir)/util/net_help.h \
$(srcdir)/util/config_file.h $(srcdir)/testcode/replay.h $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
- $(srcdir)/testcode/testpkts.h $(srcdir)/util/rbtree.h \
+ $(srcdir)/testcode/testpkts.h $(srcdir)/util/rbtree.h $(srcdir)/util/timeval_func.h \
$(srcdir)/testcode/fake_event.h $(srcdir)/sldns/str2wire.h $(srcdir)/sldns/rrdef.h
fake_event.lo fake_event.o: $(srcdir)/testcode/fake_event.c config.h $(srcdir)/testcode/fake_event.h \
$(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/util/locks.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/data/msgreply.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgencode.h $(srcdir)/util/data/dname.h \
$(srcdir)/util/edns.h $(srcdir)/util/storage/dnstree.h $(srcdir)/util/rbtree.h $(srcdir)/util/config_file.h \
- $(srcdir)/services/listen_dnsport.h $(srcdir)/services/outside_network.h \
+ $(srcdir)/services/listen_dnsport.h $(srcdir)/services/outside_network.h $(srcdir)/util/timeval_func.h \
$(srcdir)/services/cache/infra.h $(srcdir)/util/rtt.h \
$(srcdir)/testcode/replay.h $(srcdir)/testcode/testpkts.h $(srcdir)/util/fptr_wlist.h $(srcdir)/util/module.h \
$(srcdir)/util/tube.h $(srcdir)/services/mesh.h $(srcdir)/services/modstack.h $(srcdir)/services/rpz.h \
# Unbound
-[![Travis Build Status](https://travis-ci.org/NLnetLabs/unbound.svg?branch=master)](https://travis-ci.org/NLnetLabs/unbound)
+[![Github Build Status](https://github.com/NLnetLabs/unbound/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/NLnetLabs/unbound/actions)
[![Packaging status](https://repology.org/badge/tiny-repos/unbound.svg)](https://repology.org/project/unbound/versions)
[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/unbound.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:unbound)
[![Documentation Status](https://readthedocs.org/projects/unbound/badge/?version=latest)](https://unbound.readthedocs.io/en/latest/?badge=latest)
+[![Mastodon Follow](https://img.shields.io/mastodon/follow/109262826617293067?domain=https%3A%2F%2Ffosstodon.org&style=social)](https://fosstodon.org/@nlnetlabs)
Unbound is a validating, recursive, caching DNS resolver. It is designed to be
fast and lean and incorporates modern features based on open standards. If you
# Copyright 2009, Wouter Wijngaards, NLnet Labs.
# BSD licensed.
#
-# Version 44
+# Version 46
+# 2023-05-04 fix to remove unused whitespace.
+# 2023-01-26 fix -Wstrict-prototypes.
# 2022-09-01 fix checking if nonblocking sockets work on OpenBSD.
# 2021-08-17 fix sed script in ssldir split handling.
# 2021-08-17 fix for openssl to detect split version, with ssldir_include
AC_CACHE_VAL(cv_prog_cc_flag_needed_$cache,
[
echo '$2' > conftest.c
-echo 'void f(){}' >>conftest.c
+echo 'void f(void){}' >>conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS $ERRFLAG -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_needed_$cache=no"
else
AC_DEFUN([ACX_DEPFLAG],
[
AC_MSG_CHECKING([$CC dependency flag])
-echo 'void f(){}' >conftest.c
+echo 'void f(void){}' >conftest.c
if test "`$CC -MM conftest.c 2>&1`" = "conftest.o: conftest.c"; then
DEPFLAG="-MM"
else
#include <getopt.h>
#endif
-int test() {
+int test(void) {
int a;
char **opts = NULL;
struct timeval tv;
#include <getopt.h>
#endif
-int test() {
+int test(void) {
int a;
char **opts = NULL;
struct timeval tv;
[
#include <stdbool.h>
#include <ctype.h>
-int test() {
+int test(void) {
int a = 0;
return a;
}
[
#include <ctype.h>
-int test() {
+int test(void) {
int a;
a = isascii(32);
return a;
[
#include <netinet/in.h>
-int test() {
+int test(void) {
struct in6_pktinfo inf;
int a = (int)sizeof(inf);
return a;
[
#include <unistd.h>
-int test() {
+int test(void) {
int a = setresgid(0,0,0);
a = setresuid(0,0,0);
return a;
#endif
#include <netdb.h>
-int test() {
+int test(void) {
int a = 0;
char *t;
time_t time = 0;
#include <getopt.h>
#endif
-int test() {
+int test(void) {
int a;
char **opts = NULL;
struct timeval tv;
dnl Setup ATTR_FORMAT config.h parts.
dnl make sure you call ACX_CHECK_FORMAT_ATTRIBUTE also.
AC_DEFUN([AHX_CONFIG_FORMAT_ATTRIBUTE],
-[
+[
#ifdef HAVE_ATTR_FORMAT
# define ATTR_FORMAT(archetype, string_index, first_to_check) \
__attribute__ ((format (archetype, string_index, first_to_check)))
ACX_CHECK_COMPILER_FLAG_NEEDED(-D_LARGEFILE_SOURCE=1,
[
#include <stdio.h>
-int test() {
+int test(void) {
int a = fseeko(stdin, 0, 0);
return a;
}
#ifdef __cplusplus
}
#endif
-int main() {
+int main(void) {
;
return 0;
}
AC_CACHE_VAL(cv_cc_deprecated_$cache,
[
echo '$3' >conftest.c
-echo 'void f(){ $2 }' >>conftest.c
+echo 'void f(void){ $2 }' >>conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS -c conftest.c 2>&1 | grep -e deprecated -e unavailable`"; then
eval "cv_cc_deprecated_$cache=no"
else
#ifdef HAVE_WINSOCK2_H
#define FD_SET_T (u_int)
#else
-#define FD_SET_T
+#define FD_SET_T
#endif
])
AC_DEFUN([AHX_CONFIG_FLAG_OMITTED],
[#if defined($1) && !defined($2)
#define $2 $3
-[#]endif ])
+[#]endif])
dnl Wrapper for AHX_CONFIG_FLAG_OMITTED for -D style flags
dnl $1: the -DNAME or -DNAME=value string.
PYTHON_VERSION=`$PYTHON -c "import sys; \
print(sys.version.split()[[0]])"`
fi
+ # calculate the version number components.
+ [
+ v="$PYTHON_VERSION"
+ PYTHON_VERSION_MAJOR=`echo $v | sed 's/[^0-9].*//'`
+ if test -z "$PYTHON_VERSION_MAJOR"; then PYTHON_VERSION_MAJOR="0"; fi
+ v=`echo $v | sed -e 's/^[0-9]*$//' -e 's/[0-9]*[^0-9]//'`
+ PYTHON_VERSION_MINOR=`echo $v | sed 's/[^0-9].*//'`
+ if test -z "$PYTHON_VERSION_MINOR"; then PYTHON_VERSION_MINOR="0"; fi
+ v=`echo $v | sed -e 's/^[0-9]*$//' -e 's/[0-9]*[^0-9]//'`
+ PYTHON_VERSION_PATCH=`echo $v | sed 's/[^0-9].*//'`
+ if test -z "$PYTHON_VERSION_PATCH"; then PYTHON_VERSION_PATCH="0"; fi
+ ]
- # Check if you have sysconfig
- AC_MSG_CHECKING([for the sysconfig Python module])
- if ac_sysconfig_result=`$PYTHON -c "import sysconfig" 2>&1`; then
+ # For some systems, sysconfig exists, but has the wrong paths,
+ # on Debian 10, for python 2.7 and 3.7. So, we check the version,
+ # and for older versions try distutils.sysconfig first. For newer
+ # versions>=3.10, where distutils.sysconfig is deprecated, use
+ # sysconfig first and then attempt the other one.
+ py_distutils_first="no"
+ if test $PYTHON_VERSION_MAJOR -lt 3; then
+ py_distutils_first="yes"
+ fi
+ if test $PYTHON_VERSION_MAJOR -eq 3 -a $PYTHON_VERSION_MINOR -lt 10; then
+ py_distutils_first="yes"
+ fi
+
+ # Check if you have the first module
+ if test "$py_distutils_first" = "yes"; then m="distutils"; else m="sysconfig"; fi
+ sysconfig_module=""
+ AC_MSG_CHECKING([for the $m Python module])
+ if ac_modulecheck_result1=`$PYTHON -c "import $m" 2>&1`; then
AC_MSG_RESULT([yes])
- sysconfig_module="sysconfig"
- # if yes, use sysconfig, because distutils is deprecated.
+ sysconfig_module="$m"
else
AC_MSG_RESULT([no])
- # if no, try to use distutils
+ 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
+ # if not found, try the other one.
+ if test -z "$sysconfig_module"; then
+ if test "$py_distutils_first" = "yes"; then m2="sysconfig"; else m2="distutils"; fi
+ AC_MSG_CHECKING([for the $m2 Python module])
+ if ac_modulecheck_result2=`$PYTHON -c "import $m2" 2>&1`; then
AC_MSG_RESULT([yes])
+ sysconfig_module="$m2"
else
AC_MSG_RESULT([no])
- AC_MSG_ERROR([cannot import Python module "distutils".
- Please check your Python installation. The error was:
- $ac_distutils_result])
+ AC_MSG_ERROR([cannot import Python module "$m", or "$m2".
+ Please check your Python installation. The errors are:
+ $m
+ $ac_modulecheck_result1
+ $m2
+ $ac_modulecheck_result2])
PYTHON_VERSION=""
fi
-
- sysconfig_module="distutils.sysconfig"
fi
+ if test "$sysconfig_module" = "distutils"; then sysconfig_module="distutils.sysconfig"; fi
#
# Check for Python include path
testframe_init(struct module_env* env, struct cachedb_env* cachedb_env)
{
struct testframe_moddata* d;
- (void)env;
verbose(VERB_ALGO, "testframe_init");
d = (struct testframe_moddata*)calloc(1,
sizeof(struct testframe_moddata));
log_err("out of memory");
return 0;
}
+ /* Register an EDNS option (65534) to bypass the worker cache lookup
+ * for testing */
+ if(!edns_register_option(LDNS_EDNS_UNBOUND_CACHEDB_TESTFRAME_TEST,
+ 1 /* bypass cache */,
+ 0 /* no aggregation */, env)) {
+ log_err("testframe_init, could not register test opcode");
+ free(d);
+ return 0;
+ }
lock_basic_init(&d->lock);
lock_protect(&d->lock, d, sizeof(*d));
return 1;
cachedb_apply_cfg(struct cachedb_env* cachedb_env, struct config_file* cfg)
{
const char* backend_str = cfg->cachedb_backend;
+ if(!backend_str || *backend_str==0)
+ return 1;
cachedb_env->backend = cachedb_find_backend(backend_str);
if(!cachedb_env->backend) {
log_err("cachedb: cannot find backend name '%s'", backend_str);
return 1;
}
-int
+int
cachedb_init(struct module_env* env, int id)
{
struct cachedb_env* cachedb_env = (struct cachedb_env*)calloc(1,
return 1;
}
-void
+void
cachedb_deinit(struct module_env* env, int id)
{
struct cachedb_env* cachedb_env;
if(!env || !env->modinfo[id])
return;
cachedb_env = (struct cachedb_env*)env->modinfo[id];
- /* free contents */
- /* TODO */
if(cachedb_env->enabled) {
(*cachedb_env->backend->deinit)(env, cachedb_env);
}
-
free(cachedb_env);
env->modinfo[id] = NULL;
}
if(!qstate->return_msg || !qstate->return_msg->rep)
return 0;
+ /* do not store failures like SERVFAIL in the cachedb, this avoids
+ * overwriting expired, valid, content with broken content. */
+ if(FLAGS_GET_RCODE(qstate->return_msg->rep->flags) !=
+ LDNS_RCODE_NOERROR &&
+ FLAGS_GET_RCODE(qstate->return_msg->rep->flags) !=
+ LDNS_RCODE_NXDOMAIN &&
+ FLAGS_GET_RCODE(qstate->return_msg->rep->flags) !=
+ LDNS_RCODE_YXDOMAIN)
+ return 0;
/* We don't store the reply if its TTL is 0 unless serve-expired is
* enabled. Such a reply won't be reusable and simply be a waste for
* the backend. It's also compatible with the default behavior of
if(qstate->return_msg->rep->ttl == 0 &&
!qstate->env->cfg->serve_expired)
return 0;
+
+ /* The EDE is added to the out-list so it is encoded in the cached message */
+ if (qstate->env->cfg->ede && qstate->return_msg->rep->reason_bogus != LDNS_EDE_NONE) {
+ edns_opt_list_append_ede(&edns.opt_list_out, qstate->env->scratch,
+ qstate->return_msg->rep->reason_bogus,
+ qstate->return_msg->rep->reason_bogus_str);
+ }
+
if(verbosity >= VERB_ALGO)
log_dns_msg("cachedb encoding", &qstate->return_msg->qinfo,
qstate->return_msg->rep);
{
struct msg_parse* prs;
struct edns_data edns;
+ struct edns_option* ede;
uint64_t timestamp, expiry;
time_t adjust;
size_t lim = sldns_buffer_limit(buf);
if(!qstate->return_msg)
return 0;
+ /* We find the EDE in the in-list after parsing */
+ if(qstate->env->cfg->ede &&
+ (ede = edns_opt_list_find(edns.opt_list_in, LDNS_EDNS_EDE))) {
+ if(ede->opt_len >= 2) {
+ qstate->return_msg->rep->reason_bogus =
+ sldns_read_uint16(ede->opt_data);
+ }
+ /* allocate space and store the error string and it's size */
+ if(ede->opt_len > 2) {
+ size_t ede_len = ede->opt_len - 2;
+ qstate->return_msg->rep->reason_bogus_str = regional_alloc(
+ qstate->region, sizeof(char) * (ede_len+1));
+ memcpy(qstate->return_msg->rep->reason_bogus_str,
+ ede->opt_data+2, ede_len);
+ qstate->return_msg->rep->reason_bogus_str[ede_len] = 0;
+ }
+ }
+
qstate->return_rcode = LDNS_RCODE_NOERROR;
/* see how much of the TTL expired, and remove it */
verbose(VERB_ALGO, "cachedb msg expired");
/* If serve-expired is enabled, we still use an expired message
* setting the TTL to 0. */
- if(qstate->env->cfg->serve_expired)
- adjust = -1;
- else
+ if(!qstate->env->cfg->serve_expired ||
+ (FLAGS_GET_RCODE(qstate->return_msg->rep->flags)
+ != LDNS_RCODE_NOERROR &&
+ FLAGS_GET_RCODE(qstate->return_msg->rep->flags)
+ != LDNS_RCODE_NXDOMAIN &&
+ FLAGS_GET_RCODE(qstate->return_msg->rep->flags)
+ != LDNS_RCODE_YXDOMAIN))
return 0; /* message expired */
+ else
+ adjust = -1;
}
verbose(VERB_ALGO, "cachedb msg adjusted down by %d", (int)adjust);
adjust_msg_ttl(qstate->return_msg, adjust);
* See if unbound's internal cache can answer the query
*/
static int
-cachedb_intcache_lookup(struct module_qstate* qstate)
+cachedb_intcache_lookup(struct module_qstate* qstate, struct cachedb_env* cde)
{
uint8_t* dpname=NULL;
size_t dpnamelen=0;
struct dns_msg* msg;
+ /* for testframe bypass this lookup */
+ if(cde->backend == &testframe_backend) {
+ return 0;
+ }
if(iter_stub_fwd_no_cache(qstate, &qstate->qinfo,
&dpname, &dpnamelen))
return 0; /* no cache for these queries */
struct cachedb_qstate* ATTR_UNUSED(iq),
struct cachedb_env* ie, int id)
{
+ qstate->is_cachedb_answer = 0;
/* check if we are enabled, and skip if so */
if(!ie->enabled) {
/* pass request to next module */
/* lookup inside unbound's internal cache.
* This does not look for expired entries. */
- if(cachedb_intcache_lookup(qstate)) {
+ if(cachedb_intcache_lookup(qstate, ie)) {
if(verbosity >= VERB_ALGO) {
if(qstate->return_msg->rep)
log_dns_msg("cachedb internal cache lookup",
qstate->ext_state[id] = module_wait_module;
return;
}
+ qstate->is_cachedb_answer = 1;
/* we are done with the query */
qstate->ext_state[id] = module_finished;
return;
cachedb_handle_response(struct module_qstate* qstate,
struct cachedb_qstate* ATTR_UNUSED(iq), struct cachedb_env* ie, int id)
{
+ qstate->is_cachedb_answer = 0;
/* check if we are not enabled or instructed to not cache, and skip */
if(!ie->enabled || qstate->no_cache_store) {
/* we are done with the query */
#! /bin/sh
# Attempt to guess a canonical system name.
-# Copyright 1992-2022 Free Software Foundation, Inc.
+# Copyright 1992-2023 Free Software Foundation, Inc.
# shellcheck disable=SC2006,SC2268 # see below for rationale
-timestamp='2022-09-17'
+timestamp='2023-07-20'
# 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
usage="\
Usage: $0 [OPTION]
-Output the configuration name of the system \`$me' is run on.
+Output the configuration name of the system '$me' is run on.
Options:
-h, --help print this help, then exit
GNU config.guess ($timestamp)
Originally written by Per Bothner.
-Copyright 1992-2022 Free Software Foundation, Inc.
+Copyright 1992-2023 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
help="
-Try \`$me --help' for more information."
+Try '$me --help' for more information."
# Parse command line
while test $# -gt 0 ; do
# temporary files to be created and, as you can see below, it is a
# headache to deal with in a portable fashion.
-# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
-# use `HOST_CC' if defined, but it is deprecated.
+# Historically, 'CC_FOR_BUILD' used to be named 'HOST_CC'. We still
+# use 'HOST_CC' if defined, but it is deprecated.
# Portable tmp directory creation inspired by the Autoconf team.
UNAME_RELEASE=`uname -v`
;;
esac
- # Japanese Language versions have a version number like `4.1.3-JL'.
+ # Japanese Language versions have a version number like '4.1.3-JL'.
SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'`
GUESS=sparc-sun-sunos$SUN_REL
;;
GUESS=$UNAME_MACHINE-unknown-minix
;;
aarch64:Linux:*:*)
- GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+ set_cc_for_build
+ CPU=$UNAME_MACHINE
+ LIBCABI=$LIBC
+ if test "$CC_FOR_BUILD" != no_compiler_found; then
+ ABI=64
+ sed 's/^ //' << EOF > "$dummy.c"
+ #ifdef __ARM_EABI__
+ #ifdef __ARM_PCS_VFP
+ ABI=eabihf
+ #else
+ ABI=eabi
+ #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
+ eabi | eabihf) CPU=armv8l; LIBCABI=$LIBC$ABI ;;
+ esac
+ fi
+ GUESS=$CPU-unknown-linux-$LIBCABI
;;
aarch64_be:Linux:*:*)
UNAME_MACHINE=aarch64_be
k1om:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
+ kvx:Linux:*:*)
+ GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+ ;;
+ kvx:cos:*:*)
+ GUESS=$UNAME_MACHINE-unknown-cos
+ ;;
+ kvx:mbr:*:*)
+ GUESS=$UNAME_MACHINE-unknown-mbr
+ ;;
loongarch32:Linux:*:* | loongarch64:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION
;;
i*86:OS/2:*:*)
- # If we were able to find `uname', then EMX Unix compatibility
+ # If we were able to find 'uname', then EMX Unix compatibility
# is probably installed.
GUESS=$UNAME_MACHINE-pc-os2-emx
;;
GUESS=ns32k-sni-sysv
fi
;;
- PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ PENTIUM:*:4.0*:*) # Unisys 'ClearPath HMP IX 4000' SVR4/MP effort
# says <Richard.M.Bartel@ccMail.Census.GOV>
GUESS=i586-unisys-sysv4
;;
/* Define if we have LibreSSL */
#undef HAVE_LIBRESSL
+/* Define to 1 if you have the <linux/net_tstamp.h> header file. */
+#undef HAVE_LINUX_NET_TSTAMP_H
+
/* Define to 1 if you have the `localtime_r' function. */
#undef HAVE_LOCALTIME_R
#if defined(OMITTED__D_GNU_SOURCE) && !defined(_GNU_SOURCE)
#define _GNU_SOURCE 1
-#endif
+#endif
#if defined(OMITTED__D_BSD_SOURCE) && !defined(_BSD_SOURCE)
#define _BSD_SOURCE 1
-#endif
+#endif
#if defined(OMITTED__D_DEFAULT_SOURCE) && !defined(_DEFAULT_SOURCE)
#define _DEFAULT_SOURCE 1
-#endif
+#endif
#if defined(OMITTED__D__EXTENSIONS__) && !defined(__EXTENSIONS__)
#define __EXTENSIONS__ 1
-#endif
+#endif
#if defined(OMITTED__D_POSIX_C_SOURCE_200112) && !defined(_POSIX_C_SOURCE)
#define _POSIX_C_SOURCE 200112
-#endif
+#endif
#if defined(OMITTED__D_XOPEN_SOURCE_600) && !defined(_XOPEN_SOURCE)
#define _XOPEN_SOURCE 600
-#endif
+#endif
#if defined(OMITTED__D_XOPEN_SOURCE_EXTENDED_1) && !defined(_XOPEN_SOURCE_EXTENDED)
#define _XOPEN_SOURCE_EXTENDED 1
-#endif
+#endif
#if defined(OMITTED__D_ALL_SOURCE) && !defined(_ALL_SOURCE)
#define _ALL_SOURCE 1
-#endif
+#endif
#if defined(OMITTED__D_LARGEFILE_SOURCE_1) && !defined(_LARGEFILE_SOURCE)
#define _LARGEFILE_SOURCE 1
-#endif
+#endif
#endif
-
+
#ifdef HAVE_ATTR_FORMAT
# define ATTR_FORMAT(archetype, string_index, first_to_check) \
__attribute__ ((format (archetype, string_index, first_to_check)))
#ifdef HAVE_WINSOCK2_H
#define FD_SET_T (u_int)
#else
-#define FD_SET_T
+#define FD_SET_T
#endif
#! /bin/sh
# Configuration validation subroutine script.
-# Copyright 1992-2022 Free Software Foundation, Inc.
+# Copyright 1992-2023 Free Software Foundation, Inc.
# shellcheck disable=SC2006,SC2268 # see below for rationale
-timestamp='2022-09-17'
+timestamp='2023-07-31'
# 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
version="\
GNU config.sub ($timestamp)
-Copyright 1992-2022 Free Software Foundation, Inc.
+Copyright 1992-2023 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
help="
-Try \`$me --help' for more information."
+Try '$me --help' for more information."
# Parse command line
while test $# -gt 0 ; do
# Separate into logical components for further validation
case $1 in
*-*-*-*-*)
- echo Invalid configuration \`"$1"\': more than four components >&2
+ echo "Invalid configuration '$1': more than four components" >&2
exit 1
;;
*-*-*-*)
nto-qnx* | linux-* | uclinux-uclibc* \
| uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \
| netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \
- | storm-chaos* | os2-emx* | rtmk-nova* | managarm-*)
+ | storm-chaos* | os2-emx* | rtmk-nova* | managarm-* \
+ | windows-* )
basic_machine=$field1
basic_os=$maybe_os
;;
EOF
IFS=$saved_IFS
;;
- # We use `pc' rather than `unknown'
+ # We use 'pc' rather than 'unknown'
# because (1) that's what they normally are, and
# (2) the word "unknown" tends to confuse beginning users.
i*86 | x86_64)
pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
cpu=i586
;;
- pentiumpro-* | p6-* | 6x86-* | athlon-* | athalon_*-*)
+ pentiumpro-* | p6-* | 6x86-* | athlon-* | athlon_*-*)
cpu=i686
;;
pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
| i370 | i*86 | i860 | i960 | ia16 | ia64 \
| ip2k | iq2000 \
| k1om \
+ | kvx \
| le32 | le64 \
| lm32 \
| loongarch32 | loongarch64 \
| m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x \
| m88110 | m88k | maxq | mb | mcore | mep | metag \
| microblaze | microblazeel \
- | mips | mipsbe | mipseb | mipsel | mipsle \
- | mips16 \
- | mips64 | mips64eb | mips64el \
- | mips64octeon | mips64octeonel \
- | mips64orion | mips64orionel \
- | mips64r5900 | mips64r5900el \
- | mips64vr | mips64vrel \
- | mips64vr4100 | mips64vr4100el \
- | mips64vr4300 | mips64vr4300el \
- | mips64vr5000 | mips64vr5000el \
- | mips64vr5900 | mips64vr5900el \
- | mipsisa32 | mipsisa32el \
- | mipsisa32r2 | mipsisa32r2el \
- | mipsisa32r3 | mipsisa32r3el \
- | mipsisa32r5 | mipsisa32r5el \
- | mipsisa32r6 | mipsisa32r6el \
- | mipsisa64 | mipsisa64el \
- | mipsisa64r2 | mipsisa64r2el \
- | mipsisa64r3 | mipsisa64r3el \
- | mipsisa64r5 | mipsisa64r5el \
- | mipsisa64r6 | mipsisa64r6el \
- | mipsisa64sb1 | mipsisa64sb1el \
- | mipsisa64sr71k | mipsisa64sr71kel \
- | mipsr5900 | mipsr5900el \
- | mipstx39 | mipstx39el \
+ | mips* \
| mmix \
| mn10200 | mn10300 \
| moxie \
;;
*)
- echo Invalid configuration \`"$1"\': machine \`"$cpu-$vendor"\' not recognized 1>&2
+ echo "Invalid configuration '$1': machine '$cpu-$vendor' not recognized" 1>&2
exit 1
;;
esac
| hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \
| sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \
| hiux* | abug | nacl* | netware* | windows* \
- | os9* | macos* | osx* | ios* \
+ | os9* | macos* | osx* | ios* | tvos* | watchos* \
| mpw* | magic* | mmixware* | mon960* | lnews* \
| amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \
| aos* | aros* | cloudabi* | sortix* | twizzler* \
| onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \
| midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \
| nsk* | powerunix* | genode* | zvmoe* | qnx* | emx* | zephyr* \
- | fiwix* | mlibc* )
+ | fiwix* | mlibc* | cos* | mbr* )
;;
# This one is extra strict with allowed versions
sco3.2v2 | sco3.2v[4-9]* | sco5v6*)
;;
none)
;;
- kernel* )
+ kernel* | msvc* )
# Restricted further below
;;
*)
- echo Invalid configuration \`"$1"\': OS \`"$os"\' not recognized 1>&2
+ echo "Invalid configuration '$1': OS '$os' not recognized" 1>&2
exit 1
;;
esac
;;
managarm-mlibc* | managarm-kernel* )
;;
+ windows*-gnu* | windows*-msvc*)
+ ;;
-dietlibc* | -newlib* | -musl* | -relibc* | -uclibc* | -mlibc* )
# These are just libc implementations, not actual OSes, and thus
# require a kernel.
- echo "Invalid configuration \`$1': libc \`$os' needs explicit kernel." 1>&2
+ echo "Invalid configuration '$1': libc '$os' needs explicit kernel." 1>&2
exit 1
;;
-kernel* )
- echo "Invalid configuration \`$1': \`$os' needs explicit kernel." 1>&2
+ echo "Invalid configuration '$1': '$os' needs explicit kernel." 1>&2
exit 1
;;
*-kernel* )
- echo "Invalid configuration \`$1': \`$kernel' does not support \`$os'." 1>&2
+ echo "Invalid configuration '$1': '$kernel' does not support '$os'." 1>&2
+ exit 1
+ ;;
+ *-msvc* )
+ echo "Invalid configuration '$1': '$os' needs 'windows'." 1>&2
exit 1
;;
kfreebsd*-gnu* | kopensolaris*-gnu*)
;;
*-eabi* | *-gnueabi*)
;;
+ none-coff* | none-elf*)
+ # None (no kernel, i.e. freestanding / bare metal),
+ # can be paired with an output format "OS"
+ ;;
-*)
# Blank kernel with real OS is always fine.
;;
*-*)
- echo "Invalid configuration \`$1': Kernel \`$kernel' not known to work with OS \`$os'." 1>&2
+ echo "Invalid configuration '$1': Kernel '$kernel' not known to work with OS '$os'." 1>&2
exit 1
;;
esac
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for unbound 1.17.0.
+# Generated by GNU Autoconf 2.69 for unbound 1.18.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.17.0'
-PACKAGE_STRING='unbound 1.17.0'
+PACKAGE_VERSION='1.18.0'
+PACKAGE_STRING='unbound 1.18.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.17.0 to adapt to many kinds of systems.
+\`configure' configures unbound 1.18.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.17.0:";;
+ short | recursive ) echo "Configuration of unbound 1.18.0:";;
esac
cat <<\_ACEOF
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-unbound configure 1.17.0
+unbound configure 1.18.0
generated by GNU Autoconf 2.69
Copyright (C) 2012 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.17.0, which was
+It was created by unbound $as_me 1.18.0, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
UNBOUND_VERSION_MAJOR=1
-UNBOUND_VERSION_MINOR=17
+UNBOUND_VERSION_MINOR=18
UNBOUND_VERSION_MICRO=0
LIBUNBOUND_CURRENT=9
-LIBUNBOUND_REVISION=20
+LIBUNBOUND_REVISION=22
LIBUNBOUND_AGE=1
# 1.0.0 had 0:12:0
# 1.0.1 had 0:13:0
# 1.16.2 had 9:18:1
# 1.16.3 had 9:19:1
# 1.17.0 had 9:20:1
+# 1.17.1 had 9:21:1
+# 1.18.0 had 9:22:1
# Current -- the number of the binary API that we're implementing
# Revision -- which iteration of the implementation of the binary
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $CC dependency flag" >&5
$as_echo_n "checking $CC dependency flag... " >&6; }
-echo 'void f(){}' >conftest.c
+echo 'void f(void){}' >conftest.c
if test "`$CC -MM conftest.c 2>&1`" = "conftest.o: conftest.c"; then
DEPFLAG="-MM"
else
#include <getopt.h>
#endif
-int test() {
+int test(void) {
int a;
char **opts = NULL;
struct timeval tv;
return a;
}
' > conftest.c
-echo 'void f(){}' >>conftest.c
+echo 'void f(void){}' >>conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS $ERRFLAG -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_needed_$cache=no"
else
#include <getopt.h>
#endif
-int test() {
+int test(void) {
int a;
char **opts = NULL;
struct timeval tv;
return a;
}
' > conftest.c
-echo 'void f(){}' >>conftest.c
+echo 'void f(void){}' >>conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS $ERRFLAG -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_needed_$cache=no"
else
echo '
#include <stdbool.h>
#include <ctype.h>
-int test() {
+int test(void) {
int a = 0;
return a;
}
' > conftest.c
-echo 'void f(){}' >>conftest.c
+echo 'void f(void){}' >>conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS $ERRFLAG -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_needed_$cache=no"
else
echo '
#include <ctype.h>
-int test() {
+int test(void) {
int a;
a = isascii(32);
return a;
}
' > conftest.c
-echo 'void f(){}' >>conftest.c
+echo 'void f(void){}' >>conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS $ERRFLAG -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_needed_$cache=no"
else
echo '
#include <netinet/in.h>
-int test() {
+int test(void) {
struct in6_pktinfo inf;
int a = (int)sizeof(inf);
return a;
}
' > conftest.c
-echo 'void f(){}' >>conftest.c
+echo 'void f(void){}' >>conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS $ERRFLAG -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_needed_$cache=no"
else
echo '
#include <unistd.h>
-int test() {
+int test(void) {
int a = setresgid(0,0,0);
a = setresuid(0,0,0);
return a;
}
' > conftest.c
-echo 'void f(){}' >>conftest.c
+echo 'void f(void){}' >>conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS $ERRFLAG -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_needed_$cache=no"
else
#endif
#include <netdb.h>
-int test() {
+int test(void) {
int a = 0;
char *t;
time_t time = 0;
return a;
}
' > conftest.c
-echo 'void f(){}' >>conftest.c
+echo 'void f(void){}' >>conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS $ERRFLAG -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_needed_$cache=no"
else
#include <getopt.h>
#endif
-int test() {
+int test(void) {
int a;
char **opts = NULL;
struct timeval tv;
return a;
}
' > conftest.c
-echo 'void f(){}' >>conftest.c
+echo 'void f(void){}' >>conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS $ERRFLAG -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_needed_$cache=no"
else
fi
fi
+if test "$LEX" = "" -o "$LEX" = ":"; then
+ if test ! -f util/configlexer.c; then
+ as_fn_error $? "no lex and no util/configlexer.c: need flex and bison to compile from source repository." "$LINENO" 5
+ fi
+fi
for ac_prog in 'bison -y' byacc
do
# Extract the first word of "$ac_prog", so it can be a program name with args.
done
test -n "$YACC" || YACC="yacc"
+if test "$YACC" = "" -o "$YACC" = ":"; then
+ if test ! -f util/configparser.c; then
+ as_fn_error $? "no yacc and no util/configparser.c: need flex and bison to compile from source repository." "$LINENO" 5
+ fi
+fi
# Extract the first word of "doxygen", so it can be a program name with args.
set dummy doxygen; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
done
+# Check for Linux timestamping headers
+for ac_header in linux/net_tstamp.h
+do :
+ ac_fn_c_check_header_compile "$LINENO" "linux/net_tstamp.h" "ac_cv_header_linux_net_tstamp_h" "$ac_includes_default
+"
+if test "x$ac_cv_header_linux_net_tstamp_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LINUX_NET_TSTAMP_H 1
+_ACEOF
+
+fi
+
+done
+
+
# check for types.
# Using own tests for int64* because autoconf builtin only give 32bit.
ac_fn_c_check_type "$LINENO" "int8_t" "ac_cv_type_int8_t" "$ac_includes_default"
echo '
#include <stdio.h>
-int test() {
+int test(void) {
int a = fseeko(stdin, 0, 0);
return a;
}
' > conftest.c
-echo 'void f(){}' >>conftest.c
+echo 'void f(void){}' >>conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS $ERRFLAG -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_needed_$cache=no"
else
PYTHON_VERSION=`$PYTHON -c "import sys; \
print(sys.version.split()[0])"`
fi
+ # calculate the version number components.
+
+ v="$PYTHON_VERSION"
+ PYTHON_VERSION_MAJOR=`echo $v | sed 's/[^0-9].*//'`
+ if test -z "$PYTHON_VERSION_MAJOR"; then PYTHON_VERSION_MAJOR="0"; fi
+ v=`echo $v | sed -e 's/^[0-9]*$//' -e 's/[0-9]*[^0-9]//'`
+ PYTHON_VERSION_MINOR=`echo $v | sed 's/[^0-9].*//'`
+ if test -z "$PYTHON_VERSION_MINOR"; then PYTHON_VERSION_MINOR="0"; fi
+ v=`echo $v | sed -e 's/^[0-9]*$//' -e 's/[0-9]*[^0-9]//'`
+ PYTHON_VERSION_PATCH=`echo $v | sed 's/[^0-9].*//'`
+ if test -z "$PYTHON_VERSION_PATCH"; then PYTHON_VERSION_PATCH="0"; fi
+
+
+ # For some systems, sysconfig exists, but has the wrong paths,
+ # on Debian 10, for python 2.7 and 3.7. So, we check the version,
+ # and for older versions try distutils.sysconfig first. For newer
+ # versions>=3.10, where distutils.sysconfig is deprecated, use
+ # sysconfig first and then attempt the other one.
+ py_distutils_first="no"
+ if test $PYTHON_VERSION_MAJOR -lt 3; then
+ py_distutils_first="yes"
+ fi
+ if test $PYTHON_VERSION_MAJOR -eq 3 -a $PYTHON_VERSION_MINOR -lt 10; then
+ py_distutils_first="yes"
+ fi
- # Check if you have sysconfig
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the sysconfig Python module" >&5
-$as_echo_n "checking for the sysconfig Python module... " >&6; }
- if ac_sysconfig_result=`$PYTHON -c "import sysconfig" 2>&1`; then
+ # Check if you have the first module
+ if test "$py_distutils_first" = "yes"; then m="distutils"; else m="sysconfig"; fi
+ sysconfig_module=""
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the $m Python module" >&5
+$as_echo_n "checking for the $m Python module... " >&6; }
+ if ac_modulecheck_result1=`$PYTHON -c "import $m" 2>&1`; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
- sysconfig_module="sysconfig"
- # if yes, use sysconfig, because distutils is deprecated.
+ sysconfig_module="$m"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
- # if no, try to use distutils
-
- #
- # Check if you have distutils, else fail
- #
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5
-$as_echo_n "checking for the distutils Python package... " >&6; }
- if ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`; then
+ fi
+
+ # if not found, try the other one.
+ if test -z "$sysconfig_module"; then
+ if test "$py_distutils_first" = "yes"; then m2="sysconfig"; else m2="distutils"; fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the $m2 Python module" >&5
+$as_echo_n "checking for the $m2 Python module... " >&6; }
+ if ac_modulecheck_result2=`$PYTHON -c "import $m2" 2>&1`; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
+ sysconfig_module="$m2"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
- as_fn_error $? "cannot import Python module \"distutils\".
- Please check your Python installation. The error was:
- $ac_distutils_result" "$LINENO" 5
+ as_fn_error $? "cannot import Python module \"$m\", or \"$m2\".
+ Please check your Python installation. The errors are:
+ $m
+ $ac_modulecheck_result1
+ $m2
+ $ac_modulecheck_result2" "$LINENO" 5
PYTHON_VERSION=""
fi
-
- sysconfig_module="distutils.sysconfig"
fi
+ if test "$sysconfig_module" = "distutils"; then sysconfig_module="distutils.sysconfig"; fi
#
# Check for Python include path
#
if test ! -z "$PYTHON_VERSION"; then
- if test `$PYTHON -c "print('$PYTHON_VERSION' >= '2.4.0')"` = "False"; then
+ badversion="no"
+ if test "$PYTHON_VERSION_MAJOR" -lt 2; then
+ badversion="yes"
+ fi
+ if test "$PYTHON_VERSION_MAJOR" -eq 2 -a "$PYTHON_VERSION_MINOR" -lt 4; then
+ badversion="yes"
+ fi
+ if test "$badversion" = "yes"; then
as_fn_error $? "Python version >= 2.4.0 is required" "$LINENO" 5
fi
#ifdef __cplusplus
}
#endif
-int main() {
+int main(void) {
;
return 0;
}
#include <stdlib.h>
#include <unistd.h>
' >conftest.c
-echo 'void f(){ (void)daemon(0, 0); }' >>conftest.c
+echo 'void f(void){ (void)daemon(0, 0); }' >>conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS -c conftest.c 2>&1 | grep -e deprecated -e unavailable`"; then
eval "cv_cc_deprecated_$cache=no"
else
-version=1.17.0
+version=1.18.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.17.0, which was
+This file was extended by unbound $as_me 1.18.0, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-unbound config.status 1.17.0
+unbound config.status 1.18.0
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
# must be numbers. ac_defun because of later processing
m4_define([VERSION_MAJOR],[1])
-m4_define([VERSION_MINOR],[17])
+m4_define([VERSION_MINOR],[18])
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=20
+LIBUNBOUND_REVISION=22
LIBUNBOUND_AGE=1
# 1.0.0 had 0:12:0
# 1.0.1 had 0:13:0
# 1.16.2 had 9:18:1
# 1.16.3 had 9:19:1
# 1.17.0 had 9:20:1
+# 1.17.1 had 9:21:1
+# 1.18.0 had 9:22:1
# Current -- the number of the binary API that we're implementing
# Revision -- which iteration of the implementation of the binary
# are we on MinGW?
if uname -s 2>&1 | grep MINGW >/dev/null; then on_mingw="yes"
-else
+else
if echo $host | grep mingw >/dev/null; then on_mingw="yes"
else on_mingw="no"; fi
fi
AC_SUBST(ub_conf_dir)
# Determine run, chroot directory and pidfile locations
-AC_ARG_WITH(run-dir,
- AS_HELP_STRING([--with-run-dir=path],[set default directory to chdir to (by default dir part of cfg file)]),
- UNBOUND_RUN_DIR="$withval",
+AC_ARG_WITH(run-dir,
+ AS_HELP_STRING([--with-run-dir=path],[set default directory to chdir to (by default dir part of cfg file)]),
+ UNBOUND_RUN_DIR="$withval",
if test $on_mingw = no; then
UNBOUND_RUN_DIR=`dirname "$ub_conf_file"`
else
ACX_ESCAPE_BACKSLASH($UNBOUND_RUN_DIR, hdr_run)
AC_DEFINE_UNQUOTED(RUN_DIR, ["$hdr_run"], [Directory to chdir to])
-AC_ARG_WITH(chroot-dir,
- AS_HELP_STRING([--with-chroot-dir=path],[set default directory to chroot to (by default same as run-dir)]),
- UNBOUND_CHROOT_DIR="$withval",
+AC_ARG_WITH(chroot-dir,
+ AS_HELP_STRING([--with-chroot-dir=path],[set default directory to chroot to (by default same as run-dir)]),
+ UNBOUND_CHROOT_DIR="$withval",
if test $on_mingw = no; then
UNBOUND_CHROOT_DIR="$UNBOUND_RUN_DIR"
else
AC_SUBST(UNBOUND_SHARE_DIR)
AC_DEFINE_UNQUOTED(SHARE_DIR, ["$UNBOUND_SHARE_DIR"], [Shared data])
-AC_ARG_WITH(pidfile,
- AS_HELP_STRING([--with-pidfile=filename],[set default pathname to unbound pidfile (default run-dir/unbound.pid)]),
- UNBOUND_PIDFILE="$withval",
+AC_ARG_WITH(pidfile,
+ AS_HELP_STRING([--with-pidfile=filename],[set default pathname to unbound pidfile (default run-dir/unbound.pid)]),
+ UNBOUND_PIDFILE="$withval",
if test $on_mingw = no; then
UNBOUND_PIDFILE="$UNBOUND_RUN_DIR/unbound.pid"
else
ACX_ESCAPE_BACKSLASH($UNBOUND_PIDFILE, hdr_pid)
AC_DEFINE_UNQUOTED(PIDFILE, ["$hdr_pid"], [default pidfile location])
-AC_ARG_WITH(rootkey-file,
- AS_HELP_STRING([--with-rootkey-file=filename],[set default pathname to root key file (default run-dir/root.key). This file is read and written.]),
- UNBOUND_ROOTKEY_FILE="$withval",
+AC_ARG_WITH(rootkey-file,
+ AS_HELP_STRING([--with-rootkey-file=filename],[set default pathname to root key file (default run-dir/root.key). This file is read and written.]),
+ UNBOUND_ROOTKEY_FILE="$withval",
if test $on_mingw = no; then
UNBOUND_ROOTKEY_FILE="$UNBOUND_RUN_DIR/root.key"
else
ACX_ESCAPE_BACKSLASH($UNBOUND_ROOTKEY_FILE, hdr_rkey)
AC_DEFINE_UNQUOTED(ROOT_ANCHOR_FILE, ["$hdr_rkey"], [default rootkey location])
-AC_ARG_WITH(rootcert-file,
- AS_HELP_STRING([--with-rootcert-file=filename],[set default pathname to root update certificate file (default run-dir/icannbundle.pem). This file need not exist if you are content with the builtin.]),
- UNBOUND_ROOTCERT_FILE="$withval",
+AC_ARG_WITH(rootcert-file,
+ AS_HELP_STRING([--with-rootcert-file=filename],[set default pathname to root update certificate file (default run-dir/icannbundle.pem). This file need not exist if you are content with the builtin.]),
+ UNBOUND_ROOTCERT_FILE="$withval",
if test $on_mingw = no; then
UNBOUND_ROOTCERT_FILE="$UNBOUND_RUN_DIR/icannbundle.pem"
else
ACX_ESCAPE_BACKSLASH($UNBOUND_ROOTCERT_FILE, hdr_rpem)
AC_DEFINE_UNQUOTED(ROOT_CERT_FILE, ["$hdr_rpem"], [default rootcert location])
-AC_ARG_WITH(username,
- AS_HELP_STRING([--with-username=user],[set default user that unbound changes to (default user is unbound)]),
- UNBOUND_USERNAME="$withval",
+AC_ARG_WITH(username,
+ AS_HELP_STRING([--with-username=user],[set default user that unbound changes to (default user is unbound)]),
+ UNBOUND_USERNAME="$withval",
UNBOUND_USERNAME="unbound")
AC_SUBST(UNBOUND_USERNAME)
AC_DEFINE_UNQUOTED(UB_USERNAME, ["$UNBOUND_USERNAME"], [default username])
# debug mode flags warnings
AC_ARG_ENABLE(checking, AS_HELP_STRING([--enable-checking],[Enable warnings, asserts, makefile-dependencies]))
AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug],[same as enable-checking]))
-if test "$enable_debug" = "yes"; then debug_enabled="$enable_debug";
+if test "$enable_debug" = "yes"; then debug_enabled="$enable_debug";
else debug_enabled="$enable_checking"; fi
AC_SUBST(debug_enabled)
case "$debug_enabled" in
if test "$LEX" != "" -a "$LEX" != ":"; then
ACX_YYLEX_OPTION
fi
+if test "$LEX" = "" -o "$LEX" = ":"; then
+ if test ! -f util/configlexer.c; then
+ AC_MSG_ERROR([no lex and no util/configlexer.c: need flex and bison to compile from source repository.])
+ fi
+fi
AC_PROG_YACC
+if test "$YACC" = "" -o "$YACC" = ":"; then
+ if test ! -f util/configparser.c; then
+ AC_MSG_ERROR([no yacc and no util/configparser.c: need flex and bison to compile from source repository.])
+ fi
+fi
AC_CHECK_PROG(doxygen, doxygen, doxygen)
AC_CHECK_TOOL(STRIP, strip)
ACX_LIBTOOL_C_ONLY
#endif
])
+# Check for Linux timestamping headers
+AC_CHECK_HEADERS([linux/net_tstamp.h],,, [AC_INCLUDES_DEFAULT])
+
# check for types.
# Using own tests for int64* because autoconf builtin only give 32bit.
AC_CHECK_TYPE(int8_t, signed char)
# Include systemd.m4 - end
# set memory allocation checking if requested
-AC_ARG_ENABLE(alloc-checks, AS_HELP_STRING([--enable-alloc-checks],[ enable to memory allocation statistics, for debug purposes ]),
+AC_ARG_ENABLE(alloc-checks, AS_HELP_STRING([--enable-alloc-checks],[ enable to memory allocation statistics, for debug purposes ]),
, )
-AC_ARG_ENABLE(alloc-lite, AS_HELP_STRING([--enable-alloc-lite],[ enable for lightweight alloc assertions, for debug purposes ]),
+AC_ARG_ENABLE(alloc-lite, AS_HELP_STRING([--enable-alloc-lite],[ enable for lightweight alloc assertions, for debug purposes ]),
, )
-AC_ARG_ENABLE(alloc-nonregional, AS_HELP_STRING([--enable-alloc-nonregional],[ enable nonregional allocs, slow but exposes regional allocations to other memory purifiers, for debug purposes ]),
+AC_ARG_ENABLE(alloc-nonregional, AS_HELP_STRING([--enable-alloc-nonregional],[ enable nonregional allocs, slow but exposes regional allocations to other memory purifiers, for debug purposes ]),
, )
if test x_$enable_alloc_nonregional = x_yes; then
AC_DEFINE(UNBOUND_ALLOC_NONREGIONAL, 1, [use malloc not regions, for debug use])
])],
AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_WINDOWS_THREADS, 1, [Using Windows threads])
-,
+,
AC_MSG_RESULT(no)
)
# check this first, so that the pthread lib does not get linked in via
# libssl or libpython, and thus distorts the tests, and we end up using
# the non-threadsafe C libraries.
-AC_ARG_WITH(pthreads, AS_HELP_STRING([--with-pthreads],[use pthreads library, or --without-pthreads to disable threading support.]),
+AC_ARG_WITH(pthreads, AS_HELP_STRING([--with-pthreads],[use pthreads library, or --without-pthreads to disable threading support.]),
[ ],[ withval="yes" ])
ub_have_pthreads=no
if test x_$withval != x_no; then
# first compile
echo "$CC $CFLAGS -c conftest.c -o conftest.o" >&AS_MESSAGE_LOG_FD
$CC $CFLAGS -c conftest.c -o conftest.o 2>&AS_MESSAGE_LOG_FD >&AS_MESSAGE_LOG_FD
- if test $? = 0; then
+ if test $? = 0; then
# then link
echo "$CC $CFLAGS -Werror $LDFLAGS $LIBS -o conftest contest.o" >&AS_MESSAGE_LOG_FD
$CC $CFLAGS -Werror $LDFLAGS $LIBS -o conftest conftest.o 2>&AS_MESSAGE_LOG_FD >&AS_MESSAGE_LOG_FD
])
fi
-# check solaris thread library
+# check solaris thread library
AC_ARG_WITH(solaris-threads, AS_HELP_STRING([--with-solaris-threads],[use solaris native thread library.]), [ ],[ withval="no" ])
ub_have_sol_threads=no
if test x_$withval != x_no; then
ACX_CHECK_COMPILER_FLAG(mt, [CFLAGS="$CFLAGS -mt"],
[CFLAGS="$CFLAGS -D_REENTRANT"])
ub_have_sol_threads=yes
- ] , [
- AC_MSG_ERROR([no solaris threads found.])
+ ] , [
+ AC_MSG_ERROR([no solaris threads found.])
])
fi
fi
ac_save_LIBS="$LIBS" dnl otherwise AC_PYTHON_DEVEL thrashes $LIBS
AC_PYTHON_DEVEL
if test ! -z "$PYTHON_VERSION"; then
- if test `$PYTHON -c "print('$PYTHON_VERSION' >= '2.4.0')"` = "False"; then
+ badversion="no"
+ if test "$PYTHON_VERSION_MAJOR" -lt 2; then
+ badversion="yes"
+ fi
+ if test "$PYTHON_VERSION_MAJOR" -eq 2 -a "$PYTHON_VERSION_MINOR" -lt 4; then
+ badversion="yes"
+ fi
+ if test "$badversion" = "yes"; then
AC_MSG_ERROR([Python version >= 2.4.0 is required])
fi
EVP_PKEY_asn1_get0_info(&gost_id, NULL, NULL, NULL, NULL, meth);
return gost_id;
}
-int main(void) {
+int main(void) {
EVP_MD_CTX* ctx;
const EVP_MD* md;
unsigned char digest[64]; /* its a 256-bit digest, so uses 32 bytes */
fi
# set lock checking if requested
-AC_ARG_ENABLE(lock_checks, AS_HELP_STRING([--enable-lock-checks],[ enable to check lock and unlock calls, for debug purposes ]),
+AC_ARG_ENABLE(lock_checks, AS_HELP_STRING([--enable-lock-checks],[ enable to check lock and unlock calls, for debug purposes ]),
, )
if test x_$enable_lock_checks = x_yes; then
AC_DEFINE(ENABLE_LOCK_CHECKS, 1, [Define if you want to use debug lock checking (slow).])
fi
])
if test $ALLTARGET = "alltargets"; then
- if test $USE_NSS = "yes"; then
- AC_MSG_ERROR([--with-nss can only be used in combination with --with-libunbound-only.])
+ if test $USE_NSS = "yes"; then
+ AC_MSG_ERROR([--with-nss can only be used in combination with --with-libunbound-only.])
fi
if test $USE_NETTLE = "yes"; then
- AC_MSG_ERROR([--with-nettle can only be used in combination with --with-libunbound-only.])
+ AC_MSG_ERROR([--with-nettle can only be used in combination with --with-libunbound-only.])
fi
fi
if test -n "$LATE_LDFLAGS"; then
LDFLAGS="$LATE_LDFLAGS $LDFLAGS"
fi
-# remove start spaces
+# remove start spaces
LDFLAGS=`echo "$LDFLAGS"|sed -e 's/^ *//'`
LIBS=`echo "$LIBS"|sed -e 's/^ *//'`
*control = acl_allow_snoop;
else if(strcmp(str, "allow_setrd") == 0)
*control = acl_allow_setrd;
+ else if (strcmp(str, "allow_cookie") == 0)
+ *control = acl_allow_cookie;
else {
log_err("access control type %s unknown", str);
return 0;
acl_allow,
/** allow full access for all queries, recursion and cache snooping */
acl_allow_snoop,
- /** allow full access for recursion queries and set RD flag regardless of request */
- acl_allow_setrd
+ /** allow full access for recursion queries and set RD flag regardless
+ * of request */
+ acl_allow_setrd,
+ /** allow full access for recursion (+RD) queries if valid cookie
+ * present or stateful transport */
+ acl_allow_cookie
};
/**
/** dump message entry */
static int
-dump_msg(RES* ssl, struct query_info* k, struct reply_info* d,
- time_t now)
+dump_msg(RES* ssl, struct query_info* k, struct reply_info* d, time_t now)
{
size_t i;
char* nm, *tp, *cl;
}
/* meta line */
- if(!ssl_printf(ssl, "msg %s %s %s %d %d " ARG_LL "d %d %u %u %u\n",
+ if(!ssl_printf(ssl, "msg %s %s %s %d %d " ARG_LL "d %d %u %u %u %d %s\n",
nm, cl, tp,
(int)d->flags, (int)d->qdcount,
(long long)(d->ttl-now), (int)d->security,
- (unsigned)d->an_numrrsets,
+ (unsigned)d->an_numrrsets,
(unsigned)d->ns_numrrsets,
- (unsigned)d->ar_numrrsets)) {
+ (unsigned)d->ar_numrrsets,
+ (int)d->reason_bogus,
+ d->reason_bogus_str?d->reason_bogus_str:"")) {
free(nm);
free(tp);
free(cl);
struct rrset_ref ref;
uint8_t* p;
- ak = alloc_special_obtain(&worker->alloc);
+ ak = alloc_special_obtain(worker->alloc);
if(!ak) {
log_warn("error out of memory");
return 0;
ak->rk.dname = (uint8_t*)memdup(k->rk.dname, k->rk.dname_len);
if(!ak->rk.dname) {
log_warn("error out of memory");
- ub_packed_rrset_parsedelete(ak, &worker->alloc);
+ ub_packed_rrset_parsedelete(ak, worker->alloc);
return 0;
}
s = sizeof(*ad) + (sizeof(size_t) + sizeof(uint8_t*) +
ad = (struct packed_rrset_data*)malloc(s);
if(!ad) {
log_warn("error out of memory");
- ub_packed_rrset_parsedelete(ak, &worker->alloc);
+ ub_packed_rrset_parsedelete(ak, worker->alloc);
return 0;
}
p = (uint8_t*)ad;
ref.key = ak;
ref.id = ak->id;
(void)rrset_cache_update(worker->env.rrset_cache, &ref,
- &worker->alloc, *worker->env.now);
+ worker->alloc, *worker->env.now);
+
return 1;
}
long long ttl;
size_t i;
int go_on = 1;
+ int ede;
+ int consumed = 0;
+ char* ede_str = NULL;
regional_free_all(region);
}
/* read remainder of line */
- if(sscanf(s, " %u %u " ARG_LL "d %u %u %u %u", &flags, &qdcount, &ttl,
- &security, &an, &ns, &ar) != 7) {
+ /* note the last space before any possible EDE text */
+ if(sscanf(s, " %u %u " ARG_LL "d %u %u %u %u %d %n", &flags, &qdcount, &ttl,
+ &security, &an, &ns, &ar, &ede, &consumed) != 8) {
log_warn("error cannot parse numbers: %s", s);
return 0;
}
+ /* there may be EDE text after the numbers */
+ if(consumed > 0 && (size_t)consumed < strlen(s))
+ ede_str = s + consumed;
+ memset(&rep, 0, sizeof(rep));
rep.flags = (uint16_t)flags;
rep.qdcount = (uint16_t)qdcount;
rep.ttl = (time_t)ttl;
rep.ns_numrrsets = (size_t)ns;
rep.ar_numrrsets = (size_t)ar;
rep.rrset_count = (size_t)an+(size_t)ns+(size_t)ar;
+ rep.reason_bogus = (sldns_ede_code)ede;
+ rep.reason_bogus_str = ede_str?(char*)regional_strdup(region, ede_str):NULL;
rep.rrsets = (struct ub_packed_rrset_key**)regional_alloc_zero(
region, sizeof(struct ub_packed_rrset_key*)*rep.rrset_count);
/* go up? */
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))) {
+ (worker->env.cfg->do_ip6 && worker->back->num_ip6 != 0),
+ worker->env.cfg->do_nat64)) {
print_dp_main(ssl, dp, msg);
print_dp_details(ssl, worker, dp);
if(!ssl_printf(ssl, "cache delegation was "
return avail;
}
+/**
+ * Clear and delete per-worker alloc caches, and free memory maintained in
+ * superalloc.
+ * The rrset and message caches must be empty at the time of call.
+ * @param daemon: the daemon that maintains the alloc caches to be cleared.
+ */
+static void
+daemon_clear_allocs(struct daemon* daemon)
+{
+ int i;
+
+ for(i=0; i<daemon->num; i++) {
+ alloc_clear(daemon->worker_allocs[i]);
+ free(daemon->worker_allocs[i]);
+ }
+ free(daemon->worker_allocs);
+ daemon->worker_allocs = NULL;
+
+ alloc_clear_special(&daemon->superalloc);
+}
+
/**
* Allocate empty worker structures. With backptr and thread-number,
* from 0..numthread initialised. Used as user arguments to new threads.
/* the above is not ports/numthr, due to rounding */
fatal_exit("could not create worker");
}
+ /* create per-worker alloc caches if not reusing existing ones. */
+ if(!daemon->worker_allocs) {
+ daemon->worker_allocs = (struct alloc_cache**)calloc(
+ (size_t)daemon->num, sizeof(struct alloc_cache*));
+ if(!daemon->worker_allocs)
+ fatal_exit("could not allocate worker allocs");
+ for(i=0; i<daemon->num; i++) {
+ struct alloc_cache* alloc = calloc(1,
+ sizeof(struct alloc_cache));
+ if (!alloc)
+ fatal_exit("could not allocate worker alloc");
+ alloc_init(alloc, &daemon->superalloc, i);
+ daemon->worker_allocs[i] = alloc;
+ }
+ }
free(shufport);
}
/* Shutdown SHM */
shm_main_shutdown(daemon);
+ daemon->reuse_cache = daemon->workers[0]->reuse_cache;
daemon->need_to_exit = daemon->workers[0]->need_to_exit;
}
log_thread_set(NULL);
/* clean up caches because
* a) RRset IDs will be recycled after a reload, causing collisions
- * b) validation config can change, thus rrset, msg, keycache clear */
+ * b) validation config can change, thus rrset, msg, keycache clear
+ *
+ * If we are trying to keep the cache as long as possible, we should
+ * defer the cleanup until we know whether the new configuration allows
+ * the reuse. (If we're exiting, cleanup should be done here). */
+ if(!daemon->reuse_cache || daemon->need_to_exit) {
slabhash_clear(&daemon->env->rrset_cache->table);
slabhash_clear(daemon->env->msg_cache);
+ }
+ daemon->old_num = daemon->num; /* save the current num */
local_zones_delete(daemon->local_zones);
daemon->local_zones = NULL;
respip_set_delete(daemon->respip_set);
worker_delete(daemon->workers[i]);
free(daemon->workers);
daemon->workers = NULL;
+ /* Unless we're trying to keep the cache, worker alloc_caches should be
+ * cleared and freed here. We do this after deleting workers to
+ * guarantee that the alloc caches are valid throughout the lifetime
+ * of workers. */
+ if(!daemon->reuse_cache || daemon->need_to_exit)
+ daemon_clear_allocs(daemon);
daemon->num = 0;
- alloc_clear_special(&daemon->superalloc);
#ifdef USE_DNSTAP
dt_delete(daemon->dtenv);
daemon->dtenv = NULL;
void daemon_apply_cfg(struct daemon* daemon, struct config_file* cfg)
{
+ int new_num = cfg->num_threads?cfg->num_threads:1;
+
daemon->cfg = cfg;
config_apply(cfg);
+
+ /* If this is a reload and we deferred the decision on whether to
+ * reuse the alloc, RRset, and message caches, then check to see if
+ * it's safe to keep the caches:
+ * - changing the number of threads is obviously incompatible with
+ * keeping the per-thread alloc caches. It also means we have to
+ * clear RRset and message caches. (note that 'new_num' may be
+ * adjusted in daemon_create_workers, but for our purpose we can
+ * simply compare it with 'old_num'; if they are equal here,
+ * 'new_num' won't be adjusted to a different value than 'old_num').
+ * - changing RRset cache size effectively clears any remaining cache
+ * entries. We could keep their keys in alloc caches, but it would
+ * be more consistent with the sense of the change to clear allocs
+ * and free memory. To do so we also have to clear message cache.
+ * - only changing message cache size does not necessarily affect
+ * RRset or alloc cache. But almost all new subsequent queries will
+ * require recursive resolution anyway, so it doesn't help much to
+ * just keep RRset and alloc caches. For simplicity we clear/free
+ * the other two, too. */
+ if(daemon->worker_allocs &&
+ (new_num != daemon->old_num ||
+ !slabhash_is_size(daemon->env->msg_cache, cfg->msg_cache_size,
+ cfg->msg_cache_slabs) ||
+ !slabhash_is_size(&daemon->env->rrset_cache->table,
+ cfg->rrset_cache_size, cfg->rrset_cache_slabs)))
+ {
+ log_warn("cannot reuse caches due to critical config change");
+ slabhash_clear(&daemon->env->rrset_cache->table);
+ slabhash_clear(daemon->env->msg_cache);
+ daemon_clear_allocs(daemon);
+ }
+
if(!slabhash_is_size(daemon->env->msg_cache, cfg->msg_cache_size,
cfg->msg_cache_slabs)) {
slabhash_delete(daemon->env->msg_cache);
void* listen_sslctx, *connect_sslctx;
/** num threads allocated */
int num;
+ /** num threads allocated in the previous config or 0 at first */
+ int old_num;
/** the worker entries */
struct worker** workers;
+ /** per-worker allocation cache */
+ struct alloc_cache **worker_allocs;
/** do we need to exit unbound (or is it only a reload?) */
int need_to_exit;
/** master random table ; used for port div between threads on reload*/
/** the dnscrypt environment */
struct dnsc_env* dnscenv;
#endif
+ /** reuse existing cache on reload if other conditions allow it. */
+ int reuse_cache;
};
/**
* Copyright (c) 2008, NLnet Labs. All rights reserved.
*
* This software is open source.
- *
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
- *
+ *
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
- *
+ *
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
- *
+ *
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
- *
+ *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
*
* This file contains the remote control functionality for the daemon.
* The remote control can be performed using either the commandline
- * unbound-control tool, or a TLS capable web browser.
+ * unbound-control tool, or a TLS capable web browser.
* The channel is secured using TLSv1, and certificates.
* Both the server and the client(control tool) have their own keys.
*/
#include "sldns/parseutil.h"
#include "sldns/wire2str.h"
#include "sldns/sbuffer.h"
+#include "util/timeval_func.h"
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
/** what to put on statistics lines between var and value, ": " or "=" */
#define SQ "="
-/** if true, inhibits a lot of =0 lines from the stats output */
-static const int inhibit_zero = 1;
-
-/** 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)
-{
-#ifndef S_SPLINT_S
- time_t end_usec = end->tv_usec;
- d->tv_sec = end->tv_sec - start->tv_sec;
- if(end_usec < start->tv_usec) {
- end_usec += 1000000;
- d->tv_sec--;
- }
- d->tv_usec = end_usec - start->tv_usec;
-#endif
-}
-
-/** divide sum of timers to get average */
-static void
-timeval_divide(struct timeval* avg, const struct timeval* sum, long long d)
-{
-#ifndef S_SPLINT_S
- size_t leftover;
- if(d <= 0) {
- avg->tv_sec = 0;
- avg->tv_usec = 0;
- return;
- }
- avg->tv_sec = sum->tv_sec / d;
- avg->tv_usec = sum->tv_usec / d;
- /* handle fraction from seconds divide */
- leftover = sum->tv_sec - avg->tv_sec*d;
- if(leftover <= 0)
- leftover = 0;
- avg->tv_usec += (((long long)leftover)*((long long)1000000))/d;
- if(avg->tv_sec < 0)
- avg->tv_sec = 0;
- if(avg->tv_usec < 0)
- avg->tv_usec = 0;
-#endif
-}
static int
remote_setup_ctx(struct daemon_remote* rc, struct config_file* cfg)
struct daemon_remote*
daemon_remote_create(struct config_file* cfg)
{
- struct daemon_remote* rc = (struct daemon_remote*)calloc(1,
+ struct daemon_remote* rc = (struct daemon_remote*)calloc(1,
sizeof(*rc));
if(!rc) {
log_err("out of memory in daemon_remote_create");
n->next = rc->accept_list;
rc->accept_list = n;
/* open commpt */
- n->com = comm_point_create_raw(rc->worker->base, fd, 0,
+ n->com = comm_point_create_raw(rc->worker->base, fd, 0,
&remote_accept_callback, rc);
if(!n->com)
return 0;
return 1;
}
-int daemon_remote_open_accept(struct daemon_remote* rc,
+int daemon_remote_open_accept(struct daemon_remote* rc,
struct listen_port* ports, struct worker* worker)
{
struct listen_port* p;
{
struct listen_list* p;
for(p=rc->accept_list; p; p=p->next) {
- comm_point_stop_listening(p->com);
+ comm_point_stop_listening(p->com);
}
}
{
struct listen_list* p;
for(p=rc->accept_list; p; p=p->next) {
- comm_point_start_listening(p->com, -1, -1);
+ comm_point_start_listening(p->com, -1, -1);
}
}
-int remote_accept_callback(struct comm_point* c, void* arg, int err,
+int remote_accept_callback(struct comm_point* c, void* arg, int err,
struct comm_reply* ATTR_UNUSED(rep))
{
struct daemon_remote* rc = (struct daemon_remote*)arg;
}
n->fd = newfd;
/* start in reading state */
- n->c = comm_point_create_raw(rc->worker->base, newfd, 0,
+ n->c = comm_point_create_raw(rc->worker->base, newfd, 0,
&remote_control_callback, n);
if(!n->c) {
log_err("out of memory");
rc->busy_list = n;
rc->active ++;
- /* perform the first nonblocking read already, for windows,
+ /* perform the first nonblocking read already, for windows,
* so it can return wouldblock. could be faster too. */
(void)remote_control_callback(n->c, n, NETEVENT_NOERROR, NULL);
return 0;
ssl_print_text(RES* res, const char* text)
{
int r;
- if(!res)
+ if(!res)
return 0;
if(res->ssl) {
ERR_clear_error();
skipwhite(char* str)
{
/* EOS \0 is not a space */
- while( isspace((unsigned char)*str) )
+ while( isspace((unsigned char)*str) )
str++;
return str;
}
/** do the reload command */
static void
-do_reload(RES* ssl, struct worker* worker)
+do_reload(RES* ssl, struct worker* worker, int reuse_cache)
{
+ worker->reuse_cache = reuse_cache;
worker->need_to_exit = 0;
comm_base_exit(worker->base);
send_ok(ssl);
print_stats(RES* ssl, const char* nm, struct ub_stats_info* s)
{
struct timeval sumwait, avg;
- if(!ssl_printf(ssl, "%s.num.queries"SQ"%lu\n", nm,
+ if(!ssl_printf(ssl, "%s.num.queries"SQ"%lu\n", nm,
(unsigned long)s->svr.num_queries)) return 0;
if(!ssl_printf(ssl, "%s.num.queries_ip_ratelimited"SQ"%lu\n", nm,
(unsigned long)s->svr.num_queries_ip_ratelimited)) return 0;
- if(!ssl_printf(ssl, "%s.num.cachehits"SQ"%lu\n", nm,
- (unsigned long)(s->svr.num_queries
+ if(!ssl_printf(ssl, "%s.num.queries_cookie_valid"SQ"%lu\n", nm,
+ (unsigned long)s->svr.num_queries_cookie_valid)) return 0;
+ if(!ssl_printf(ssl, "%s.num.queries_cookie_client"SQ"%lu\n", nm,
+ (unsigned long)s->svr.num_queries_cookie_client)) return 0;
+ if(!ssl_printf(ssl, "%s.num.queries_cookie_invalid"SQ"%lu\n", nm,
+ (unsigned long)s->svr.num_queries_cookie_invalid)) return 0;
+ if(!ssl_printf(ssl, "%s.num.cachehits"SQ"%lu\n", nm,
+ (unsigned long)(s->svr.num_queries
- s->svr.num_queries_missed_cache))) return 0;
- if(!ssl_printf(ssl, "%s.num.cachemiss"SQ"%lu\n", nm,
+ if(!ssl_printf(ssl, "%s.num.cachemiss"SQ"%lu\n", nm,
(unsigned long)s->svr.num_queries_missed_cache)) return 0;
- if(!ssl_printf(ssl, "%s.num.prefetch"SQ"%lu\n", nm,
+ if(!ssl_printf(ssl, "%s.num.prefetch"SQ"%lu\n", nm,
(unsigned long)s->svr.num_queries_prefetch)) return 0;
+ if(!ssl_printf(ssl, "%s.num.queries_timed_out"SQ"%lu\n", nm,
+ (unsigned long)s->svr.num_queries_timed_out)) return 0;
+ if(!ssl_printf(ssl, "%s.query.queue_time_us.max"SQ"%lu\n", nm,
+ (unsigned long)s->svr.max_query_time_us)) return 0;
if(!ssl_printf(ssl, "%s.num.expired"SQ"%lu\n", nm,
(unsigned long)s->svr.ans_expired)) return 0;
- if(!ssl_printf(ssl, "%s.num.recursivereplies"SQ"%lu\n", nm,
+ if(!ssl_printf(ssl, "%s.num.recursivereplies"SQ"%lu\n", nm,
(unsigned long)s->mesh_replies_sent)) return 0;
#ifdef USE_DNSCRYPT
if(!ssl_printf(ssl, "%s.num.dnscrypt.crypted"SQ"%lu\n", nm,
timeval_divide(&avg, &sumwait, s->mesh_replies_sent);
if(!ssl_printf(ssl, "%s.recursion.time.avg"SQ ARG_LL "d.%6.6d\n", nm,
(long long)avg.tv_sec, (int)avg.tv_usec)) return 0;
- if(!ssl_printf(ssl, "%s.recursion.time.median"SQ"%g\n", nm,
+ if(!ssl_printf(ssl, "%s.recursion.time.median"SQ"%g\n", nm,
s->mesh_time_median)) return 0;
if(!ssl_printf(ssl, "%s.tcpusage"SQ"%lu\n", nm,
(unsigned long)s->svr.tcp_accept_usage)) return 0;
/* more than a Gb */
size_t front = x / (size_t)1000000;
size_t back = x % (size_t)1000000;
- return ssl_printf(ssl, "%s%u%6.6u\n", desc,
+ return ssl_printf(ssl, "%s%u%6.6u\n", desc,
(unsigned)front, (unsigned)back);
} else {
return ssl_printf(ssl, "%s%lu\n", desc, (unsigned long)x);
timeval_subtract(&dt, &now, &worker->daemon->time_last_stat);
if(reset)
worker->daemon->time_last_stat = now;
- if(!ssl_printf(ssl, "time.now"SQ ARG_LL "d.%6.6d\n",
+ if(!ssl_printf(ssl, "time.now"SQ ARG_LL "d.%6.6d\n",
(long long)now.tv_sec, (unsigned)now.tv_usec)) return 0;
- if(!ssl_printf(ssl, "time.up"SQ ARG_LL "d.%6.6d\n",
+ if(!ssl_printf(ssl, "time.up"SQ ARG_LL "d.%6.6d\n",
(long long)up.tv_sec, (unsigned)up.tv_usec)) return 0;
- if(!ssl_printf(ssl, "time.elapsed"SQ ARG_LL "d.%6.6d\n",
+ if(!ssl_printf(ssl, "time.elapsed"SQ ARG_LL "d.%6.6d\n",
(long long)dt.tv_sec, (unsigned)dt.tv_usec)) return 0;
return 1;
}
}
timehist_import(hist, s->svr.hist, NUM_BUCKETS_HIST);
for(i=0; i<hist->num; i++) {
- if(!ssl_printf(ssl,
+ if(!ssl_printf(ssl,
"histogram.%6.6d.%6.6d.to.%6.6d.%6.6d=%lu\n",
(int)hist->buckets[i].lower.tv_sec,
(int)hist->buckets[i].lower.tv_usec,
/** print extended stats */
static int
-print_ext(RES* ssl, struct ub_stats_info* s)
+print_ext(RES* ssl, struct ub_stats_info* s, int inhibit_zero)
{
int i;
char nm[32];
} else {
snprintf(nm, sizeof(nm), "TYPE%d", i);
}
- if(!ssl_printf(ssl, "num.query.type.%s"SQ"%lu\n",
+ if(!ssl_printf(ssl, "num.query.type.%s"SQ"%lu\n",
nm, (unsigned long)s->svr.qtype[i])) return 0;
}
if(!inhibit_zero || s->svr.qtype_big) {
- if(!ssl_printf(ssl, "num.query.type.other"SQ"%lu\n",
+ if(!ssl_printf(ssl, "num.query.type.other"SQ"%lu\n",
(unsigned long)s->svr.qtype_big)) return 0;
}
/* CLASS */
} else {
snprintf(nm, sizeof(nm), "CLASS%d", i);
}
- if(!ssl_printf(ssl, "num.query.class.%s"SQ"%lu\n",
+ if(!ssl_printf(ssl, "num.query.class.%s"SQ"%lu\n",
nm, (unsigned long)s->svr.qclass[i])) return 0;
}
if(!inhibit_zero || s->svr.qclass_big) {
- if(!ssl_printf(ssl, "num.query.class.other"SQ"%lu\n",
+ if(!ssl_printf(ssl, "num.query.class.other"SQ"%lu\n",
(unsigned long)s->svr.qclass_big)) return 0;
}
/* OPCODE */
} else {
snprintf(nm, sizeof(nm), "OPCODE%d", i);
}
- if(!ssl_printf(ssl, "num.query.opcode.%s"SQ"%lu\n",
+ if(!ssl_printf(ssl, "num.query.opcode.%s"SQ"%lu\n",
nm, (unsigned long)s->svr.qopcode[i])) return 0;
}
/* transport */
- if(!ssl_printf(ssl, "num.query.tcp"SQ"%lu\n",
+ if(!ssl_printf(ssl, "num.query.tcp"SQ"%lu\n",
(unsigned long)s->svr.qtcp)) return 0;
- if(!ssl_printf(ssl, "num.query.tcpout"SQ"%lu\n",
+ if(!ssl_printf(ssl, "num.query.tcpout"SQ"%lu\n",
(unsigned long)s->svr.qtcp_outgoing)) return 0;
if(!ssl_printf(ssl, "num.query.udpout"SQ"%lu\n",
(unsigned long)s->svr.qudp_outgoing)) return 0;
- if(!ssl_printf(ssl, "num.query.tls"SQ"%lu\n",
+ if(!ssl_printf(ssl, "num.query.tls"SQ"%lu\n",
(unsigned long)s->svr.qtls)) return 0;
- if(!ssl_printf(ssl, "num.query.tls.resume"SQ"%lu\n",
+ if(!ssl_printf(ssl, "num.query.tls.resume"SQ"%lu\n",
(unsigned long)s->svr.qtls_resume)) return 0;
- if(!ssl_printf(ssl, "num.query.ipv6"SQ"%lu\n",
+ if(!ssl_printf(ssl, "num.query.ipv6"SQ"%lu\n",
(unsigned long)s->svr.qipv6)) return 0;
if(!ssl_printf(ssl, "num.query.https"SQ"%lu\n",
(unsigned long)s->svr.qhttps)) return 0;
/* flags */
- if(!ssl_printf(ssl, "num.query.flags.QR"SQ"%lu\n",
+ if(!ssl_printf(ssl, "num.query.flags.QR"SQ"%lu\n",
(unsigned long)s->svr.qbit_QR)) return 0;
- if(!ssl_printf(ssl, "num.query.flags.AA"SQ"%lu\n",
+ if(!ssl_printf(ssl, "num.query.flags.AA"SQ"%lu\n",
(unsigned long)s->svr.qbit_AA)) return 0;
- if(!ssl_printf(ssl, "num.query.flags.TC"SQ"%lu\n",
+ if(!ssl_printf(ssl, "num.query.flags.TC"SQ"%lu\n",
(unsigned long)s->svr.qbit_TC)) return 0;
- if(!ssl_printf(ssl, "num.query.flags.RD"SQ"%lu\n",
+ if(!ssl_printf(ssl, "num.query.flags.RD"SQ"%lu\n",
(unsigned long)s->svr.qbit_RD)) return 0;
- if(!ssl_printf(ssl, "num.query.flags.RA"SQ"%lu\n",
+ if(!ssl_printf(ssl, "num.query.flags.RA"SQ"%lu\n",
(unsigned long)s->svr.qbit_RA)) return 0;
- if(!ssl_printf(ssl, "num.query.flags.Z"SQ"%lu\n",
+ if(!ssl_printf(ssl, "num.query.flags.Z"SQ"%lu\n",
(unsigned long)s->svr.qbit_Z)) return 0;
- if(!ssl_printf(ssl, "num.query.flags.AD"SQ"%lu\n",
+ if(!ssl_printf(ssl, "num.query.flags.AD"SQ"%lu\n",
(unsigned long)s->svr.qbit_AD)) return 0;
- if(!ssl_printf(ssl, "num.query.flags.CD"SQ"%lu\n",
+ if(!ssl_printf(ssl, "num.query.flags.CD"SQ"%lu\n",
(unsigned long)s->svr.qbit_CD)) return 0;
- if(!ssl_printf(ssl, "num.query.edns.present"SQ"%lu\n",
+ if(!ssl_printf(ssl, "num.query.edns.present"SQ"%lu\n",
(unsigned long)s->svr.qEDNS)) return 0;
- if(!ssl_printf(ssl, "num.query.edns.DO"SQ"%lu\n",
+ if(!ssl_printf(ssl, "num.query.edns.DO"SQ"%lu\n",
(unsigned long)s->svr.qEDNS_DO)) return 0;
/* RCODE */
} else {
snprintf(nm, sizeof(nm), "RCODE%d", i);
}
- if(!ssl_printf(ssl, "num.answer.rcode.%s"SQ"%lu\n",
+ if(!ssl_printf(ssl, "num.answer.rcode.%s"SQ"%lu\n",
nm, (unsigned long)s->svr.ans_rcode[i])) return 0;
}
if(!inhibit_zero || s->svr.ans_rcode_nodata) {
- if(!ssl_printf(ssl, "num.answer.rcode.nodata"SQ"%lu\n",
+ if(!ssl_printf(ssl, "num.answer.rcode.nodata"SQ"%lu\n",
(unsigned long)s->svr.ans_rcode_nodata)) return 0;
}
/* iteration */
- if(!ssl_printf(ssl, "num.query.ratelimited"SQ"%lu\n",
+ if(!ssl_printf(ssl, "num.query.ratelimited"SQ"%lu\n",
(unsigned long)s->svr.queries_ratelimited)) return 0;
/* validation */
- if(!ssl_printf(ssl, "num.answer.secure"SQ"%lu\n",
+ if(!ssl_printf(ssl, "num.answer.secure"SQ"%lu\n",
(unsigned long)s->svr.ans_secure)) return 0;
- if(!ssl_printf(ssl, "num.answer.bogus"SQ"%lu\n",
+ if(!ssl_printf(ssl, "num.answer.bogus"SQ"%lu\n",
(unsigned long)s->svr.ans_bogus)) return 0;
- if(!ssl_printf(ssl, "num.rrset.bogus"SQ"%lu\n",
+ if(!ssl_printf(ssl, "num.rrset.bogus"SQ"%lu\n",
(unsigned long)s->svr.rrset_bogus)) return 0;
- if(!ssl_printf(ssl, "num.query.aggressive.NOERROR"SQ"%lu\n",
+ if(!ssl_printf(ssl, "num.query.aggressive.NOERROR"SQ"%lu\n",
(unsigned long)s->svr.num_neg_cache_noerror)) return 0;
- if(!ssl_printf(ssl, "num.query.aggressive.NXDOMAIN"SQ"%lu\n",
+ if(!ssl_printf(ssl, "num.query.aggressive.NXDOMAIN"SQ"%lu\n",
(unsigned long)s->svr.num_neg_cache_nxdomain)) return 0;
/* threat detection */
- if(!ssl_printf(ssl, "unwanted.queries"SQ"%lu\n",
+ if(!ssl_printf(ssl, "unwanted.queries"SQ"%lu\n",
(unsigned long)s->svr.unwanted_queries)) return 0;
- if(!ssl_printf(ssl, "unwanted.replies"SQ"%lu\n",
+ if(!ssl_printf(ssl, "unwanted.replies"SQ"%lu\n",
(unsigned long)s->svr.unwanted_replies)) return 0;
/* cache counts */
if(!ssl_printf(ssl, "msg.cache.count"SQ"%u\n",
(unsigned)s->svr.infra_cache_count)) return 0;
if(!ssl_printf(ssl, "key.cache.count"SQ"%u\n",
(unsigned)s->svr.key_cache_count)) return 0;
+ /* max collisions */
+ if(!ssl_printf(ssl, "msg.cache.max_collisions"SQ"%u\n",
+ (unsigned)s->svr.msg_cache_max_collisions)) return 0;
+ if(!ssl_printf(ssl, "rrset.cache.max_collisions"SQ"%u\n",
+ (unsigned)s->svr.rrset_cache_max_collisions)) return 0;
/* applied RPZ actions */
for(i=0; i<UB_STATS_RPZ_ACTION_NUM; i++) {
if(i == RPZ_NO_OVERRIDE_ACTION)
if(!ssl_printf(ssl, "num.query.subnet_cache"SQ"%lu\n",
(unsigned long)s->svr.num_query_subnet_cache)) return 0;
#endif /* CLIENT_SUBNET */
+#ifdef USE_CACHEDB
+ if(!ssl_printf(ssl, "num.query.cachedb"SQ"%lu\n",
+ (unsigned long)s->svr.num_query_cachedb)) return 0;
+#endif /* USE_CACHEDB */
return 1;
}
}
/* print the thread statistics */
total.mesh_time_median /= (double)daemon->num;
- if(!print_stats(ssl, "total", &total))
+ if(!print_stats(ssl, "total", &total))
return;
if(!print_uptime(ssl, worker, reset))
return;
return;
if(!print_hist(ssl, &total))
return;
- if(!print_ext(ssl, &total))
+ if(!print_ext(ssl, &total, daemon->cfg->stat_inhibit_zero))
return;
}
}
return 0;
}
lock_rw_wrlock(&zones->lock);
- if((z=local_zones_find(zones, nm, nmlen,
+ if((z=local_zones_find(zones, nm, nmlen,
nmlabs, LDNS_RR_CLASS_IN))) {
/* already present in tree */
lock_rw_wrlock(&z->lock);
lock_rw_unlock(&zones->lock);
return 1;
}
- if(!local_zones_add_zone(zones, nm, nmlen,
+ if(!local_zones_add_zone(zones, nm, nmlen,
nmlabs, LDNS_RR_CLASS_IN, t)) {
lock_rw_unlock(&zones->lock);
ssl_printf(ssl, "error out of memory\n");
if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs))
return 0;
lock_rw_wrlock(&zones->lock);
- if((z=local_zones_find(zones, nm, nmlen,
+ if((z=local_zones_find(zones, nm, nmlen,
nmlabs, LDNS_RR_CLASS_IN))) {
/* present in tree */
local_zones_del_zone(zones, z);
if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs))
return;
t = sldns_get_rr_type_by_name(arg2);
+ if(t == 0 && strcmp(arg2, "TYPE0") != 0) {
+ return;
+ }
do_cache_remove(worker, nm, nmlen, t, LDNS_RR_CLASS_IN);
-
+
free(nm);
send_ok(ssl);
}
struct del_info* inf = (struct del_info*)arg;
struct ub_packed_rrset_key* k = (struct ub_packed_rrset_key*)e->key;
if(dname_subdomain_c(k->rk.dname, inf->name)) {
- struct packed_rrset_data* d =
+ struct packed_rrset_data* d =
(struct packed_rrset_data*)e->data;
if(d->ttl > inf->expired) {
d->ttl = inf->expired;
inf.num_rrsets = 0;
inf.num_msgs = 0;
inf.num_keys = 0;
- slabhash_traverse(&worker->env.rrset_cache->table, 1,
+ slabhash_traverse(&worker->env.rrset_cache->table, 1,
&zone_del_rrset, &inf);
slabhash_traverse(worker->env.msg_cache, 1, &zone_del_msg, &inf);
/* and validator cache */
if(worker->env.key_cache) {
- slabhash_traverse(worker->env.key_cache->slab, 1,
+ slabhash_traverse(worker->env.key_cache->slab, 1,
&zone_del_kcache, &inf);
}
free(nm);
(void)ssl_printf(ssl, "ok removed %lu rrsets, %lu messages "
- "and %lu key entries\n", (unsigned long)inf.num_rrsets,
+ "and %lu key entries\n", (unsigned long)inf.num_rrsets,
(unsigned long)inf.num_msgs, (unsigned long)inf.num_keys);
}
inf.num_rrsets = 0;
inf.num_msgs = 0;
inf.num_keys = 0;
- slabhash_traverse(&worker->env.rrset_cache->table, 1,
+ slabhash_traverse(&worker->env.rrset_cache->table, 1,
&bogus_del_rrset, &inf);
slabhash_traverse(worker->env.msg_cache, 1, &bogus_del_msg, &inf);
/* and validator cache */
if(worker->env.key_cache) {
- slabhash_traverse(worker->env.key_cache->slab, 1,
+ slabhash_traverse(worker->env.key_cache->slab, 1,
&bogus_del_kcache, &inf);
}
(void)ssl_printf(ssl, "ok removed %lu rrsets, %lu messages "
- "and %lu key entries\n", (unsigned long)inf.num_rrsets,
+ "and %lu key entries\n", (unsigned long)inf.num_rrsets,
(unsigned long)inf.num_msgs, (unsigned long)inf.num_keys);
}
inf.num_rrsets = 0;
inf.num_msgs = 0;
inf.num_keys = 0;
- slabhash_traverse(&worker->env.rrset_cache->table, 1,
+ slabhash_traverse(&worker->env.rrset_cache->table, 1,
&negative_del_rrset, &inf);
slabhash_traverse(worker->env.msg_cache, 1, &negative_del_msg, &inf);
/* and validator cache */
if(worker->env.key_cache) {
- slabhash_traverse(worker->env.key_cache->slab, 1,
+ slabhash_traverse(worker->env.key_cache->slab, 1,
&negative_del_kcache, &inf);
}
(void)ssl_printf(ssl, "ok removed %lu rrsets, %lu messages "
- "and %lu key entries\n", (unsigned long)inf.num_rrsets,
+ "and %lu key entries\n", (unsigned long)inf.num_rrsets,
(unsigned long)inf.num_msgs, (unsigned long)inf.num_keys);
}
do_cache_remove(w, nm, nmlen, LDNS_RR_TYPE_PTR, LDNS_RR_CLASS_IN);
do_cache_remove(w, nm, nmlen, LDNS_RR_TYPE_SRV, LDNS_RR_CLASS_IN);
do_cache_remove(w, nm, nmlen, LDNS_RR_TYPE_NAPTR, LDNS_RR_CLASS_IN);
-
+ do_cache_remove(w, nm, nmlen, LDNS_RR_TYPE_SVCB, LDNS_RR_CLASS_IN);
+ do_cache_remove(w, nm, nmlen, LDNS_RR_TYPE_HTTPS, LDNS_RR_CLASS_IN);
+
free(nm);
send_ok(ssl);
}
uptime = (time_t)time(NULL) - (time_t)worker->daemon->time_boot.tv_sec;
if(!ssl_printf(ssl, "uptime: " ARG_LL "d seconds\n", (long long)uptime))
return;
- if(!ssl_printf(ssl, "options:%s%s%s%s\n" ,
+ if(!ssl_printf(ssl, "options:%s%s%s%s\n" ,
(worker->daemon->reuseport?" reuseport":""),
(worker->daemon->rc->accept_list?" control":""),
(worker->daemon->rc->accept_list && worker->daemon->rc->use_cert?"(ssl)":""),
/** get age for the mesh state */
static void
-get_mesh_age(struct mesh_state* m, char* buf, size_t len,
+get_mesh_age(struct mesh_state* m, char* buf, size_t len,
struct module_env* env)
{
if(m->reply_list) {
/** get status of a mesh state */
static void
-get_mesh_status(struct mesh_area* mesh, struct mesh_state* m,
+get_mesh_status(struct mesh_area* mesh, struct mesh_state* m,
char* buf, size_t len)
{
enum module_ext_state s = m->s.ext_state[m->s.curmod];
snprintf(buf, len, " ");
l = strlen(buf);
buf += l; len -= l;
- addr_to_str(&e->qsent->addr, e->qsent->addrlen,
+ addr_to_str(&e->qsent->addr, e->qsent->addrlen,
buf, len);
l = strlen(buf);
buf += l; len -= l;
dname_str(m->s.qinfo.qname, buf);
get_mesh_age(m, timebuf, sizeof(timebuf), &worker->env);
get_mesh_status(mesh, m, statbuf, sizeof(statbuf));
- if(!ssl_printf(ssl, "%3d %4s %2s %s %s %s\n",
+ if(!ssl_printf(ssl, "%3d %4s %2s %s %s %s\n",
num, (t?t:"TYPE??"), (c?c:"CLASS??"), buf, timebuf,
statbuf)) {
free(t);
free(nm);
send_ok(ssl);
}
-
+
/** do the set_option command */
static void
do_set_option(RES* ssl, struct worker* worker, char* arg)
RBTREE_FOR(z, struct local_zone*, &zones->ztree) {
lock_rw_rdlock(&z->lock);
dname_str(z->name, buf);
- if(!ssl_printf(ssl, "%s %s\n", buf,
+ if(!ssl_printf(ssl, "%s %s\n", buf,
local_zone_type2str(z->type))) {
/* failure to print */
lock_rw_unlock(&z->lock);
distribute_cmd(struct daemon_remote* rc, RES* ssl, char* cmd)
{
int i;
- if(!cmd || !ssl)
+ if(!cmd || !ssl)
return;
/* skip i=0 which is me */
for(i=1; i<rc->worker->daemon->num; i++) {
/** execute a remote control command */
static void
-execute_cmd(struct daemon_remote* rc, RES* ssl, char* cmd,
+execute_cmd(struct daemon_remote* rc, RES* ssl, char* cmd,
struct worker* worker)
{
char* p = skipwhite(cmd);
if(cmdcmp(p, "stop", 4)) {
do_stop(ssl, worker);
return;
+ } else if(cmdcmp(p, "reload_keep_cache", 17)) {
+ do_reload(ssl, worker, 1);
+ return;
} else if(cmdcmp(p, "reload", 6)) {
- do_reload(ssl, worker);
+ do_reload(ssl, worker, 0);
return;
} else if(cmdcmp(p, "stats_noreset", 13)) {
do_stats(ssl, worker, 0);
}
}
-void
+void
daemon_remote_exec(struct worker* worker)
{
/* read the cmd string */
return 0;
}
-int remote_control_callback(struct comm_point* c, void* arg, int err,
+int remote_control_callback(struct comm_point* c, void* arg, int err,
struct comm_reply* ATTR_UNUSED(rep))
{
RES res;
struct daemon_remote* rc = s->rc;
int r;
if(err != NETEVENT_NOERROR) {
- if(err==NETEVENT_TIMEOUT)
+ if(err==NETEVENT_TIMEOUT)
log_err("remote control timed out");
clean_point(rc, s);
return 0;
#ifndef DAEMON_REMOTE_H
#define DAEMON_REMOTE_H
#ifdef HAVE_OPENSSL_SSL_H
-#include "openssl/ssl.h"
+#include <openssl/ssl.h>
#endif
struct config_file;
struct listen_list;
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
* This software is open source.
- *
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
- *
+ *
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
- *
+ *
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
- *
+ *
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
- *
+ *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
log_info("server stats for thread %d: %u queries, "
"%u answers from cache, %u recursions, %u prefetch, %u rejected by "
"ip ratelimiting",
- threadnum, (unsigned)stats->num_queries,
- (unsigned)(stats->num_queries -
+ threadnum, (unsigned)stats->num_queries,
+ (unsigned)(stats->num_queries -
stats->num_queries_missed_cache),
(unsigned)stats->num_queries_missed_cache,
(unsigned)stats->num_queries_prefetch,
s->svr.ans_rcode[i] += (long long)worker->env.mesh->ans_rcode[i];
for(i=0; i<UB_STATS_RPZ_ACTION_NUM; i++)
s->svr.rpz_action[i] += (long long)worker->env.mesh->rpz_action[i];
- timehist_export(worker->env.mesh->histogram, s->svr.hist,
+ timehist_export(worker->env.mesh->histogram, s->svr.hist,
NUM_BUCKETS_HIST);
/* values from outside network */
s->svr.unwanted_replies = (long long)worker->back->unwanted_replies;
s->svr.queries_ratelimited = (long long)get_queries_ratelimit(worker, reset);
/* get cache sizes */
- s->svr.msg_cache_count = (long long)count_slabhash_entries(worker->env.msg_cache);
- s->svr.rrset_cache_count = (long long)count_slabhash_entries(&worker->env.rrset_cache->table);
+ get_slabhash_stats(worker->env.msg_cache,
+ &s->svr.msg_cache_count, &s->svr.msg_cache_max_collisions);
+ get_slabhash_stats(&worker->env.rrset_cache->table,
+ &s->svr.rrset_cache_count, &s->svr.rrset_cache_max_collisions);
s->svr.infra_cache_count = (long long)count_slabhash_entries(worker->env.infra_cache->hosts);
if(worker->env.key_cache)
s->svr.key_cache_count = (long long)count_slabhash_entries(worker->env.key_cache->slab);
s->svr.num_query_subnet = 0;
s->svr.num_query_subnet_cache = 0;
#endif
+#ifdef USE_CACHEDB
+ s->svr.num_query_cachedb = (long long)worker->env.mesh->ans_cachedb;
+#else
+ s->svr.num_query_cachedb = 0;
+#endif
/* get tcp accept usage */
s->svr.tcp_accept_usage = 0;
struct ub_stats_info s;
server_stats_compile(worker, &s, reset);
verbose(VERB_ALGO, "write stats replymsg");
- if(!tube_write_msg(worker->daemon->workers[0]->cmd,
+ if(!tube_write_msg(worker->daemon->workers[0]->cmd,
(uint8_t*)&s, sizeof(s), 0))
fatal_exit("could not write stat values over cmd channel");
}
{
total->svr.num_queries += a->svr.num_queries;
total->svr.num_queries_ip_ratelimited += a->svr.num_queries_ip_ratelimited;
+ total->svr.num_queries_cookie_valid += a->svr.num_queries_cookie_valid;
+ total->svr.num_queries_cookie_client += a->svr.num_queries_cookie_client;
+ total->svr.num_queries_cookie_invalid += a->svr.num_queries_cookie_invalid;
total->svr.num_queries_missed_cache += a->svr.num_queries_missed_cache;
total->svr.num_queries_prefetch += a->svr.num_queries_prefetch;
+ total->svr.num_queries_timed_out += a->svr.num_queries_timed_out;
+ if (total->svr.max_query_time_us < a->svr.max_query_time_us)
+ total->svr.max_query_time_us = a->svr.max_query_time_us;
total->svr.sum_query_list_size += a->svr.sum_query_list_size;
total->svr.ans_expired += a->svr.ans_expired;
#ifdef USE_DNSCRYPT
total->svr.unwanted_replies += a->svr.unwanted_replies;
total->svr.unwanted_queries += a->svr.unwanted_queries;
total->svr.tcp_accept_usage += a->svr.tcp_accept_usage;
+#ifdef USE_CACHEDB
+ total->svr.num_query_cachedb += a->svr.num_query_cachedb;
+#endif
for(i=0; i<UB_STATS_QTYPE_NUM; i++)
total->svr.qtype[i] += a->svr.qtype[i];
for(i=0; i<UB_STATS_QCLASS_NUM; i++)
if(c->ssl != NULL) {
stats->qtls++;
#ifdef HAVE_SSL
- if(SSL_session_reused(c->ssl))
+ if(SSL_session_reused(c->ssl))
stats->qtls_resume++;
#endif
if(c->type == comm_http)
stats->ans_rcode_nodata ++;
}
}
+
+void server_stats_downstream_cookie(struct ub_server_stats* stats,
+ struct edns_data* edns)
+{
+ if(!(edns->edns_present && edns->cookie_present)) return;
+ if(edns->cookie_valid) {
+ stats->num_queries_cookie_valid++;
+ } else if(edns->cookie_client) {
+ stats->num_queries_cookie_client++;
+ } else {
+ stats->num_queries_cookie_invalid++;
+ }
+}
*/
void server_stats_insrcode(struct ub_server_stats* stats, struct sldns_buffer* buf);
+/**
+ * Add DNS Cookie stats for this query
+ * @param stats: the stats
+ * @param edns: edns record
+ */
+void server_stats_downstream_cookie(struct ub_server_stats* stats,
+ struct edns_data* edns);
#endif /* DAEMON_STATS_H */
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
* This software is open source.
- *
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
- *
+ *
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
- *
+ *
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
- *
+ *
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
- *
+ *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
#include "util/fptr_wlist.h"
#include "util/tube.h"
#include "util/edns.h"
+#include "util/timeval_func.h"
#include "iterator/iter_fwd.h"
#include "iterator/iter_hints.h"
#include "iterator/iter_utils.h"
/** Report on memory usage by this thread and global */
static void
-worker_mem_report(struct worker* ATTR_UNUSED(worker),
+worker_mem_report(struct worker* ATTR_UNUSED(worker),
struct serviced_query* ATTR_UNUSED(cur_serv))
{
#ifdef UNBOUND_ALLOC_STATS
#ifdef CLIENT_SUBNET
size_t subnet = 0;
#endif /* CLIENT_SUBNET */
- if(verbosity < VERB_ALGO)
+ if(verbosity < VERB_ALGO)
return;
front = listen_get_mem(worker->front);
back = outnet_get_mem(worker->back);
rrset = slabhash_get_mem(&worker->env.rrset_cache->table);
infra = infra_get_mem(worker->env.infra_cache);
mesh = mesh_get_mem(worker->env.mesh);
- ac = alloc_get_mem(&worker->alloc);
+ ac = alloc_get_mem(worker->alloc);
superac = alloc_get_mem(&worker->daemon->superalloc);
anch = anchors_get_mem(worker->env.anchors);
iter = 0;
(&worker->env, i);
}
me = sizeof(*worker) + sizeof(*worker->base) + sizeof(*worker->comsig)
- + comm_point_get_mem(worker->cmd_com)
- + sizeof(worker->rndstate)
- + regional_get_mem(worker->scratchpad)
- + sizeof(*worker->env.scratch_buffer)
+ + comm_point_get_mem(worker->cmd_com)
+ + sizeof(worker->rndstate)
+ + regional_get_mem(worker->scratchpad)
+ + sizeof(*worker->env.scratch_buffer)
+ sldns_buffer_capacity(worker->env.scratch_buffer)
+ forwards_get_mem(worker->env.fwds)
+ hints_get_mem(worker->env.hints);
log_info("Memory conditions: %u front=%u back=%u mesh=%u msg=%u "
"rrset=%u infra=%u iter=%u val=%u subnet=%u anchors=%u "
"alloccache=%u globalalloccache=%u me=%u",
- (unsigned)total, (unsigned)front, (unsigned)back,
+ (unsigned)total, (unsigned)front, (unsigned)back,
(unsigned)mesh, (unsigned)msg, (unsigned)rrset, (unsigned)infra,
(unsigned)iter, (unsigned)val,
(unsigned)subnet, (unsigned)anch, (unsigned)ac,
log_info("Memory conditions: %u front=%u back=%u mesh=%u msg=%u "
"rrset=%u infra=%u iter=%u val=%u anchors=%u "
"alloccache=%u globalalloccache=%u me=%u",
- (unsigned)total, (unsigned)front, (unsigned)back,
- (unsigned)mesh, (unsigned)msg, (unsigned)rrset,
+ (unsigned)total, (unsigned)front, (unsigned)back,
+ (unsigned)mesh, (unsigned)msg, (unsigned)rrset,
(unsigned)infra, (unsigned)iter, (unsigned)val, (unsigned)anch,
(unsigned)ac, (unsigned)superac, (unsigned)me);
#endif /* CLIENT_SUBNET */
log_info("Total heap memory estimate: %u total-alloc: %u "
- "total-free: %u", (unsigned)total,
+ "total-free: %u", (unsigned)total,
(unsigned)unbound_mem_alloc, (unsigned)unbound_mem_freed);
#else /* no UNBOUND_ALLOC_STATS */
size_t val = 0;
#endif /* UNBOUND_ALLOC_STATS */
}
-void
+void
worker_send_cmd(struct worker* worker, enum worker_commands cmd)
{
uint32_t c = (uint32_t)htonl(cmd);
}
}
-int
-worker_handle_service_reply(struct comm_point* c, void* arg, int error,
+int
+worker_handle_service_reply(struct comm_point* c, void* arg, int error,
struct comm_reply* reply_info)
{
struct outbound_entry* e = (struct outbound_entry*)arg;
}
/* sanity check. */
if(!LDNS_QR_WIRE(sldns_buffer_begin(c->buffer))
- || LDNS_OPCODE_WIRE(sldns_buffer_begin(c->buffer)) !=
+ || LDNS_OPCODE_WIRE(sldns_buffer_begin(c->buffer)) !=
LDNS_PACKET_QUERY
|| LDNS_QDCOUNT(sldns_buffer_begin(c->buffer)) > 1) {
/* error becomes timeout for the module as if this reply
* never arrived. */
verbose(VERB_ALGO, "worker: bad reply handled as timeout");
- mesh_report_reply(worker->env.mesh, e, reply_info,
+ mesh_report_reply(worker->env.mesh, e, reply_info,
NETEVENT_TIMEOUT);
worker_mem_report(worker, sq);
return 0;
return err;
}
+/**
+ * Structure holding the result of the worker_check_request function.
+ * Based on configuration it could be called up to four times; ideally should
+ * be called once.
+ */
+struct check_request_result {
+ int checked;
+ int value;
+};
/** check request sanity.
* @param pkt: the wire packet to examine for sanity.
* @param worker: parameters for checking.
- * @return error code, 0 OK, or -1 discard.
+ * @param out: struct to update with the result.
*/
-static int
-worker_check_request(sldns_buffer* pkt, struct worker* worker)
+static void
+worker_check_request(sldns_buffer* pkt, struct worker* worker,
+ struct check_request_result* out)
{
+ if(out->checked) return;
+ out->checked = 1;
if(sldns_buffer_limit(pkt) < LDNS_HEADER_SIZE) {
verbose(VERB_QUERY, "request too short, discarded");
- return -1;
+ out->value = -1;
+ return;
}
- if(sldns_buffer_limit(pkt) > NORMAL_UDP_SIZE &&
+ if(sldns_buffer_limit(pkt) > NORMAL_UDP_SIZE &&
worker->daemon->cfg->harden_large_queries) {
verbose(VERB_QUERY, "request too large, discarded");
- return -1;
+ out->value = -1;
+ return;
}
if(LDNS_QR_WIRE(sldns_buffer_begin(pkt))) {
verbose(VERB_QUERY, "request has QR bit on, discarded");
- return -1;
+ out->value = -1;
+ return;
}
if(LDNS_TC_WIRE(sldns_buffer_begin(pkt))) {
LDNS_TC_CLR(sldns_buffer_begin(pkt));
verbose(VERB_QUERY, "request bad, has TC bit on");
- return worker_err_ratelimit(worker, LDNS_RCODE_FORMERR);
+ out->value = worker_err_ratelimit(worker, LDNS_RCODE_FORMERR);
+ return;
}
if(LDNS_OPCODE_WIRE(sldns_buffer_begin(pkt)) != LDNS_PACKET_QUERY &&
LDNS_OPCODE_WIRE(sldns_buffer_begin(pkt)) != LDNS_PACKET_NOTIFY) {
- verbose(VERB_QUERY, "request unknown opcode %d",
+ verbose(VERB_QUERY, "request unknown opcode %d",
LDNS_OPCODE_WIRE(sldns_buffer_begin(pkt)));
- return worker_err_ratelimit(worker, LDNS_RCODE_NOTIMPL);
+ out->value = worker_err_ratelimit(worker, LDNS_RCODE_NOTIMPL);
+ return;
}
if(LDNS_QDCOUNT(sldns_buffer_begin(pkt)) != 1) {
- verbose(VERB_QUERY, "request wrong nr qd=%d",
+ verbose(VERB_QUERY, "request wrong nr qd=%d",
LDNS_QDCOUNT(sldns_buffer_begin(pkt)));
- return worker_err_ratelimit(worker, LDNS_RCODE_FORMERR);
+ out->value = worker_err_ratelimit(worker, LDNS_RCODE_FORMERR);
+ return;
}
- if(LDNS_ANCOUNT(sldns_buffer_begin(pkt)) != 0 &&
+ if(LDNS_ANCOUNT(sldns_buffer_begin(pkt)) != 0 &&
(LDNS_ANCOUNT(sldns_buffer_begin(pkt)) != 1 ||
LDNS_OPCODE_WIRE(sldns_buffer_begin(pkt)) != LDNS_PACKET_NOTIFY)) {
- verbose(VERB_QUERY, "request wrong nr an=%d",
+ verbose(VERB_QUERY, "request wrong nr an=%d",
LDNS_ANCOUNT(sldns_buffer_begin(pkt)));
- return worker_err_ratelimit(worker, LDNS_RCODE_FORMERR);
+ out->value = worker_err_ratelimit(worker, LDNS_RCODE_FORMERR);
+ return;
}
if(LDNS_NSCOUNT(sldns_buffer_begin(pkt)) != 0) {
- verbose(VERB_QUERY, "request wrong nr ns=%d",
+ verbose(VERB_QUERY, "request wrong nr ns=%d",
LDNS_NSCOUNT(sldns_buffer_begin(pkt)));
- return worker_err_ratelimit(worker, LDNS_RCODE_FORMERR);
+ out->value = worker_err_ratelimit(worker, LDNS_RCODE_FORMERR);
+ return;
}
if(LDNS_ARCOUNT(sldns_buffer_begin(pkt)) > 1) {
- verbose(VERB_QUERY, "request wrong nr ar=%d",
+ verbose(VERB_QUERY, "request wrong nr ar=%d",
LDNS_ARCOUNT(sldns_buffer_begin(pkt)));
- return worker_err_ratelimit(worker, LDNS_RCODE_FORMERR);
+ out->value = worker_err_ratelimit(worker, LDNS_RCODE_FORMERR);
+ return;
}
- return 0;
+ out->value = 0;
+ return;
}
-void
+void
worker_handle_control_cmd(struct tube* ATTR_UNUSED(tube), uint8_t* msg,
size_t len, int error, void* arg)
{
/** check if a delegation is secure */
static enum sec_status
-check_delegation_secure(struct reply_info *rep)
+check_delegation_secure(struct reply_info *rep)
{
/* return smallest security status */
size_t i;
s = ((struct packed_rrset_data*)rep->rrsets[i]->entry.data)
->security;
if(s != sec_status_secure) {
- memmove(rep->rrsets+i, rep->rrsets+i+1,
- sizeof(struct ub_packed_rrset_key*)*
+ memmove(rep->rrsets+i, rep->rrsets+i+1,
+ sizeof(struct ub_packed_rrset_key*)*
(rep->rrset_count - i - 1));
- rep->ar_numrrsets--;
+ rep->ar_numrrsets--;
rep->rrset_count--;
i--;
}
/** answer nonrecursive query from the cache */
static int
answer_norec_from_cache(struct worker* worker, struct query_info* qinfo,
- uint16_t id, uint16_t flags, struct comm_reply* repinfo,
+ uint16_t id, uint16_t flags, struct comm_reply* repinfo,
struct edns_data* edns)
{
/* for a nonrecursive query return either:
* o an error (servfail; we try to avoid this)
* o a delegation (closest we have; this routine tries that)
- * o the answer (checked by answer_from_cache)
+ * o the answer (checked by answer_from_cache)
*
- * So, grab a delegation from the rrset cache.
+ * So, grab a delegation from the rrset cache.
* Then check if it needs validation, if so, this routine fails,
* so that iterator can prime and validator can verify rrsets.
*/
uint16_t udpsize = edns->udp_size;
int secure = 0;
time_t timenow = *worker->env.now;
- int must_validate = (!(flags&BIT_CD) || worker->env.cfg->ignore_cd)
+ int has_cd_bit = (flags&BIT_CD);
+ int must_validate = (!has_cd_bit || worker->env.cfg->ignore_cd)
&& worker->env.need_to_validate;
struct dns_msg *msg = NULL;
struct delegpt *dp;
- dp = dns_cache_find_delegation(&worker->env, qinfo->qname,
+ dp = dns_cache_find_delegation(&worker->env, qinfo->qname,
qinfo->qname_len, qinfo->qtype, qinfo->qclass,
worker->scratchpad, &msg, timenow, 0, NULL, 0);
if(!dp) { /* no delegation, need to reprime */
if(must_validate) {
switch(check_delegation_secure(msg->rep)) {
case sec_status_unchecked:
- /* some rrsets have not been verified yet, go and
+ /* some rrsets have not been verified yet, go and
* let validator do that */
return 0;
case sec_status_bogus:
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, "");
+ /* Attach the cached EDE (RFC8914) */
+ if(worker->env.cfg->ede &&
+ msg->rep->reason_bogus != LDNS_EDE_NONE) {
+ edns_opt_list_append_ede(&edns->opt_list_out,
+ worker->scratchpad, msg->rep->reason_bogus,
+ msg->rep->reason_bogus_str);
}
- error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
+ error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
&msg->qinfo, id, flags, edns);
if(worker->stats.extended) {
worker->stats.ans_bogus++;
worker->env.now_tv))
return 0;
msg->rep->flags |= BIT_QR|BIT_RA;
+ /* Attach the cached EDE (RFC8914) if CD bit is set and the answer is
+ * bogus. */
+ if(worker->env.cfg->ede && has_cd_bit &&
+ (check_delegation_secure(msg->rep) == sec_status_bogus ||
+ check_delegation_secure(msg->rep) == sec_status_secure_sentinel_fail) &&
+ msg->rep->reason_bogus != LDNS_EDE_NONE) {
+ edns_opt_list_append_ede(&edns->opt_list_out,
+ worker->scratchpad, msg->rep->reason_bogus,
+ msg->rep->reason_bogus_str);
+ }
if(!reply_info_answer_encode(&msg->qinfo, msg->rep, id, flags,
repinfo->c->buffer, 0, 1, worker->scratchpad,
udpsize, edns, (int)(edns->bits & EDNS_DO), secure)) {
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,
+ error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
&msg->qinfo, id, flags, edns);
}
if(worker->stats.extended) {
/* xxx_deny actions mean dropping the reply, unless the original reply
* was redirected to response-ip data. */
- if((actinfo.action == respip_deny ||
+ if(actinfo.action == respip_always_deny ||
+ ((actinfo.action == respip_deny ||
actinfo.action == respip_inform_deny) &&
- *encode_repp == rep)
+ *encode_repp == rep))
*encode_repp = NULL;
/* If address info is returned, it means the action should be an
uint16_t udpsize = edns->udp_size;
struct reply_info* encode_rep = rep;
struct reply_info* partial_rep = *partial_repp;
- int must_validate = (!(flags&BIT_CD) || worker->env.cfg->ignore_cd)
+ int has_cd_bit = (flags&BIT_CD);
+ int must_validate = (!has_cd_bit || worker->env.cfg->ignore_cd)
&& worker->env.need_to_validate;
*partial_repp = NULL; /* avoid accidental further pass */
if(worker->env.cfg->serve_expired_ttl &&
rep->serve_expired_ttl < timenow)
return 0;
+ /* Ignore expired failure answers */
+ if(FLAGS_GET_RCODE(rep->flags) !=
+ LDNS_RCODE_NOERROR &&
+ FLAGS_GET_RCODE(rep->flags) !=
+ LDNS_RCODE_NXDOMAIN &&
+ FLAGS_GET_RCODE(rep->flags) !=
+ LDNS_RCODE_YXDOMAIN)
+ return 0;
if(!rrset_array_lock(rep->ref, rep->rrset_count, 0))
return 0;
*is_expired_answer = 1;
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, "");
+ /* Attach the cached EDE (RFC8914) */
+ if(worker->env.cfg->ede && rep->reason_bogus != LDNS_EDE_NONE) {
+ edns_opt_list_append_ede(&edns->opt_list_out,
+ worker->scratchpad, rep->reason_bogus,
+ rep->reason_bogus_str);
}
error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
qinfo, id, flags, edns);
edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->ext_rcode = 0;
edns->bits &= EDNS_DO;
- if(!inplace_cb_reply_cache_call(&worker->env, qinfo, NULL, rep,
- (int)(flags&LDNS_RCODE_MASK), edns, repinfo, worker->scratchpad,
- worker->env.now_tv))
- goto bail_out;
*alias_rrset = NULL; /* avoid confusion if caller set it to non-NULL */
if((worker->daemon->use_response_ip || worker->daemon->use_rpz) &&
!partial_rep && !apply_respip_action(worker, qinfo, cinfo, rep,
goto bail_out;
}
} else {
- /* We don't check the global ede as this is a warning, not
- * an error */
- if (*is_expired_answer == 1 &&
+ 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, "");
}
+ /* Attach the cached EDE (RFC8914) if CD bit is set and the
+ * answer is bogus. */
+ if(*is_secure_answer == 0 &&
+ worker->env.cfg->ede && has_cd_bit &&
+ encode_rep->reason_bogus != LDNS_EDE_NONE) {
+ edns_opt_list_append_ede(&edns->opt_list_out,
+ worker->scratchpad, encode_rep->reason_bogus,
+ encode_rep->reason_bogus_str);
+ }
+ if(!inplace_cb_reply_cache_call(&worker->env, qinfo, NULL, encode_rep,
+ (int)(flags&LDNS_RCODE_MASK), edns, repinfo, worker->scratchpad,
+ worker->env.now_tv))
+ goto bail_out;
if(!reply_info_answer_encode(qinfo, encode_rep, id, flags,
repinfo->c->buffer, timenow, 1, worker->scratchpad,
udpsize, edns, (int)(edns->bits & EDNS_DO),
return 1;
bail_out:
- rrset_array_unlock_touch(worker->env.rrset_cache,
+ rrset_array_unlock_touch(worker->env.rrset_cache,
worker->scratchpad, rep->ref, rep->rrset_count);
return 0;
}
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);
+ PREFETCH_EXPIRY_ADD, rpz_passthru,
+ &repinfo->client_addr, opt_list);
return;
}
#endif
struct config_file* cfg = w->env.cfg;
if(qinfo->qtype != LDNS_RR_TYPE_ANY && qinfo->qtype != LDNS_RR_TYPE_TXT)
return 0;
- if(query_dname_compare(qinfo->qname,
+ if(query_dname_compare(qinfo->qname,
(uint8_t*)"\002id\006server") == 0 ||
- query_dname_compare(qinfo->qname,
+ query_dname_compare(qinfo->qname,
(uint8_t*)"\010hostname\004bind") == 0)
{
- if(cfg->hide_identity)
+ if(cfg->hide_identity)
return 0;
if(cfg->identity==NULL || cfg->identity[0]==0) {
char buf[MAXHOSTNAMELEN+1];
else chaos_replyonestr(pkt, cfg->identity, edns, w, repinfo);
return 1;
}
- if(query_dname_compare(qinfo->qname,
+ if(query_dname_compare(qinfo->qname,
(uint8_t*)"\007version\006server") == 0 ||
- query_dname_compare(qinfo->qname,
+ query_dname_compare(qinfo->qname,
(uint8_t*)"\007version\004bind") == 0)
{
- if(cfg->hide_version)
+ if(cfg->hide_version)
return 0;
if(cfg->version==NULL || cfg->version[0]==0)
chaos_replyonestr(pkt, PACKAGE_STRING, edns, w, repinfo);
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 acl_addr* acladdr, int ede)
+ struct acl_addr* acladdr, int ede,
+ struct check_request_result* check_result)
{
if(acl == deny) {
if(verbosity >= VERB_ALGO) {
if(worker->stats.extended)
worker->stats.unwanted_queries++;
- if(worker_check_request(c->buffer, worker) == -1) {
+ worker_check_request(c->buffer, worker, check_result);
+ if(check_result->value != 0) {
+ if(check_result->value != -1) {
+ LDNS_QR_SET(sldns_buffer_begin(c->buffer));
+ LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
+ check_result->value);
+ return 1;
+ }
comm_point_drop_reply(repinfo);
- return 0; /* discard this */
+ return 0;
}
/* worker_check_request() above guarantees that the buffer contains at
* least a header and that qdcount == 1
return 1;
}
LDNS_QR_SET(sldns_buffer_begin(c->buffer));
- LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
+ LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
LDNS_RCODE_REFUSED);
sldns_buffer_skip(c->buffer, (ssize_t)sizeof(uint16_t)); /* skip qtype */
/* 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,
+ 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),
static int
deny_refuse_all(struct comm_point* c, enum acl_access* acl,
struct worker* worker, struct comm_reply* repinfo,
- struct acl_addr** acladdr, int ede, int check_proxy)
+ struct acl_addr** acladdr, int ede, int check_proxy,
+ struct check_request_result* check_result)
{
if(check_proxy) {
*acladdr = acl_addr_lookup(worker->daemon->acl,
}
*acl = acl_get_control(*acladdr);
return deny_refuse(c, *acl, acl_deny, acl_refuse, worker, repinfo,
- *acladdr, ede);
+ *acladdr, ede, check_result);
}
static int
deny_refuse_non_local(struct comm_point* c, enum acl_access acl,
struct worker* worker, struct comm_reply* repinfo,
- struct acl_addr* acladdr, int ede)
+ struct acl_addr* acladdr, int ede,
+ struct check_request_result* check_result)
{
return deny_refuse(c, acl, acl_deny_non_local, acl_refuse_non_local,
- worker, repinfo, acladdr, ede);
+ worker, repinfo, acladdr, ede, check_result);
+}
+
+/* Returns 1 if the ip rate limit check can happen before EDNS parsing,
+ * else 0 */
+static int
+pre_edns_ip_ratelimit_check(enum acl_access acl)
+{
+ if(acl == acl_allow_cookie) return 0;
+ return 1;
+}
+
+/* Check if the query is blocked by source IP rate limiting.
+ * Returns 1 if it passes the check, 0 otherwise. */
+static int
+check_ip_ratelimit(struct worker* worker, struct sockaddr_storage* addr,
+ socklen_t addrlen, int has_cookie, sldns_buffer* pkt)
+{
+ if(!infra_ip_ratelimit_inc(worker->env.infra_cache, addr, addrlen,
+ *worker->env.now, has_cookie,
+ worker->env.cfg->ip_ratelimit_backoff, pkt)) {
+ /* See if we can pass through with slip factor */
+ if(!has_cookie && worker->env.cfg->ip_ratelimit_factor != 0 &&
+ ub_random_max(worker->env.rnd,
+ worker->env.cfg->ip_ratelimit_factor) == 0) {
+ char addrbuf[128];
+ addr_to_str(addr, addrlen, addrbuf, sizeof(addrbuf));
+ verbose(VERB_QUERY, "ip_ratelimit allowed through for "
+ "ip address %s because of slip in "
+ "ip_ratelimit_factor", addrbuf);
+ return 1;
+ }
+ return 0;
+ }
+ return 1;
}
int
struct edns_option* original_edns_list = NULL;
enum acl_access acl;
struct acl_addr* acladdr;
+ int pre_edns_ip_ratelimit = 1;
int rc = 0;
int need_drop = 0;
int is_expired_answer = 0;
int is_secure_answer = 0;
int rpz_passthru = 0;
+ long long wait_queue_time = 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. */
struct query_info* lookup_qinfo = &qinfo;
struct query_info qinfo_tmp; /* placeholder for lookup_qinfo */
struct respip_client_info* cinfo = NULL, cinfo_tmp;
+ struct timeval wait_time;
+ struct check_request_result check_result = {0,0};
memset(&qinfo, 0, sizeof(qinfo));
if((error != NETEVENT_NOERROR && error != NETEVENT_DONE)|| !repinfo) {
verbose(VERB_ALGO, "handle request called with err=%d", error);
return 0;
}
+
+ if (worker->env.cfg->sock_queue_timeout && timeval_isset(&c->recv_tv)) {
+ timeval_subtract(&wait_time, worker->env.now_tv, &c->recv_tv);
+ wait_queue_time = wait_time.tv_sec * 1000000 + wait_time.tv_usec;
+ if (worker->stats.max_query_time_us < wait_queue_time)
+ worker->stats.max_query_time_us = wait_queue_time;
+ if(wait_queue_time >
+ (long long)(worker->env.cfg->sock_queue_timeout * 1000000)) {
+ /* count and drop queries that were sitting in the socket queue too long */
+ worker->stats.num_queries_timed_out++;
+ return 0;
+ }
+ }
+
#ifdef USE_DNSCRYPT
repinfo->max_udp_size = worker->daemon->cfg->max_udp_size;
if(!dnsc_handle_curved_request(worker->daemon->dnscenv, repinfo)) {
if(c->dnscrypt && !repinfo->is_dnscrypted) {
char buf[LDNS_MAX_DOMAINLEN+1];
/* Check if this is unencrypted and asking for certs */
- if(worker_check_request(c->buffer, worker) != 0) {
+ worker_check_request(c->buffer, worker, &check_result);
+ if(check_result.value != 0) {
verbose(VERB_ALGO,
"dnscrypt: worker check request: bad query.");
log_addr(VERB_CLIENT,"from",&repinfo->client_addr,
if(worker->dtenv.log_client_query_messages) {
log_addr(VERB_ALGO, "request from client", &repinfo->client_addr, repinfo->client_addrlen);
log_addr(VERB_ALGO, "to local addr", (void*)repinfo->c->socket->addr->ai_addr, repinfo->c->socket->addr->ai_addrlen);
- dt_msg_send_client_query(&worker->dtenv, &repinfo->client_addr, (void*)repinfo->c->socket->addr->ai_addr, c->type, c->buffer);
+ dt_msg_send_client_query(&worker->dtenv, &repinfo->client_addr, (void*)repinfo->c->socket->addr->ai_addr, c->type, c->buffer,
+ ((worker->env.cfg->sock_queue_timeout && timeval_isset(&c->recv_tv))?&c->recv_tv:NULL));
}
#endif
/* Check deny/refuse ACLs */
if(repinfo->is_proxied) {
if((ret=deny_refuse_all(c, &acl, worker, repinfo, &acladdr,
- worker->env.cfg->ede, 1)) != -1) {
+ worker->env.cfg->ede, 1, &check_result)) != -1) {
if(ret == 1)
goto send_reply;
return ret;
}
}
if((ret=deny_refuse_all(c, &acl, worker, repinfo, &acladdr,
- worker->env.cfg->ede, 0)) != -1) {
+ worker->env.cfg->ede, 0, &check_result)) != -1) {
if(ret == 1)
goto send_reply;
return ret;
}
- if((ret=worker_check_request(c->buffer, worker)) != 0) {
+ worker_check_request(c->buffer, worker, &check_result);
+ if(check_result.value != 0) {
verbose(VERB_ALGO, "worker check request: bad query.");
log_addr(VERB_CLIENT,"from",&repinfo->client_addr, repinfo->client_addrlen);
- if(ret != -1) {
+ if(check_result.value != -1) {
LDNS_QR_SET(sldns_buffer_begin(c->buffer));
- LDNS_RCODE_SET(sldns_buffer_begin(c->buffer), ret);
+ LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
+ check_result.value);
return 1;
}
comm_point_drop_reply(repinfo);
}
worker->stats.num_queries++;
-
- /* check if this query should be dropped based on source ip rate limiting
- * NOTE: we always check the repinfo->client_address. IP ratelimiting is
- * implicitly disabled for proxies. */
- if(!infra_ip_ratelimit_inc(worker->env.infra_cache,
- &repinfo->client_addr, repinfo->client_addrlen,
- *worker->env.now,
- worker->env.cfg->ip_ratelimit_backoff, c->buffer)) {
- /* See if we are passed through with slip factor */
- if(worker->env.cfg->ip_ratelimit_factor != 0 &&
- ub_random_max(worker->env.rnd,
- worker->env.cfg->ip_ratelimit_factor) == 0) {
- char addrbuf[128];
- addr_to_str(&repinfo->client_addr,
- repinfo->client_addrlen, addrbuf,
- sizeof(addrbuf));
- verbose(VERB_QUERY, "ip_ratelimit allowed through for "
- "ip address %s because of slip in "
- "ip_ratelimit_factor", addrbuf);
- } else {
+ pre_edns_ip_ratelimit = pre_edns_ip_ratelimit_check(acl);
+
+ /* If the IP rate limiting check needs extra EDNS information (e.g.,
+ * DNS Cookies) postpone the check until after EDNS is parsed. */
+ if(pre_edns_ip_ratelimit) {
+ /* NOTE: we always check the repinfo->client_address.
+ * IP ratelimiting is implicitly disabled for proxies. */
+ if(!check_ip_ratelimit(worker, &repinfo->client_addr,
+ repinfo->client_addrlen, 0, c->buffer)) {
worker->stats.num_queries_ip_ratelimited++;
comm_point_drop_reply(repinfo);
return 0;
}
}
- /* see if query is in the cache */
if(!query_info_parse(&qinfo, c->buffer)) {
verbose(VERB_ALGO, "worker parse request: formerror.");
log_addr(VERB_CLIENT, "from", &repinfo->client_addr,
}
sldns_buffer_rewind(c->buffer);
LDNS_QR_SET(sldns_buffer_begin(c->buffer));
- LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
+ LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
LDNS_RCODE_FORMERR);
goto send_reply;
}
addr_to_str(&repinfo->client_addr, repinfo->client_addrlen, ip, sizeof(ip));
log_query_in(ip, qinfo.qname, qinfo.qtype, qinfo.qclass);
}
- if(qinfo.qtype == LDNS_RR_TYPE_AXFR ||
+ if(qinfo.qtype == LDNS_RR_TYPE_AXFR ||
qinfo.qtype == LDNS_RR_TYPE_IXFR) {
verbose(VERB_ALGO, "worker request: refused zone transfer.");
log_addr(VERB_CLIENT, "from", &repinfo->client_addr,
repinfo->client_addrlen);
sldns_buffer_rewind(c->buffer);
LDNS_QR_SET(sldns_buffer_begin(c->buffer));
- LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
+ LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
LDNS_RCODE_REFUSED);
if(worker->stats.extended) {
worker->stats.qtype[qinfo.qtype]++;
}
goto send_reply;
}
- if(qinfo.qtype == LDNS_RR_TYPE_OPT ||
+ if(qinfo.qtype == LDNS_RR_TYPE_OPT ||
qinfo.qtype == LDNS_RR_TYPE_TSIG ||
qinfo.qtype == LDNS_RR_TYPE_TKEY ||
qinfo.qtype == LDNS_RR_TYPE_MAILA ||
}
sldns_buffer_rewind(c->buffer);
LDNS_QR_SET(sldns_buffer_begin(c->buffer));
- LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
+ LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
LDNS_RCODE_FORMERR);
if(worker->stats.extended) {
worker->stats.qtype[qinfo.qtype]++;
}
goto send_reply;
}
- if((ret=parse_edns_from_query_pkt(c->buffer, &edns, worker->env.cfg, c,
- worker->scratchpad)) != 0) {
+ if((ret=parse_edns_from_query_pkt(
+ c->buffer, &edns, worker->env.cfg, c, repinfo,
+ (worker->env.now ? *worker->env.now : time(NULL)),
+ worker->scratchpad)) != 0) {
struct edns_data reply_edns;
verbose(VERB_ALGO, "worker parse edns: formerror.");
log_addr(VERB_CLIENT, "from", &repinfo->client_addr,
repinfo->client_addrlen);
memset(&reply_edns, 0, sizeof(reply_edns));
reply_edns.edns_present = 1;
- reply_edns.udp_size = EDNS_ADVERTISED_SIZE;
- LDNS_RCODE_SET(sldns_buffer_begin(c->buffer), ret);
error_encode(c->buffer, ret, &qinfo,
*(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
sldns_buffer_read_u16_at(c->buffer, 2), &reply_edns);
}
if(edns.edns_present) {
if(edns.edns_version != 0) {
- edns.ext_rcode = (uint8_t)(EDNS_RCODE_BADVERS>>4);
- edns.edns_version = EDNS_ADVERTISED_VERSION;
- edns.udp_size = EDNS_ADVERTISED_SIZE;
- edns.bits &= EDNS_DO;
edns.opt_list_in = NULL;
edns.opt_list_out = NULL;
edns.opt_list_inplace_cb_out = NULL;
- edns.padding_block_size = 0;
verbose(VERB_ALGO, "query with bad edns version.");
log_addr(VERB_CLIENT, "from", &repinfo->client_addr,
repinfo->client_addrlen);
- error_encode(c->buffer, EDNS_RCODE_BADVERS&0xf, &qinfo,
+ extended_error_encode(c->buffer, EDNS_RCODE_BADVERS, &qinfo,
*(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
- sldns_buffer_read_u16_at(c->buffer, 2), NULL);
- if(sldns_buffer_capacity(c->buffer) >=
- sldns_buffer_limit(c->buffer)+calc_edns_field_size(&edns))
- attach_edns_record(c->buffer, &edns);
+ sldns_buffer_read_u16_at(c->buffer, 2), 0, &edns);
regional_free_all(worker->scratchpad);
goto send_reply;
}
edns.udp_size = NORMAL_UDP_SIZE;
}
}
+
+ /* Get stats for cookies */
+ server_stats_downstream_cookie(&worker->stats, &edns);
+
+ /* If the IP rate limiting check was postponed, check now. */
+ if(!pre_edns_ip_ratelimit) {
+ /* NOTE: we always check the repinfo->client_address.
+ * IP ratelimiting is implicitly disabled for proxies. */
+ if(!check_ip_ratelimit(worker, &repinfo->client_addr,
+ repinfo->client_addrlen, edns.cookie_valid,
+ c->buffer)) {
+ worker->stats.num_queries_ip_ratelimited++;
+ comm_point_drop_reply(repinfo);
+ return 0;
+ }
+ }
+
+ /* "if, else if" sequence below deals with downstream DNS Cookies */
+ if(acl != acl_allow_cookie)
+ ; /* pass; No cookie downstream processing whatsoever */
+
+ else if(edns.cookie_valid)
+ ; /* pass; Valid cookie is good! */
+
+ else if(c->type != comm_udp)
+ ; /* pass; Stateful transport */
+
+ else if(edns.cookie_present) {
+ /* Cookie present, but not valid: Cookie was bad! */
+ extended_error_encode(c->buffer,
+ LDNS_EXT_RCODE_BADCOOKIE, &qinfo,
+ *(uint16_t*)(void *)
+ sldns_buffer_begin(c->buffer),
+ sldns_buffer_read_u16_at(c->buffer, 2),
+ 0, &edns);
+ regional_free_all(worker->scratchpad);
+ goto send_reply;
+ } else {
+ /* Cookie required, but no cookie present on UDP */
+ verbose(VERB_ALGO, "worker request: "
+ "need cookie or stateful transport");
+ log_addr(VERB_ALGO, "from",&repinfo->remote_addr
+ , repinfo->remote_addrlen);
+ EDNS_OPT_LIST_APPEND_EDE(&edns.opt_list_out,
+ worker->scratchpad, LDNS_EDE_OTHER,
+ "DNS Cookie needed for UDP replies");
+ error_encode(c->buffer,
+ (LDNS_RCODE_REFUSED|BIT_TC), &qinfo,
+ *(uint16_t*)(void *)
+ sldns_buffer_begin(c->buffer),
+ sldns_buffer_read_u16_at(c->buffer, 2),
+ &edns);
+ regional_free_all(worker->scratchpad);
+ goto send_reply;
+ }
+
if(edns.udp_size > worker->daemon->cfg->max_udp_size &&
c->type == comm_udp) {
verbose(VERB_QUERY,
repinfo->client_addrlen);
LDNS_QR_SET(sldns_buffer_begin(c->buffer));
LDNS_TC_SET(sldns_buffer_begin(c->buffer));
- LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
+ LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
LDNS_RCODE_SERVFAIL);
sldns_buffer_set_position(c->buffer, LDNS_HEADER_SIZE);
- sldns_buffer_write_at(c->buffer, 4,
+ sldns_buffer_write_at(c->buffer, 4,
(uint8_t*)"\0\0\0\0\0\0\0\0", 8);
sldns_buffer_flip(c->buffer);
regional_free_all(worker->scratchpad);
/* 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, acladdr,
- worker->env.cfg->ede)) != -1)
+ worker->env.cfg->ede, &check_result)) != -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) {
+ if(worker->env.cfg->ede) {
EDNS_OPT_LIST_APPEND_EDE(&edns.opt_list_out,
worker->scratchpad, LDNS_EDE_NOT_AUTHORITATIVE, "");
}
if(!LDNS_RD_WIRE(sldns_buffer_begin(c->buffer))) {
if(answer_norec_from_cache(worker, &qinfo,
- *(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
- sldns_buffer_read_u16_at(c->buffer, 2), repinfo,
+ *(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
+ sldns_buffer_read_u16_at(c->buffer, 2), repinfo,
&edns)) {
regional_free_all(worker->scratchpad);
goto send_reply;
return rc;
}
-void
+void
worker_sighandler(int sig, void* arg)
{
- /* note that log, print, syscalls here give race conditions.
+ /* note that log, print, syscalls here give race conditions.
* And cause hangups if the log-lock is held by the application. */
struct worker* worker = (struct worker*)arg;
switch(sig) {
comm_timer_set(worker->env.probe_timer, &tv);
}
-struct worker*
+struct worker*
worker_create(struct daemon* daemon, int id, int* ports, int n)
{
unsigned int seed;
- struct worker* worker = (struct worker*)calloc(1,
+ struct worker* worker = (struct worker*)calloc(1,
sizeof(struct worker));
- if(!worker)
+ if(!worker)
return NULL;
worker->numports = n;
worker->ports = (int*)memdup(ports, sizeof(int)*n);
}
int
-worker_init(struct worker* worker, struct config_file *cfg,
+worker_init(struct worker* worker, struct config_file *cfg,
struct listen_port* ports, int do_sigs)
{
#ifdef USE_DNSTAP
#endif
ub_thread_sig_unblock(SIGTERM);
#ifndef LIBEVENT_SIGNAL_PROBLEM
- worker->comsig = comm_signal_create(worker->base,
+ worker->comsig = comm_signal_create(worker->base,
worker_sighandler, worker);
- if(!worker->comsig
+ if(!worker->comsig
#ifdef SIGHUP
|| !comm_signal_bind(worker->comsig, SIGHUP)
#endif
return 0;
}
#endif /* LIBEVENT_SIGNAL_PROBLEM */
- if(!daemon_remote_open_accept(worker->daemon->rc,
+ if(!daemon_remote_open_accept(worker->daemon->rc,
worker->daemon->rc_ports, worker)) {
worker_delete(worker);
return 0;
return 0;
}
worker->back = outside_network_create(worker->base,
- cfg->msg_buffer_size, (size_t)cfg->outgoing_num_ports,
- cfg->out_ifs, cfg->num_out_ifs, cfg->do_ip4, cfg->do_ip6,
+ cfg->msg_buffer_size, (size_t)cfg->outgoing_num_ports,
+ cfg->out_ifs, cfg->num_out_ifs, cfg->do_ip4, cfg->do_ip6,
cfg->do_tcp?cfg->outgoing_num_tcp:0, cfg->ip_dscp,
worker->daemon->env->infra_cache, worker->rndstate,
cfg->use_caps_bits_for_id, worker->ports, worker->numports,
worker_delete(worker);
return 0;
}
- worker->stat_timer = comm_timer_create(worker->base,
+ worker->stat_timer = comm_timer_create(worker->base,
worker_stat_timer_cb, worker);
if(!worker->stat_timer) {
log_err("could not create statistics timer");
}
- /* we use the msg_buffer_size as a good estimate for what the
+ /* we use the msg_buffer_size as a good estimate for what the
* user wants for memory usage sizes */
worker->scratchpad = regional_create_custom(cfg->msg_buffer_size);
if(!worker->scratchpad) {
}
server_stats_init(&worker->stats, cfg);
- alloc_init(&worker->alloc, &worker->daemon->superalloc,
- worker->thread_num);
- alloc_set_id_cleanup(&worker->alloc, &worker_alloc_cleanup, worker);
+ worker->alloc = worker->daemon->worker_allocs[worker->thread_num];
+ alloc_set_id_cleanup(worker->alloc, &worker_alloc_cleanup, worker);
worker->env = *worker->daemon->env;
comm_base_timept(worker->base, &worker->env.now, &worker->env.now_tv);
worker->env.worker = worker;
worker->env.worker_base = worker->base;
worker->env.send_query = &worker_send_query;
- worker->env.alloc = &worker->alloc;
+ worker->env.alloc = worker->alloc;
worker->env.outnet = worker->back;
worker->env.rnd = worker->rndstate;
/* If case prefetch is triggered, the corresponding mesh will clear
worker_mem_report(worker, NULL);
/* if statistics enabled start timer */
if(worker->env.cfg->stat_interval > 0) {
- verbose(VERB_ALGO, "set statistics interval %d secs",
+ verbose(VERB_ALGO, "set statistics interval %d secs",
worker->env.cfg->stat_interval);
worker_restart_timer(worker);
}
return 1;
}
-void
+void
worker_work(struct worker* worker)
{
comm_base_dispatch(worker->base);
}
-void
+void
worker_delete(struct worker* worker)
{
- if(!worker)
+ if(!worker)
return;
if(worker->env.mesh && verbosity >= VERB_OPS) {
server_stats_log(&worker->stats, worker, worker->thread_num);
#endif /* USE_DNSTAP */
comm_base_delete(worker->base);
ub_randfree(worker->rndstate);
- alloc_clear(&worker->alloc);
+ /* don't touch worker->alloc, as it's maintained in daemon */
regional_destroy(worker->env.scratch);
regional_destroy(worker->scratchpad);
free(worker);
struct worker* worker = q->env->worker;
struct outbound_entry* e = (struct outbound_entry*)regional_alloc(
q->region, sizeof(*e));
- if(!e)
+ if(!e)
return NULL;
e->qstate = q;
e->qsent = outnet_serviced_query(worker->back, qinfo, flags, dnssec,
return e;
}
-void
+void
worker_alloc_cleanup(void* arg)
{
struct worker* worker = (struct worker*)arg;
return 0;
}
-int libworker_handle_service_reply(struct comm_point* ATTR_UNUSED(c),
+int libworker_handle_service_reply(struct comm_point* ATTR_UNUSED(c),
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
struct comm_reply* ATTR_UNUSED(reply_info))
{
/** do we need to restart or quit (on signal) */
int need_to_exit;
/** allocation cache for this thread */
- struct alloc_cache alloc;
+ struct alloc_cache *alloc;
/** per thread statistics */
struct ub_server_stats stats;
/** thread scratch regional */
/** dnstap environment, changed for this thread */
struct dt_env dtenv;
#endif
+ /** reuse existing cache on reload if other conditions allow it. */
+ int reuse_cache;
};
/**
******************************************************************************/
/**
- * This is the default DNS64 prefix that is used whent he dns64 module is listed
+ * This is the default DNS64 prefix that is used when the dns64 module is listed
* in module-config but when the dns64-prefix variable is not present.
*/
static const char DEFAULT_DNS64_PREFIX[] = "64:ff9b::/96";
cp = construct_reply_info_base(super->region, rep->flags, rep->qdcount,
rep->ttl, rep->prefetch_ttl, rep->serve_expired_ttl,
rep->an_numrrsets, rep->ns_numrrsets, rep->ar_numrrsets,
- rep->rrset_count, rep->security);
+ rep->rrset_count, rep->security, LDNS_EDE_NONE);
if(!cp)
return;
struct sockaddr_storage *qsock,
struct sockaddr_storage *rsock,
enum comm_point_type cptype,
- sldns_buffer *qmsg)
+ sldns_buffer *qmsg,
+ struct timeval* tstamp)
{
struct dt_msg dm;
struct timeval qtime;
- gettimeofday(&qtime, NULL);
+ if(tstamp)
+ memcpy(&qtime, tstamp, sizeof(qtime));
+ else gettimeofday(&qtime, NULL);
/* type */
dt_msg_init(env, &dm, DNSTAP__MESSAGE__TYPE__CLIENT_QUERY);
+25 August 2023: Wouter
+ - Fix compile error on NetBSD in util/netevent.h.
+
+23 August 2023: Wouter
+ - Tag for 1.18.0rc1 release.
+
+22 August 2023: Wouter
+ - Set version number to 1.18.0.
+
+21 August 2023: Wouter
+ - Debug Windows ci workflow.
+ - Fix windows ci workflow to install bison and flex.
+ - Fix for #925: unbound.service: Main process exited, code=killed,
+ status=11/SEGV. Fixes cachedb configuration handling.
+ - Fix #923: processQueryResponse() THROWAWAY should be mindful of
+ fail_reply.
+ - Fix unit test for unbound-control to work when threads are disabled,
+ and fix cache dump check.
+
+18 August 2023: Wouter
+ - Fix for iter_dec_attempts that could cause a hang, part of
+ capsforid and qname minimisation, depending on the settings.
+ - Fix uninitialized memory passed in padding bytes of cmsg to sendmsg.
+ - Fix stat_values test to work with dig that enables DNS cookies.
+
+17 August 2023: Wouter
+ - Merge PR #762: Downstream DNS Server Cookies a la RFC7873 and
+ RFC9018. Create server cookies for clients that send client cookies.
+ This needs to be explicitly turned on in the config file with:
+ `answer-cookie: yes`. A `cookie-secret:` can be configured for
+ anycast setups. Without one, a random cookie secret is generated.
+ The acl option `allow_cookie` allows queries with either a valid
+ cookie or over a stateful transport. The statistics output has
+ `queries_cookie_valid` and `queries_cookie_client` and
+ `queries_cookie_invalid` information. The `ip\-ratelimit\-cookie:`
+ value determines a rate limit for queries with cookies, if desired.
+ - Fix regional_alloc_init for potential unaligned source of the copy.
+ - Fix ip_ratelimit test to work with dig that enables DNS cookies.
+
+2 August 2023: George
+ - Move a cache reply callback in worker.c closer to the cache reply
+ generation.
+
+1 August 2023: George
+ - Merge #911 from natalie-reece: Exclude EDE before other EDNS options
+ when there isn't enough space.
+ - For #911: Try to trim EXTRA-TEXT (and LDNS_EDE_OTHER options
+ altogether) before giving up on attaching EDE options.
+ - More braces and formatting for Fix for EDNS EDE size calculation to
+ avoid future bugs.
+ - Fix to use the now cached EDE, if any, for CD_bit queries.
+
+1 August 2023: Wouter
+ - Fix for EDNS EDE size calculation.
+
+31 July 2023: George
+ - Merge #790 from Tom Carpay: Add support for EDE caching in cachedb
+ and subnetcache.
+
+31 July 2023: Wouter
+ - iana portlist update.
+
+30 July 2023: George
+ - Merge #759 from Tom Carpay: Add EDE (RFC8914) caching.
+
+28 July 2023: George
+ - Fix unused variable compile warning for kernel timestamps in
+ netevent.c
+
+21 July 2023: George
+ - Merge #857 from eaglegai: fix potential memory leaks when errors
+ happen.
+ - For #857: fix mixed declarations and code.
+ - Merge #118 from mibere: Changed verbosity level for Redis init &
+ deinit.
+ - Merge #390 from Frank Riley: Add missing callbacks to the python
+ module.
+ - Cleaner failure code for callback functions in interface.i.
+ - Merge #889 from borisVanhoof: Free memory in error case + remove
+ unused function.
+ - For #889: use netcat-openbsd instead of netcat-traditional.
+ - For #889: Account for num_detached_states before possible
+ mesh_state_delete when erroring out.
+
+20 July 2023: George
+ - Merge #909 from headshog: Numeric truncation when parsing TYPEXX and
+ CLASSXX representation.
+ - For #909: Fix return values.
+ - Merge #901 from Sergei Trofimovich: config: improve handling of
+ unknown modules.
+
+20 July 2023: Wouter
+ - For #909: Fix RR class comparison.
+
+14 July 2023: George
+ - More clear description of the different auth-zone behaviors on the
+ man page.
+
+13 July 2023: George
+ - Merge #880 from chipitsine: services/authzone.c: remove redundant
+ check.
+
+11 July 2023: George
+ - Merge #664 from tilan7763: Add prefetch support for subnet cache
+ entries.
+ - For #664: Easier code flow for subnetcache prefetching.
+ - For #664: Add testcase.
+ - For #664: Rename subnet_prefetch tests to subnet_global_prefetch to
+ differentiate from the new subnet prefetch support.
+
+3 July 2023: George
+ - Merge #739: Add SVCB dohpath support.
+ - Code cleanup for sldns_str2wire_svcparam_key_lookup.
+ - Merge #802: add validation EDEs to queries where the CD bit is set.
+ - For #802: Cleanup comments and add RCODE check for CD bit test case.
+ - Skip the 00-lint test. splint is not maintained; it either does not
+ work or produces false positives. Static analysis is handled in the
+ clang test.
+
+3 July 2023: Wouter
+ - Fix #906: warning: ‘Py_SetProgramName’ is deprecated.
+ - Fix dereference of NULL variable warning in mesh_do_callback.
+
+29 June 2023: George
+ - More fixes for reference counting for python module and clean up
+ failure code.
+ - Merge #827 from rcmcdonald91: Eliminate unnecessary Python reloading
+ which causes memory leaks.
+
+29 June 2023: Wouter
+ - Fix python modules with multiple scripts, by incrementing reference
+ counts.
+
+27 June 2023: George
+ - Merge #892: Add cachedb hit stat. Introduces 'num.query.cachedb' as
+ a new statistical counter.
+ - Remove warning about unknown cast-function-type warning pragma.
+
+22 June 2023: Wouter
+ - Merge #903: contrib: add yocto compatible init script.
+
+15 June 2023: Philip
+ - Fix for issue #887 (Timeouts to forward servers on BSD based
+ system with ASLR)
+ - Probably fixes #516 (Stream reuse does not work on Windows) as well
+
+14 June 2023: George
+ - Properly handle all return values of worker_check_request during
+ early EDE code.
+ - Do not check the incoming request more than once.
+
+12 June 2023: Wouter
+ - Merge #896: Fix: #895: pythonmodule: add all site-packages
+ directories to sys.path.
+ - Fix #895: python + sysconfig gives ANOTHER path comparing to
+ distutils.
+ - Fix for uncertain unit test for doh buffer size events.
+
+25 May 2023: Wouter
+ - Fix unbound-dnstap-socket printout when no query is present.
+ - Fix unbound-dnstap-socket time fraction conversion for printout.
+
+19 May 2023: Wouter
+ - Fix RPZ removal of client-ip, nsip, nsdname triggers from IXFR.
+ - Fix to remove unused variables from RPZ clientip data structure.
+
+16 May 2023: Wouter
+ - Fix #888: [FR] Use kernel timestamps for dnstap.
+ - Fix to print debug log for ancillary data with correct IP address.
+
+11 May 2023: Wouter
+ - Fix warning in windows compile, in set_recvtimestamp.
+
+4 May 2023: Wouter
+ - Fix #885: Error: util/configlexer.c: No such file or directory,
+ adds error messages explaining to install flex and bison.
+ - Fix to remove unused whitespace from acx_nlnetlabs.m4 and config.h.
+ - Fix doxygen in addr_to_nat64 header definition.
+
+1 May 2023: George
+ - Merge #722 from David 'eqvinox' Lamparter: NAT64 support.
+ - For #722: minor fixes, formatting, refactoring.
+
+1 May 2023: Wouter
+ - Fix RPZ IP responses with trigger rpz-drop on cache entries, that
+ they are dropped.
+
+26 April 2023: Philip
+ - Fix issue #860: Bad interaction with 0 TTL records and serve-expired
+
+26 April 2023: Wouter
+ - Merge #882 from vvfedorenko: Features/dropqueuedpackets, with
+ sock-queue-timeout option that drops packets that have been in the
+ socket queue for too long. Added statistics num.queries_timed_out
+ and query.queue_time_us.max that track the socket queue timeouts.
+ - Fix for #882: small changes, date updated in Copyright for
+ util/timeval_func.c and util/timeval_func.h. Man page entries and
+ example entry.
+ - Fix for #882: document variable to stop doxygen warning.
+
+19 April 2023: Wouter
+ - Fix for #878: Invalid IP address in unbound.conf causes Segmentation
+ Fault on OpenBSD.
+
+14 April 2023: Wouter
+ - Merge #875: change obsolete txt URL in unbound-anchor.c to point
+ to RFC 7958, and Fix #874.
+
+13 April 2023: Wouter
+ - Fix build badge, from failing travis link to github ci action link.
+
+6 April 2023: Wouter
+ - Fix for #870: Add test case for the qname minimisation and CNAME.
+
+4 April 2023: Wouter
+ - Fix #870: NXDOMAIN instead of NOERROR rcode when asked for existing
+ CNAME record.
+
+24 March 2023: Philip
+ - Fix issue #676: Unencrypted query is sent when
+ forward-tls-upstream: yes is used without tls-cert-bundle
+ - Extra consistency check to make sure that when TLS is requested,
+ either we set up a TLS connection or we return an error.
+
+21 March 2023: Philip
+ - Fix issue #851: reserved identifier violation
+
+20 March 2023: Wouter
+ - iana portlist update.
+
+17 March 2023: George
+ - Fix #812, fix #846, by using the SSL_OP_IGNORE_UNEXPECTED_EOF option
+ to ignore the unexpected eof while reading in openssl >= 3.
+
+16 March 2023: Wouter
+ - Fix ssl.h include brackets, instead of quotes.
+
+14 March 2023: Wouter
+ - Fix unbound-dnstap-socket test program to reply the finish frame
+ over a TLS connection correctly.
+
+23 February 2023: Wouter
+ - Fix for #852: Completion of error handling.
+
+21 February 2023: Philip
+ - Fix #825: Unexpected behavior with client-subnet-always-forward
+ and serve-expired
+
+10 February 2023: George
+ - Clean up iterator/iterator.c::error_response_cache() and allow for
+ better interaction with serve-expired, prefetch and cached error
+ responses.
+
+9 February 2023: George
+ - Allow TTL refresh of expired error responses.
+ - Add testcase for refreshing expired error responses.
+
+9 February 2023: Wouter
+ - Fix to ignore entirely empty responses, and try at another authority.
+ This turns completely empty responses, a type of noerror/nodata into
+ a servfail, but they do not conform to RFC2308, and the retry can
+ fetch improved content.
+ - Fix unit tests for spurious empty messages.
+ - Fix consistency of unit test without roundrobin answers for the
+ cnametooptout unit test.
+ - Fix to git ignore the library symbol file that configure can create.
+
+8 February 2023: Wouter
+ - Fix #841: Unbound won't build with aaaa-filter-iterator.patch.
+
+30 January 2023: George
+ - Add duration variable for speed_local.test.
+
+26 January 2023: Wouter
+ - Fix acx_nlnetlabs.m4 for -Wstrict-prototypes.
+
+23 January 2023: George
+ - Fix #833: [FR] Ability to set the Redis password.
+
+23 January 2023: Wouter
+ - Fix #835: [FR] Ability to use Redis unix sockets.
+
+20 January 2023: Wouter
+ - Merge #819: Added new static zone type block_a to suppress all A
+ queries for specific zones.
+
+19 January 2023: Wouter
+ - Set max-udp-size default to 1232. This is the same default value as
+ the default value for edns-buffer-size. It restricts client edns
+ buffer size choices, and makes unbound behave similar to other DNS
+ resolvers. The new choice, down from 4096 means it is harder to get
+ large responses from Unbound. Thanks to Xiang Li, from NISL Lab,
+ Tsinghua University.
+ - Add harden-unknown-additional option. It removes
+ unknown records from the authority section and additional section.
+ Thanks to Xiang Li, from NISL Lab, Tsinghua University.
+ - Set default for harden-unknown-additional to no. So that it does
+ not hamper future protocol developments.
+ - Fix test for new default.
+
+18 January 2023: Wouter
+ - Fix not following cleared RD flags potentially enables amplification
+ DDoS attacks, reported by Xiang Li and Wei Xu from NISL Lab,
+ Tsinghua University. The fix stops query loops, by refusing to send
+ RD=0 queries to a forwarder, they still get answered from cache.
+
+13 January 2023: Wouter
+ - Merge #826: Аdd a metric about the maximum number of collisions in
+ lrushah.
+ - Improve documentation for #826, describe the large collisions amount.
+
+9 January 2023: Wouter
+ - Fix python module install path detection.
+ - Fix python version detection in configure.
+
+6 January 2023: Wouter
+ - Fix #823: Response change to NODATA for some ANY queries since
+ 1.12, tested on 1.16.1.
+ - Fix wildcard in hyperlocal zone service degradation, reported
+ by Sergey Kacheev. This fix is included in 1.17.1rc2.
+ That became 1.17.1 on 12 Jan 2023, the code repo continues
+ with 1.17.2. 1.17.1 excludes fix #823, it is included forwards.
+
+5 January 2023: Wouter
+ - Tag for 1.17.1 release.
+
+2 January 2023: Wouter
+ - Fix windows compile for libunbound subprocess reap comm point closes.
+ - Update github workflows to use checkout v3.
+
+14 December 2022: George
+ - Merge #569 from JINMEI Tatuya: add keep-cache option to
+ 'unbound-control reload' to keep caches.
+
+13 December 2022: George
+ - Expose 'statistics-inhibit-zero' as a configuration option; the
+ default value retains Unbound's behavior.
+ - Expose 'max-sent-count' as a configuration option; the
+ default value retains Unbound's behavior.
+ - Merge #461 from Christian Allred: Add max-query-restarts option.
+ Exposes an internal configuration but the default value retains
+ Unbound's behavior.
+
+13 December 2022: Wouter
+ - Merge #808: Wrap Makefile script's directory variables in quotes.
+ - Fix to wrap Makefile scripts directory in quotes for uninstall.
+
+1 December 2022: Wouter
+ - Fix #773: When used with systemd-networkd, unbound does not start
+ until systemd-networkd-wait-online.service times out.
+
+30 November 2022: George
+ - Add SVCB and HTTPS to the types removed by 'unbound-control flush'.
+ - Clear documentation for interactivity between the subnet module and
+ the serve-expired and prefetch configuration options.
+
+30 November 2022: Wouter
+ - Fix #782: Segmentation fault in stats.c:404.
+
+28 November 2022: Wouter
+ - Fix for the ignore of tcp events for closed comm points, preserve
+ the use after free protection features.
+
+23 November 2022: Philip
+ - Merge #720 from jonathangray: fix use after free when
+ WSACreateEvent() fails.
+
+22 November 2022: George
+ - Ignore expired error responses.
+
+11 November 2022: Wouter
+ - Fix #779: [doc] Missing documention in ub_resolve_event() for
+ callback parameter was_ratelimited.
+
+9 November 2022: George
+ - Complementary fix for distutils.sysconfig deprecation in Python 3.10
+ to commit 62c5039ab9da42713e006e840b7578e01d66e7f2.
+
+8 November 2022: Wouter
+ - Fix to ignore tcp events for closed comm points.
+ - Fix to make sure to not read again after a tcp comm point is closed.
+ - Fix #775: libunbound: subprocess reap causes parent process reap
+ to hang.
+ - iana portlist update.
+
+21 October 2022: George
+ - Merge #767 from jonathangray: consistently use IPv4/IPv6 in
+ unbound.conf.5.
+
+21 October 2022: Wouter
+ - Fix that cachedb does not store failures in the external cache.
+
+18 October 2022: George
+ - Clarify the use of MAX_SENT_COUNT in the iterator code.
+
+17 October 2022: Wouter
+ - testcode/dohclient sets log identity to its name.
+
+14 October 2022: Wouter
+ - Merge #768 from fobser: Arithmetic on a pointer to void is a GNU
+ extension.
+ - In unit test, print python script name list correctly.
+
+13 October 2022: Wouter
+ - Tag for 1.17.0 release. The code repository continues with 1.17.1.
+
11 October 2022: George
- Fix PROXYv2 header read for TCP connections when no proxied addresses
are provided.
+7 October 2022: Wouter
+ - Tag for 1.17.0rc1 release.
+
7 October 2022: George
- Fix to stop possible loops in the tcp reuse code (write_wait list
and tcp_wait list). Based on analysis and patch from Prad Seniappan
-README for Unbound 1.17.0
+README for Unbound 1.18.0
Copyright 2007 NLnet Labs
http://unbound.net
#
# Example configuration file.
#
-# See unbound.conf(5) man page, version 1.17.0.
+# See unbound.conf(5) man page, version 1.18.0.
#
# this is a comment.
# statistics-cumulative: no
# enable extended statistics (query types, answer codes, status)
- # printed from unbound-control. default off, because of speed.
+ # printed from unbound-control. Default off, because of speed.
# extended-statistics: no
+ # Inhibits selected extended statistics (qtype, qclass, qopcode, rcode,
+ # rpz-actions) from printing if their value is 0.
+ # Default on.
+ # statistics-inhibit-zero: yes
+
# number of threads to create. 1 disables threading.
# num-threads: 1
# edns-buffer-size: 1232
# Maximum UDP response size (not applied to TCP response).
- # Suggested values are 512 to 4096. Default is 4096. 65536 disables it.
- # max-udp-size: 4096
+ # Suggested values are 512 to 4096. Default is 1232. 65536 disables it.
+ # max-udp-size: 1232
# max memory to use for stream(tcp and tls) waiting result buffers.
# stream-wait-size: 4m
# a throwaway response (also timeouts) is received.
# outbound-msg-retry: 5
+ # Hard limit on the number of outgoing queries Unbound will make while
+ # resolving a name, making sure large NS sets do not loop.
+ # It resets on query restarts (e.g., CNAME) and referrals.
+ # max-sent-count: 32
+
+ # Hard limit on the number of times Unbound is allowed to restart a
+ # query upon encountering a CNAME record.
+ # max-query-restarts: 11
+
# msec for waiting for an unknown server to reply. Increase if you
# are behind a slow satellite link, to eg. 1128.
# unknown-server-time-limit: 376
# Enable IPv6, "yes" or "no".
# do-ip6: yes
+ # If running unbound on an IPv6-only host, domains that only have
+ # IPv4 servers would become unresolveable. If NAT64 is available in
+ # the network, unbound can use NAT64 to reach these servers with
+ # the following option. This is NOT needed for enabling DNS64 on a
+ # system that has IPv4 connectivity.
+ # Consider also enabling prefer-ip6 to prefer native IPv6 connections
+ # to nameservers.
+ # do-nat64: no
+
+ # NAT64 prefix. Defaults to using dns64-prefix value.
+ # nat64-prefix: 64:ff9b::0/96
+
# Enable UDP, "yes" or "no".
# do-udp: yes
# Timeout for EDNS TCP keepalive, in msec.
# edns-tcp-keepalive-timeout: 120000
+ # UDP queries that have waited in the socket buffer for a long time
+ # can be dropped. Default is 0, disabled. In seconds, such as 3.
+ # sock-queue-timeout: 0
+
# Use systemd socket activation for UDP, TCP, and control sockets.
# use-systemd: no
# to validate the zone.
# harden-algo-downgrade: no
+ # Harden against unknown records in the authority section and the
+ # additional section.
+ # harden-unknown-additional: no
+
# Sent minimum amount of information to upstream servers to enhance
# privacy. Only sent minimum required labels of the QNAME and set QTYPE
# to A when possible.
# o always_transparent, always_refuse, always_nxdomain, always_nodata,
# always_deny resolve in that way but ignore local data for
# that name
+ # o block_a resolves all records normally but returns
+ # NODATA for A queries and ignores local data for that name
# o always_null returns 0.0.0.0 or ::0 for any name in the zone.
# o noview breaks out of that view towards global local-zones.
#
# redis-server-host: 127.0.0.1
# # redis server's TCP port
# redis-server-port: 6379
+# # if the server uses a unix socket, set its path, or "" when not used.
+# # redis-server-path: "/var/lib/redis/redis-server.sock"
+# # if the server uses an AUTH password, specify here, or "" when not used.
+# # redis-server-password: ""
# # timeout (in ms) for communication with the redis server
# redis-timeout: 100
# # set timeout on redis records based on DNS response TTL
-.TH "libunbound" "3" "Oct 13, 2022" "NLnet Labs" "unbound 1.17.0"
+.TH "libunbound" "3" "Aug 30, 2023" "NLnet Labs" "unbound 1.18.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.17.0 functions.
+\- Unbound DNS validating resolver 1.18.0 functions.
.SH "SYNOPSIS"
.B #include <unbound.h>
.LP
-.TH "unbound-anchor" "8" "Oct 13, 2022" "NLnet Labs" "unbound 1.17.0"
+.TH "unbound-anchor" "8" "Aug 30, 2023" "NLnet Labs" "unbound 1.18.0"
.\"
.\" unbound-anchor.8 -- unbound anchor maintenance utility manual
.\"
-.TH "unbound-checkconf" "8" "Oct 13, 2022" "NLnet Labs" "unbound 1.17.0"
+.TH "unbound-checkconf" "8" "Aug 30, 2023" "NLnet Labs" "unbound 1.18.0"
.\"
.\" unbound-checkconf.8 -- unbound configuration checker manual
.\"
-.TH "unbound-control" "8" "Oct 13, 2022" "NLnet Labs" "unbound 1.17.0"
+.TH "unbound-control" "8" "Aug 30, 2023" "NLnet Labs" "unbound 1.18.0"
.\"
.\" unbound-control.8 -- unbound remote control manual
.\"
.B reload
Reload the server. This flushes the cache and reads the config file fresh.
.TP
+.B reload_keep_cache
+Reload the server but try to keep the RRset and message cache if
+(re)configuration allows for it.
+That means the caches sizes and the number of threads must not change between
+reloads.
+.TP
.B verbosity \fInumber
Change verbosity value for logging. Same values as \fBverbosity\fR keyword in
\fIunbound.conf\fR(5). This new setting lasts until the server is issued
.TP
.B flush \fIname
Remove the name from the cache. Removes the types
-A, AAAA, NS, SOA, CNAME, DNAME, MX, PTR, SRV and NAPTR.
+A, AAAA, NS, SOA, CNAME, DNAME, MX, PTR, SRV, NAPTR, SVCB and HTTPS.
Because that is fast to do. Other record types can be removed using
.B flush_type
or
.I threadX.num.queries_ip_ratelimited
number of queries rate limited by thread
.TP
+.I threadX.num.queries_cookie_valid
+number of queries with a valid DNS Cookie by thread
+.TP
+.I threadX.num.queries_cookie_client
+number of queries with a client part only DNS Cookie by thread
+.TP
+.I threadX.num.queries_cookie_invalid
+number of queries with an invalid DNS Cookie by thread
+.TP
.I threadX.num.cachehits
number of queries that were successfully answered using a cache lookup
.TP
.I threadX.num.expired
number of replies that served an expired cache entry.
.TP
+.I threadX.num.queries_timed_out
+number of queries that are dropped because they waited in the UDP socket buffer
+for too long.
+.TP
+.I threadX.query.queue_time_us.max
+The maximum wait time for packets in the socket buffer, in microseconds. This
+is only reported when sock-queue-timeout is enabled.
+.TP
.I threadX.num.recursivereplies
The number of replies sent to queries that needed recursive processing. Could be smaller than threadX.num.cachemiss if due to timeouts no replies were sent for some queries.
.TP
.I total.num.queries
summed over threads.
.TP
+.I total.num.queries_ip_ratelimited
+summed over threads.
+.TP
+.I total.num.queries_cookie_valid
+summed over threads.
+.TP
+.I total.num.queries_cookie_client
+summed over threads.
+.TP
+.I total.num.queries_cookie_invalid
+summed over threads.
+.TP
.I total.num.cachehits
summed over threads.
.TP
.I total.num.expired
summed over threads.
.TP
+.I total.num.queries_timed_out
+summed over threads.
+.TP
+.I total.query.queue_time_us.max
+the maximum of the thread values.
+.TP
.I total.num.recursivereplies
summed over threads.
.TP
.TP
.I num.query.dnscrypt.shared_secret.cachemiss
The number of dnscrypt queries that did not find a shared secret in the cache.
-The can be use to compute the shared secret hitrate.
+This can be used to compute the shared secret hitrate.
.TP
.I num.query.dnscrypt.replay
The number of dnscrypt queries that found a nonce hit in the nonce cache and
The number of items in the key cache. These are DNSSEC keys, one item
per delegation point, and their validation status.
.TP
+.I msg.cache.max_collisions
+The maximum number of hash table collisions in the msg cache. This is the
+number of hashes that are identical when a new element is inserted in the
+hash table. If the value is very large, like hundreds, something is wrong
+with the performance of the hash table, hash values are incorrect or malicious.
+.TP
+.I rrset.cache.max_collisions
+The maximum number of hash table collisions in the rrset cache. This is the
+number of hashes that are identical when a new element is inserted in the
+hash table. If the value is very large, like hundreds, something is wrong
+with the performance of the hash table, hash values are incorrect or malicious.
+.TP
.I dnscrypt_shared_secret.cache.count
The number of items in the shared secret cache. These are precomputed shared
secrets for a given client public key/server secret key pair. Shared secrets
.I num.query.subnet_cache
Number of queries answered from the edns client subnet cache. These are
counted as cachemiss by the main counters, but hit the client subnet
-specific cache, after getting processed by the edns client subnet module.
+specific cache after getting processed by the edns client subnet module.
+.TP
+.I num.query.cachedb
+Number of queries answered from the external cache of cachedb.
+These are counted as cachemiss by the main counters, but hit the cachedb
+external cache after getting processed by the cachedb module.
.TP
.I num.rpz.action.<rpz_action>
Number of queries answered using configured RPZ policy, per RPZ action type.
-.TH "unbound\-host" "1" "Oct 13, 2022" "NLnet Labs" "unbound 1.17.0"
+.TH "unbound\-host" "1" "Aug 30, 2023" "NLnet Labs" "unbound 1.18.0"
.\"
.\" unbound-host.1 -- unbound DNS lookup utility
.\"
-.TH "unbound" "8" "Oct 13, 2022" "NLnet Labs" "unbound 1.17.0"
+.TH "unbound" "8" "Aug 30, 2023" "NLnet Labs" "unbound 1.18.0"
.\"
.\" unbound.8 -- unbound manual
.\"
.\"
.SH "NAME"
.B unbound
-\- Unbound DNS validating resolver 1.17.0.
+\- Unbound DNS validating resolver 1.18.0.
.SH "SYNOPSIS"
.B unbound
.RB [ \-h ]
-.TH "unbound.conf" "5" "Oct 13, 2022" "NLnet Labs" "unbound 1.17.0"
+.TH "unbound.conf" "5" "Aug 30, 2023" "NLnet Labs" "unbound 1.18.0"
.\"
.\" unbound.conf.5 -- unbound.conf manual
.\"
Default is off, because keeping track of more statistics takes time. The
counters are listed in \fIunbound\-control\fR(8).
.TP
+.B statistics\-inhibit\-zero: \fI<yes or no>
+If enabled, selected extended statistics with a value of 0 are inhibited from
+printing with \fIunbound\-control\fR(8).
+These are query types, query classes, query opcodes, answer rcodes
+(except NOERROR, FORMERR, SERVFAIL, NXDOMAIN, NOTIMPL, REFUSED) and
+RPZ actions.
+Default is on.
+.TP
.B num\-threads: \fI<number>
The number of threads to create to serve clients. Use 1 for no threading.
.TP
.B max\-udp\-size: \fI<number>
Maximum UDP response size (not applied to TCP response). 65536 disables the
udp response size maximum, and uses the choice from the client, always.
-Suggested values are 512 to 4096. Default is 4096.
+Suggested values are 512 to 4096. Default is 1232. The default value is the
+same as the default for edns\-buffer\-size.
.TP
.B stream\-wait\-size: \fI<number>
Number of bytes size maximum to use for waiting stream buffers. Default is
The value of the Differentiated Services Codepoint (DSCP) in the
differentiated services field (DS) of the outgoing IP packet headers.
The field replaces the outdated IPv4 Type-Of-Service field and the
-IPV6 traffic class field.
+IPv6 traffic class field.
.TP
.B rrset\-cache\-size: \fI<number>
Number of bytes size of the RRset cache. Default is 4 megabytes.
Enable or disable whether ip6 queries are answered or issued. Default is yes.
If disabled, queries are not answered on IPv6, and queries are not sent on
IPv6 to the internet nameservers. With this option you can disable the
-ipv6 transport for sending DNS traffic, it does not impact the contents of
+IPv6 transport for sending DNS traffic, it does not impact the contents of
the DNS traffic, which may have ip4 and ip6 addresses in it.
.TP
.B prefer\-ip4: \fI<yes or no>
A minimum actual timeout of 200 milliseconds is observed regardless of the
advertised timeout.
.TP
+.B sock\-queue\-timeout: \fI<sec>\fR
+UDP queries that have waited in the socket buffer for a long time can be
+dropped. Default is 0, disabled. The time is set in seconds, 3 could be a
+good value to ignore old queries that likely the client does not need a reply
+for any more. This could happen if the host has not been able to service
+the queries for a while, i.e. Unbound is not running, and then is enabled
+again. It uses timestamp socket options.
+.TP
.B tcp\-upstream: \fI<yes or no>
Enable or disable whether the upstream queries use TCP only for transport.
Default is no. Useful in tunneling scenarios. If set to no you can specify
.B access\-control: \fI<IP netblock> <action>
The netblock is given as an IP4 or IP6 address with /size appended for a
classless network block. The action can be \fIdeny\fR, \fIrefuse\fR,
-\fIallow\fR, \fIallow_setrd\fR, \fIallow_snoop\fR, \fIdeny_non_local\fR or
-\fIrefuse_non_local\fR.
+\fIallow\fR, \fIallow_setrd\fR, \fIallow_snoop\fR, \fIallow_cookie\fR,
+\fIdeny_non_local\fR or \fIrefuse_non_local\fR.
The most specific netblock match is used, if none match \fIrefuse\fR is used.
The order of the access\-control statements therefore does not matter.
.IP
-The action \fIdeny\fR stops queries from hosts from that netblock.
+The \fIdeny\fR action stops queries from hosts from that netblock.
.IP
-The action \fIrefuse\fR stops queries too, but sends a DNS rcode REFUSED
+The \fIrefuse\fR action stops queries too, but sends a DNS rcode REFUSED
error message back.
.IP
-The action \fIallow\fR gives access to clients from that netblock.
+The \fIallow\fR action gives access to clients from that netblock.
It gives only access for recursion clients (which is
what almost all clients need). Nonrecursive queries are refused.
.IP
zones to a resolver DNS server, but only supports stub domains and
sends queries to the resolver DNS server with the RD bit cleared.
.IP
-The action \fIallow_snoop\fR gives nonrecursive access too. This give
+The \fIallow_snoop\fR action gives nonrecursive access too. This give
both recursive and non recursive access. The name \fIallow_snoop\fR refers
to cache snooping, a technique to use nonrecursive queries to examine
the cache contents (for malicious acts). However, nonrecursive queries can
also be a valuable debugging tool (when you want to examine the cache
contents). In that case use \fIallow_snoop\fR for your administration host.
.IP
+The \fIallow_cookie\fR action allows access to UDP queries that contain a
+valid DNS Cookie as specified in RFC 7873 and RFC 9018, when the
+\fBanswer\-cookie\fR option is enabled.
+UDP queries containing only a DNS Client Cookie and no Server Cookie, or an
+invalid DNS Cookie, will receive a BADCOOKIE response including a newly
+generated DNS Cookie, allowing clients to retry with that DNS Cookie.
+The \fIallow_cookie\fR action will also accept requests over stateful
+transports, regardless of the presence of an DNS Cookie and regardless of the
+\fBanswer\-cookie\fR setting.
+If \fBip\-ratelimit\fR is used, clients with a valid DNS Cookie will bypass the
+ratelimit.
+If a ratelimit for such clients is still needed, \fBip\-ratelimit\-cookie\fR
+can be used instead.
+.IP
By default only localhost is \fIallow\fRed, the rest is \fIrefuse\fRd.
The default is \fIrefuse\fRd, because that is protocol\-friendly. The DNS
protocol is not designed to handle dropped packets due to policy, and
that allow this feature to work, but sometimes they do not, and turning
this option off avoids that validation failure.
.TP
+.B harden\-unknown\-additional: \fI<yes or no>
+Harden against unknown records in the authority section and additional
+section. Default is no. If no, such records are copied from the upstream
+and presented to the client together with the answer. If yes, it could
+hamper future protocol developments that want to add records.
+.TP
.B use\-caps\-for\-id: \fI<yes or no>
Use 0x20\-encoded random bits in the query to foil spoof attempts.
This perturbs the lowercase and uppercase of query names sent to
Configure a local zone. The type determines the answer to give if
there is no match from local\-data. The types are deny, refuse, static,
transparent, redirect, nodefault, typetransparent, inform, inform_deny,
-inform_redirect, always_transparent, always_refuse, always_nxdomain, always_null, noview,
-and are explained below. After that the default settings are listed. Use
-local\-data: to enter data into the local zone. Answers for local zones
-are authoritative DNS answers. By default the zones are class IN.
+inform_redirect, always_transparent, block_a, always_refuse, always_nxdomain,
+always_null, noview, and are explained below. After that the default settings
+are listed. Use local\-data: to enter data into the local zone. Answers for
+local zones are authoritative DNS answers. By default the zones are class IN.
.IP
If you need more complicated authoritative data, with referrals, wildcards,
CNAME/DNAME support, or DNSSEC authoritative service, setup a stub\-zone for
\h'5'\fIalways_transparent\fR
Like transparent, but ignores local data and resolves normally.
.TP 10
+\h'5'\fIblock_a\fR
+Like transparent, but ignores local data and resolves normally all query
+types excluding A. For A queries it unconditionally returns NODATA.
+Useful in cases when there is a need to explicitly force all apps to use
+IPv6 protocol and avoid any queries to IPv4.
+.TP 10
\h'5'\fIalways_refuse\fR
Like refuse, but ignores local data and refuses the query.
.TP 10
to redirect as specified by "\fIresource record string\fR". "Resource
record string" is similar to that of \fIaccess-control-tag-action\fR,
but it must be of either AAAA, A or CNAME types.
-If the IP-netblock is an IPv6/IPV4 prefix, the record
+If the IP-netblock is an IPv6/IPv4 prefix, the record
must be AAAA/A respectively, unless it is a CNAME (which can be used
for both versions of IP netblocks). If it is CNAME there must not be
more than one \fIresponse-ip-data\fR for the same IP-netblock.
.TP 5
.B ip\-ratelimit: \fI<number or 0>
Enable global ratelimiting of queries accepted per IP address.
-If 0, the default, it is disabled. This option is experimental at this time.
+This option is experimental at this time.
The ratelimit is in queries per second that are allowed. More queries are
completely dropped and will not receive a reply, SERVFAIL or otherwise.
IP ratelimiting happens before looking in the cache. This may be useful for
mitigating amplification attacks.
+Default is 0 (disabled).
+.TP 5
+.B ip\-ratelimit\-cookie: \fI<number or 0>
+Enable global ratelimiting of queries accepted per IP address with a valid DNS
+Cookie.
+This option is experimental at this time.
+The ratelimit is in queries per second that are allowed.
+More queries are completely dropped and will not receive a reply, SERVFAIL or
+otherwise.
+IP ratelimiting happens before looking in the cache.
+This option could be useful in combination with \fIallow_cookie\fR in an
+attempt to mitigate other amplification attacks than UDP reflections (e.g.,
+attacks targeting Unbound itself) which are already handled with DNS Cookies.
+If used, the value is suggested to be higher than \fBip\-ratelimit\fR e.g.,
+tenfold.
+Default is 0 (disabled).
.TP 5
.B ip\-ratelimit\-size: \fI<memory size>
Give the size of the data structure in which the current ongoing rates are
the zone.
Default is 5.
.TP 5
+.B max\-sent\-count: \fI<number>
+Hard limit on the number of outgoing queries Unbound will make while resolving
+a name, making sure large NS sets do not loop.
+Results in SERVFAIL when reached.
+It resets on query restarts (e.g., CNAME) and referrals.
+Default is 32.
+.TP 5
+.B max\-query\-restarts: \fI<number>
+Hard limit on the number of times Unbound is allowed to restart a query upon
+encountering a CNAME record.
+Results in SERVFAIL when reached.
+Changing this value needs caution as it can allow long CNAME chains to be
+accepted, where Unbound needs to verify (resolve) each link individually.
+Default is 11.
+.TP 5
.B fast\-server\-permil: \fI<number>
Specify how many times out of 1000 to pick from the set of fastest servers.
0 turns the feature off. A value of 900 would pick from the fastest
use the fastest specified number of servers with the fast\-server\-permil
option, that turns this on or off. The default is to use the fastest 3 servers.
.TP 5
+.B answer\-cookie: \fI<yes or no>
+If enabled, Unbound will answer to requests containing DNS Cookies as
+specified in RFC 7873 and RFC 9018.
+Default is no.
+.TP 5
+.B cookie\-secret: \fI<128 bit hex string>
+Server's secret for DNS Cookie generation.
+Useful to explicitly set for servers in an anycast deployment that need to
+share the secret in order to verify each other's Server Cookies.
+An example hex string would be "000102030405060708090a0b0c0d0e0f".
+Default is a 128 bits random secret generated at startup time.
+.TP 5
.B edns\-client\-string: \fI<IP netblock> <string>
Include an EDNS0 option containing configured ascii string in queries with
destination address matching the configured IP netblock. This configuration
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
+.TP 5
.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
Authority zones are configured with \fBauth\-zone:\fR, and each one must
have a \fBname:\fR. There can be multiple ones, by listing multiple auth\-zone clauses, each with a different name, pertaining to that part of the namespace.
The authority zone with the name closest to the name looked up is used.
-Authority zones are processed after \fBlocal\-zones\fR and before
-cache (\fBfor\-downstream:\fR \fIyes\fR), and when used in this manner
-make Unbound respond like an authority server. Authority zones are also
-processed after cache, just before going to the network to fetch
-information for recursion (\fBfor\-upstream:\fR \fIyes\fR), and when used
-in this manner provide a local copy of an authority server that speeds up
-lookups of that data.
+Authority zones can be processed on two distinct, non-exclusive, configurable
+stages.
+.LP
+With \fBfor\-downstream:\fR \fIyes\fR (default), authority zones are processed
+after \fBlocal\-zones\fR and before cache.
+When used in this manner, Unbound responds like an authority server with no
+further processing other than returning an answer from the zone contents.
+A notable example, in this case, is CNAME records which are returned verbatim
+to downstream clients without further resolution.
+.LP
+With \fBfor\-upstream:\fR \fIyes\fR (default), authority zones are processed
+after the cache lookup, just before going to the network to fetch
+information for recursion.
+When used in this manner they provide a local copy of an authority server
+that speeds up lookups for that data during resolving.
+.LP
+If both options are enabled (default), client queries for an authority zone are
+answered authoritatively from Unbound, while internal queries that require data
+from the authority zone consult the local zone data instead of going to the
+network.
+.LP
+An interesting configuration is \fBfor\-downstream:\fR \fIno\fR,
+\fBfor\-upstream:\fR \fIyes\fR that allows for hyperlocal behavior where both
+client and internal queries consult the local zone data while resolving.
+In this case, the aforementioned CNAME example will result in a thoroughly
+resolved answer.
.LP
Authority zones can be read from zonefile. And can be kept updated via
AXFR and IXFR. After update the zonefile is rewritten. The update mechanism
used by dns64 processing instead. Can be entered multiple times, list a
new domain for which it applies, one per line. Applies also to names
underneath the name given.
+.SS "NAT64 Operation"
+.LP
+NAT64 operation allows using a NAT64 prefix for outbound requests to IPv4-only
+servers. It is controlled by two options in the \fBserver:\fR section:
+.TP
+.B do\-nat64: \fI<yes or no>\fR
+Use NAT64 to reach IPv4-only servers.
+Consider also enabling \fBprefer\-ip6\fR to prefer native IPv6 connections to
+nameservers.
+Default no.
+.TP
+.B nat64\-prefix: \fI<IPv6 prefix>\fR
+Use a specific NAT64 prefix to reach IPv4-only servers. Defaults to using
+the prefix configured in \fBdns64\-prefix\fR, which in turn defaults to
+64:ff9b::/96. The prefix length must be one of /32, /40, /48, /56, /64 or /96.
.SS "DNSCrypt Options"
.LP
The
configuration file. On top of that, for each query only 100 different subnets
are allowed to be stored for each address family. Exceeding that number, older
entries will be purged from cache.
+.LP
+This module does not interact with the \fBserve\-expired*\fR and
+\fBprefetch:\fR options.
.TP
.B send\-client\-subnet: \fI<IP address>\fR
Send client source address to this authority. Append /num to indicate a
The TCP port number of the Redis server.
This option defaults to 6379.
.TP
+.B redis-server-path: \fI<unix socket path>\fR
+The unix socket path to connect to the redis server. Off by default, and it
+can be set to "" to turn this off. Unix sockets may have better throughput
+than the IP address option.
+.TP
+.B redis-server-password: \fI"<password>"\fR
+The Redis AUTH password to use for the redis server.
+Only relevant if Redis is configured for client password authorisation.
+Off by default, and it can be set to "" to turn this off.
+.TP
.B redis-timeout: \fI<msec>\fR
The period until when Unbound waits for a response from the Redis sever.
If this timeout expires Unbound closes the connection, treats it as
}
alloc_init(&sn_env->alloc, NULL, 0);
env->modinfo[id] = (void*)sn_env;
+
+ /* Warn that serve-expired and prefetch do not work with the subnet
+ * module cache. */
+ if(env->cfg->serve_expired)
+ log_warn(
+ "subnetcache: serve-expired is set but not working "
+ "for data originating from the subnet module cache.");
+ if(env->cfg->prefetch)
+ log_warn(
+ "subnetcache: prefetch is set but not working "
+ "for data originating from the subnet module cache.");
/* Copy msg_cache settings */
sn_env->subnet_msg_cache = slabhash_create(env->cfg->msg_cache_slabs,
HASH_DEFAULT_STARTARRAY, env->cfg->msg_cache_size,
((struct subnet_qstate*)qstate->minfo[id])->qinfo_hash :
query_info_hash(&qstate->qinfo, qstate->query_flags);
/* Step 1, general qinfo lookup */
- struct lruhash_entry *lru_entry = slabhash_lookup(subnet_msg_cache, h,
+ struct lruhash_entry* lru_entry = slabhash_lookup(subnet_msg_cache, h,
&qstate->qinfo, 1);
int need_to_insert = (lru_entry == NULL);
if (!lru_entry) {
log_err("subnetcache: cache insertion failed");
return;
}
-
+
/* store RRsets */
for(i=0; i<rep->rrset_count; i++) {
rep->ref[i].key = rep->rrsets[i];
/** Lookup in cache and reply true iff reply is sent. */
static int
-lookup_and_reply(struct module_qstate *qstate, int id, struct subnet_qstate *sq)
+lookup_and_reply(struct module_qstate *qstate, int id, struct subnet_qstate *sq, int prefetch)
{
struct lruhash_entry *e;
struct module_env *env = qstate->env;
INET6_SIZE);
sq->ecs_client_out.subnet_validdata = 1;
}
+
+ if (prefetch && *qstate->env->now >= ((struct reply_info *)node->elem)->prefetch_ttl) {
+ qstate->need_refetch = 1;
+ }
return 1;
}
* module_finished */
return module_finished;
}
-
+
/* We have not asked for subnet data */
if (!sq->subnet_sent) {
if (s_in->subnet_validdata)
cp_edns_bad_response(c_out, c_in);
return module_finished;
}
-
+
/* subnet sent but nothing came back */
if (!s_in->subnet_validdata) {
/* The authority indicated no support for edns subnet. As a
cp_edns_bad_response(c_out, c_in);
return module_finished;
}
-
+
/* Being here means we have asked for and got a subnet specific
* answer. Also, the answer from the authority is not yet cached
* anywhere. */
-
+
/* can we accept response? */
if(s_out->subnet_addr_fam != s_in->subnet_addr_fam ||
s_out->subnet_source_mask != s_in->subnet_source_mask ||
&qstate->mesh_info->reply_list->query_reply.client_addr,
&sq->ecs_client_in, qstate->env->cfg);
}
+ else if(qstate->client_addr.ss_family != AF_UNSPEC) {
+ subnet_option_from_ss(
+ &qstate->client_addr,
+ &sq->ecs_client_in, qstate->env->cfg);
+ }
if(sq->ecs_client_in.subnet_validdata == 0) {
/* No clients are interested in result or we could not
if(!sq->started_no_cache_lookup && !qstate->blacklist) {
lock_rw_wrlock(&sne->biglock);
- if(lookup_and_reply(qstate, id, sq)) {
+ if(qstate->mesh_info->reply_list &&
+ lookup_and_reply(qstate, id, sq,
+ qstate->env->cfg->prefetch)) {
sne->num_msg_cache++;
lock_rw_unlock(&sne->biglock);
verbose(VERB_QUERY, "subnetcache: answered from cache");
}
}
+int
+delegpt_addr_on_result_list(struct delegpt* dp, struct delegpt_addr* find)
+{
+ struct delegpt_addr* a = dp->result_list;
+ while(a) {
+ if(a == find)
+ return 1;
+ a = a->next_result;
+ }
+ return 0;
+}
+
+void
+delegpt_usable_list_remove_addr(struct delegpt* dp, struct delegpt_addr* del)
+{
+ struct delegpt_addr* usa = dp->usable_list, *prev = NULL;
+ while(usa) {
+ if(usa == del) {
+ /* snip off the usable list */
+ if(prev)
+ prev->next_usable = usa->next_usable;
+ else dp->usable_list = usa->next_usable;
+ return;
+ }
+ prev = usa;
+ usa = usa->next_usable;
+ }
+}
+
+void
+delegpt_add_to_result_list(struct delegpt* dp, struct delegpt_addr* a)
+{
+ if(delegpt_addr_on_result_list(dp, a))
+ return;
+ delegpt_usable_list_remove_addr(dp, a);
+ a->next_result = dp->result_list;
+ dp->result_list = a;
+}
+
void
delegpt_add_unused_targets(struct delegpt* dp)
{
/** get memory in use by dp */
size_t delegpt_get_mem(struct delegpt* dp);
+/**
+ * See if the addr is on the result list.
+ * @param dp: delegation point.
+ * @param find: the pointer is searched for on the result list.
+ * @return 1 if found, 0 if not found.
+ */
+int delegpt_addr_on_result_list(struct delegpt* dp, struct delegpt_addr* find);
+
+/**
+ * Remove the addr from the usable list.
+ * @param dp: the delegation point.
+ * @param del: the addr to remove from the list, the pointer is searched for.
+ */
+void delegpt_usable_list_remove_addr(struct delegpt* dp,
+ struct delegpt_addr* del);
+
+/**
+ * Add the delegpt_addr back to the result list, if it is not already on
+ * the result list. Also removes it from the usable list.
+ * @param dp: delegation point.
+ * @param a: addr to add, nothing happens if it is already on the result list.
+ * It is removed from the usable list.
+ */
+void delegpt_add_to_result_list(struct delegpt* dp, struct delegpt_addr* a);
+
#endif /* ITERATOR_ITER_DELEGPT_H */
/* If we've gotten this far, this is NOERROR/NODATA (which could
* be an entirely empty message) */
+ /* but ignore entirely empty messages, noerror/nodata has a soa
+ * negative ttl value in the authority section, this makes it try
+ * again at another authority. And turns it from a 5 second empty
+ * message into a 5 second servfail response. */
+ if(msg->rep->an_numrrsets == 0 && msg->rep->ns_numrrsets == 0 &&
+ msg->rep->ar_numrrsets == 0)
+ return RESPONSE_TYPE_THROWAWAY;
/* check if recursive answer; saying it has empty cache */
if( (msg->rep->flags&BIT_RA) && !(msg->rep->flags&BIT_AA) && !rdset)
return RESPONSE_TYPE_REC_LAME;
return 0;
}
+/** Check if type is allowed in the authority section */
+static int
+type_allowed_in_authority_section(uint16_t tp)
+{
+ if(tp == LDNS_RR_TYPE_SOA || tp == LDNS_RR_TYPE_NS ||
+ tp == LDNS_RR_TYPE_DS || tp == LDNS_RR_TYPE_NSEC ||
+ tp == LDNS_RR_TYPE_NSEC3)
+ return 1;
+ return 0;
+}
+
+/** Check if type is allowed in the additional section */
+static int
+type_allowed_in_additional_section(uint16_t tp)
+{
+ if(tp == LDNS_RR_TYPE_A || tp == LDNS_RR_TYPE_AAAA)
+ return 1;
+ return 0;
+}
+
/**
* This routine normalizes a response. This includes removing "irrelevant"
* records from the answer and additional sections and (re)synthesizing
* @param msg: msg to normalize.
* @param qinfo: original query.
* @param region: where to allocate synthesized CNAMEs.
+ * @param env: module env with config options.
* @return 0 on error.
*/
static int
scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
- struct query_info* qinfo, struct regional* region)
+ struct query_info* qinfo, struct regional* region,
+ struct module_env* env)
{
uint8_t* sname = qinfo->qname;
size_t snamelen = qinfo->qname_len;
/* Mark additional names from AUTHORITY */
while(rrset && rrset->section == LDNS_SECTION_AUTHORITY) {
+ /* protect internals of recursor by making sure to del these */
if(rrset->type==LDNS_RR_TYPE_DNAME ||
rrset->type==LDNS_RR_TYPE_CNAME ||
rrset->type==LDNS_RR_TYPE_A ||
"RRset:", pkt, msg, prev, &rrset);
continue;
}
+ /* Allowed list of types in the authority section */
+ if(env->cfg->harden_unknown_additional &&
+ !type_allowed_in_authority_section(rrset->type)) {
+ remove_rrset("normalize: removing irrelevant "
+ "RRset:", pkt, msg, prev, &rrset);
+ continue;
+ }
/* only one NS set allowed in authority section */
if(rrset->type==LDNS_RR_TYPE_NS) {
/* NS set must be pertinent to the query */
* found in ANSWER and AUTHORITY. */
/* These records have not been marked OK previously */
while(rrset && rrset->section == LDNS_SECTION_ADDITIONAL) {
- /* FIXME: what about other types? */
if(rrset->type==LDNS_RR_TYPE_A ||
rrset->type==LDNS_RR_TYPE_AAAA)
{
continue;
}
}
+ /* protect internals of recursor by making sure to del these */
if(rrset->type==LDNS_RR_TYPE_DNAME ||
rrset->type==LDNS_RR_TYPE_CNAME ||
rrset->type==LDNS_RR_TYPE_NS) {
"RRset:", pkt, msg, prev, &rrset);
continue;
}
+ /* Allowed list of types in the additional section */
+ if(env->cfg->harden_unknown_additional &&
+ !type_allowed_in_additional_section(rrset->type)) {
+ remove_rrset("normalize: removing irrelevant "
+ "RRset:", pkt, msg, prev, &rrset);
+ continue;
+ }
prev = rrset;
rrset = rrset->rrset_all_next;
}
}
/* normalize the response, this cleans up the additional. */
- if(!scrub_normalize(pkt, msg, qinfo, region))
+ if(!scrub_normalize(pkt, msg, qinfo, region, env))
return 0;
/* delete all out-of-zone information */
if(!scrub_sanitize(pkt, msg, qinfo, zonename, env, ie))
/** time when nameserver glue is said to be 'recent' */
#define SUSPICION_RECENT_EXPIRY 86400
+/** if NAT64 is enabled and no NAT64 prefix is configured, first fall back to
+ * DNS64 prefix. If that is not configured, fall back to this default value.
+ */
+static const char DEFAULT_NAT64_PREFIX[] = "64:ff9b::/96";
+
/** fillup fetch policy array */
static void
fetch_fill(struct iter_env* ie, const char* str)
int
iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg)
{
+ const char *nat64_prefix;
int i;
/* target fetch policy */
if(!read_fetch_policy(iter_env, cfg->target_fetch_policy))
}
}
+
+ nat64_prefix = cfg->nat64_prefix;
+ if(!nat64_prefix)
+ nat64_prefix = cfg->dns64_prefix;
+ if(!nat64_prefix)
+ nat64_prefix = DEFAULT_NAT64_PREFIX;
+ if(!netblockstrtoaddr(nat64_prefix, 0, &iter_env->nat64_prefix_addr,
+ &iter_env->nat64_prefix_addrlen,
+ &iter_env->nat64_prefix_net)) {
+ log_err("cannot parse nat64-prefix netblock: %s", nat64_prefix);
+ return 0;
+ }
+ if(!addr_is_ip6(&iter_env->nat64_prefix_addr,
+ iter_env->nat64_prefix_addrlen)) {
+ log_err("nat64-prefix is not IPv6: %s", cfg->nat64_prefix);
+ return 0;
+ }
+ if(!prefixnet_is_nat64(iter_env->nat64_prefix_net)) {
+ log_err("nat64-prefix length it not 32, 40, 48, 56, 64 or 96: %s",
+ nat64_prefix);
+ return 0;
+ }
+
iter_env->supports_ipv6 = cfg->do_ip6;
iter_env->supports_ipv4 = cfg->do_ip4;
+ iter_env->use_nat64 = cfg->do_nat64;
iter_env->outbound_msg_retry = cfg->outbound_msg_retry;
+ iter_env->max_sent_count = cfg->max_sent_count;
+ iter_env->max_query_restarts = cfg->max_query_restarts;
return 1;
}
if(!iter_env->supports_ipv6 && addr_is_ip6(&a->addr, a->addrlen)) {
return -1; /* there is no ip6 available */
}
- if(!iter_env->supports_ipv4 && !addr_is_ip6(&a->addr, a->addrlen)) {
+ if(!iter_env->supports_ipv4 && !iter_env->use_nat64 &&
+ !addr_is_ip6(&a->addr, a->addrlen)) {
return -1; /* there is no ip4 available */
}
/* check lameness - need zone , class info */
int
iter_dp_is_useless(struct query_info* qinfo, uint16_t qflags,
- struct delegpt* dp, int supports_ipv4, int supports_ipv6)
+ struct delegpt* dp, int supports_ipv4, int supports_ipv6,
+ int use_nat64)
{
struct delegpt_ns* ns;
struct delegpt_addr* a;
+
+ if(supports_ipv6 && use_nat64)
+ supports_ipv4 = 1;
+
/* check:
* o RD qflag is on.
* o no addresses are provided.
for(a=dp->target_list; a; a = a->next_target) {
if(a->attempts >= outbound_msg_retry) {
/* add back to result list */
- a->next_result = dp->result_list;
- dp->result_list = a;
+ delegpt_add_to_result_list(dp, a);
}
if(a->attempts > d)
a->attempts -= d;
* 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.
+ * @param use_nat64: if we support NAT64 for lookups to the target.
+ * if yes, IPv4 addresses are useful even if we don't support IPv4.
* @return true if dp is useless.
*/
-int iter_dp_is_useless(struct query_info* qinfo, uint16_t qflags,
- struct delegpt* dp, int supports_ipv4, int supports_ipv6);
+int iter_dp_is_useless(struct query_info* qinfo, uint16_t qflags,
+ struct delegpt* dp, int supports_ipv4, int supports_ipv6,
+ int use_nat64);
/**
* See if qname has DNSSEC needs. This is true if there is a trust anchor above
log_err("out of memory adding missing");
}
delegpt_mark_neg(dpns, qstate->qinfo.qtype);
- if((dpns->got4 == 2 || !ie->supports_ipv4) &&
+ if((dpns->got4 == 2 || (!ie->supports_ipv4 && !ie->use_nat64)) &&
(dpns->got6 == 2 || !ie->supports_ipv6)) {
dpns->resolved = 1; /* mark as failed */
target_count_increase_nx(super_iq, 1);
static int
error_response_cache(struct module_qstate* qstate, int id, int rcode)
{
- if(!qstate->no_cache_store) {
- /* store in cache */
- struct reply_info err;
- if(qstate->prefetch_leeway > NORR_TTL) {
- verbose(VERB_ALGO, "error response for prefetch in cache");
- /* attempt to adjust the cache entry prefetch */
- if(dns_cache_prefetch_adjust(qstate->env, &qstate->qinfo,
- NORR_TTL, qstate->query_flags))
- return error_response(qstate, id, rcode);
- /* if that fails (not in cache), fall through to store err */
+ struct reply_info err;
+ struct msgreply_entry* msg;
+ if(qstate->no_cache_store) {
+ return error_response(qstate, id, rcode);
+ }
+ if(qstate->prefetch_leeway > NORR_TTL) {
+ verbose(VERB_ALGO, "error response for prefetch in cache");
+ /* attempt to adjust the cache entry prefetch */
+ if(dns_cache_prefetch_adjust(qstate->env, &qstate->qinfo,
+ NORR_TTL, qstate->query_flags))
+ return error_response(qstate, id, rcode);
+ /* if that fails (not in cache), fall through to store err */
+ }
+ if((msg=msg_cache_lookup(qstate->env,
+ qstate->qinfo.qname, qstate->qinfo.qname_len,
+ qstate->qinfo.qtype, qstate->qinfo.qclass,
+ qstate->query_flags, 0,
+ qstate->env->cfg->serve_expired_ttl_reset)) != NULL) {
+ struct reply_info* rep = (struct reply_info*)msg->entry.data;
+ if(qstate->env->cfg->serve_expired &&
+ qstate->env->cfg->serve_expired_ttl_reset && rep &&
+ *qstate->env->now + qstate->env->cfg->serve_expired_ttl
+ > rep->serve_expired_ttl) {
+ verbose(VERB_ALGO, "reset serve-expired-ttl for "
+ "response in cache");
+ rep->serve_expired_ttl = *qstate->env->now +
+ qstate->env->cfg->serve_expired_ttl;
}
- if(qstate->env->cfg->serve_expired) {
- /* if serving expired contents, and such content is
- * already available, don't overwrite this servfail */
- struct msgreply_entry* msg;
- if((msg=msg_cache_lookup(qstate->env,
- qstate->qinfo.qname, qstate->qinfo.qname_len,
- qstate->qinfo.qtype, qstate->qinfo.qclass,
- qstate->query_flags, 0,
- qstate->env->cfg->serve_expired_ttl_reset))
- != NULL) {
- if(qstate->env->cfg->serve_expired_ttl_reset) {
- struct reply_info* rep =
- (struct reply_info*)msg->entry.data;
- if(rep && *qstate->env->now +
- qstate->env->cfg->serve_expired_ttl >
- rep->serve_expired_ttl) {
- rep->serve_expired_ttl =
- *qstate->env->now +
- qstate->env->cfg->serve_expired_ttl;
- }
- }
- lock_rw_unlock(&msg->entry.lock);
- return error_response(qstate, id, rcode);
- }
- /* serving expired contents, but nothing is cached
- * at all, so the servfail cache entry is useful
- * (stops waste of time on this servfail NORR_TTL) */
- } else {
- /* don't overwrite existing (non-expired) data in
- * cache with a servfail */
- struct msgreply_entry* msg;
- if((msg=msg_cache_lookup(qstate->env,
- qstate->qinfo.qname, qstate->qinfo.qname_len,
- qstate->qinfo.qtype, qstate->qinfo.qclass,
- qstate->query_flags, *qstate->env->now, 0))
- != NULL) {
- struct reply_info* rep = (struct reply_info*)
- msg->entry.data;
- if(FLAGS_GET_RCODE(rep->flags) ==
- LDNS_RCODE_NOERROR ||
- FLAGS_GET_RCODE(rep->flags) ==
- LDNS_RCODE_NXDOMAIN) {
- /* we have a good entry,
- * don't overwrite */
- lock_rw_unlock(&msg->entry.lock);
- return error_response(qstate, id, rcode);
- }
- lock_rw_unlock(&msg->entry.lock);
- }
-
+ if(rep && (FLAGS_GET_RCODE(rep->flags) ==
+ LDNS_RCODE_NOERROR ||
+ FLAGS_GET_RCODE(rep->flags) ==
+ LDNS_RCODE_NXDOMAIN ||
+ FLAGS_GET_RCODE(rep->flags) ==
+ LDNS_RCODE_YXDOMAIN) &&
+ (qstate->env->cfg->serve_expired ||
+ *qstate->env->now <= rep->ttl)) {
+ /* we have a good entry, don't overwrite */
+ lock_rw_unlock(&msg->entry.lock);
+ return error_response(qstate, id, rcode);
}
- memset(&err, 0, sizeof(err));
- err.flags = (uint16_t)(BIT_QR | BIT_RA);
- FLAGS_SET_RCODE(err.flags, rcode);
- err.qdcount = 1;
- err.ttl = NORR_TTL;
- err.prefetch_ttl = PREFETCH_TTL_CALC(err.ttl);
- err.serve_expired_ttl = NORR_TTL;
- /* do not waste time trying to validate this servfail */
- err.security = sec_status_indeterminate;
- verbose(VERB_ALGO, "store error response in message cache");
- iter_dns_store(qstate->env, &qstate->qinfo, &err, 0, 0, 0, NULL,
- qstate->query_flags, qstate->qstarttime);
- }
+ lock_rw_unlock(&msg->entry.lock);
+ /* nothing interesting is cached (already error response or
+ * expired good record when we don't serve expired), so this
+ * servfail cache entry is useful (stops waste of time on this
+ * servfail NORR_TTL) */
+ }
+ /* store in cache */
+ memset(&err, 0, sizeof(err));
+ err.flags = (uint16_t)(BIT_QR | BIT_RA);
+ FLAGS_SET_RCODE(err.flags, rcode);
+ err.qdcount = 1;
+ err.ttl = NORR_TTL;
+ err.prefetch_ttl = PREFETCH_TTL_CALC(err.ttl);
+ err.serve_expired_ttl = NORR_TTL;
+ /* do not waste time trying to validate this servfail */
+ err.security = sec_status_indeterminate;
+ verbose(VERB_ALGO, "store error response in message cache");
+ iter_dns_store(qstate->env, &qstate->qinfo, &err, 0, 0, 0, NULL,
+ qstate->query_flags, qstate->qstarttime);
return error_response(qstate, id, rcode);
}
return 1;
}
+/** fill fail address for later recovery */
+static void
+fill_fail_addr(struct iter_qstate* iq, struct sockaddr_storage* addr,
+ socklen_t addrlen)
+{
+ if(addrlen == 0) {
+ iq->fail_addr_type = 0;
+ return;
+ }
+ if(((struct sockaddr_in*)addr)->sin_family == AF_INET) {
+ iq->fail_addr_type = 4;
+ memcpy(&iq->fail_addr.in,
+ &((struct sockaddr_in*)addr)->sin_addr,
+ sizeof(iq->fail_addr.in));
+ }
+#ifdef AF_INET6
+ else if(((struct sockaddr_in*)addr)->sin_family == AF_INET6) {
+ iq->fail_addr_type = 6;
+ memcpy(&iq->fail_addr.in6,
+ &((struct sockaddr_in6*)addr)->sin6_addr,
+ sizeof(iq->fail_addr.in6));
+ }
+#endif
+ else {
+ iq->fail_addr_type = 0;
+ }
+}
+
+/** print fail addr to string */
+static void
+print_fail_addr(struct iter_qstate* iq, char* buf, size_t len)
+{
+ if(iq->fail_addr_type == 4) {
+ if(inet_ntop(AF_INET, &iq->fail_addr.in, buf,
+ (socklen_t)len) == 0)
+ (void)strlcpy(buf, "(inet_ntop error)", len);
+ }
+#ifdef AF_INET6
+ else if(iq->fail_addr_type == 6) {
+ if(inet_ntop(AF_INET6, &iq->fail_addr.in6, buf,
+ (socklen_t)len) == 0)
+ (void)strlcpy(buf, "(inet_ntop error)", len);
+ }
+#endif
+ else
+ (void)strlcpy(buf, "", len);
+}
+
/** add response specific error information for log servfail */
static void
errinf_reply(struct module_qstate* qstate, struct iter_qstate* iq)
if(qstate->env->cfg->val_log_level < 2 && !qstate->env->cfg->log_servfail)
return;
if((qstate->reply && qstate->reply->remote_addrlen != 0) ||
- (iq->fail_reply && iq->fail_reply->remote_addrlen != 0)) {
+ (iq->fail_addr_type != 0)) {
char from[256], frm[512];
if(qstate->reply && qstate->reply->remote_addrlen != 0)
addr_to_str(&qstate->reply->remote_addr,
qstate->reply->remote_addrlen, from,
sizeof(from));
else
- addr_to_str(&iq->fail_reply->remote_addr,
- iq->fail_reply->remote_addrlen, from,
- sizeof(from));
+ print_fail_addr(iq, from, sizeof(from));
snprintf(frm, sizeof(frm), "from %s", from);
errinf(qstate, frm);
}
* Generate a NS check request to obtain authoritative information
* on an NS rrset.
*
- * @param qstate: the qtstate that triggered the need to prime.
+ * @param qstate: the qstate that triggered the need to prime.
* @param iq: iterator query state.
* @param id: module id.
*/
/* We enforce a maximum number of query restarts. This is primarily a
* cheap way to prevent CNAME loops. */
- if(iq->query_restart_count > MAX_RESTART_COUNT) {
+ if(iq->query_restart_count > ie->max_query_restarts) {
verbose(VERB_QUERY, "request has exceeded the maximum number"
" of query restarts with %d", iq->query_restart_count);
errinf(qstate, "request has exceeded the maximum number "
errinf(qstate, "malloc failure for forward zone");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
+ if((qstate->query_flags&BIT_RD)==0) {
+ /* If the server accepts RD=0 queries and forwards
+ * with RD=1, then if the server is listed as an NS
+ * entry, it starts query loops. Stop that loop by
+ * disallowing the query. The RD=0 was previously used
+ * to check the cache with allow_snoop. For stubs,
+ * the iterator pass would have primed the stub and
+ * then cached information can be used for further
+ * queries. */
+ verbose(VERB_ALGO, "cannot forward RD=0 query, to stop query loops");
+ errinf(qstate, "cannot forward RD=0 query");
+ return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+ }
iq->refetch_glue = 0;
iq->minimisation_state = DONOT_MINIMISE_STATE;
/* the request has been forwarded.
/* see if this dp not useless.
* It is useless if:
- * o all NS items are required glue.
+ * o all NS items are required glue.
* or the query is for NS item that is required glue.
* o no addresses are provided.
* o RD qflag is on.
* Instead, go up one level, and try to get even further
- * If the root was useless, use safety belt information.
+ * If the root was useless, use safety belt information.
* Only check cache returns, because replies for servers
* could be useless but lead to loops (bumping into the
* same server reply) if useless-checked.
*/
- if(iter_dp_is_useless(&qstate->qinfo, qstate->query_flags,
- iq->dp, ie->supports_ipv4, ie->supports_ipv6)) {
+ if(iter_dp_is_useless(&qstate->qinfo, qstate->query_flags,
+ iq->dp, ie->supports_ipv4, ie->supports_ipv6,
+ ie->use_nat64)) {
struct delegpt* retdp = NULL;
if(!can_have_last_resort(qstate->env, iq->dp->name, iq->dp->namelen, iq->qchase.qclass, &retdp)) {
if(retdp) {
break;
}
/* Send the A request. */
- if(ie->supports_ipv4 &&
+ if((ie->supports_ipv4 || ie->use_nat64) &&
((ns->lame && !ns->done_pside4) ||
(!ns->lame && !ns->got4))) {
if(!generate_target_query(qstate, iq, id,
/* if this nameserver is at a delegation point, but that
* delegation point is a stub and we cannot go higher, skip*/
if( ((ie->supports_ipv6 && !ns->done_pside6) ||
- (ie->supports_ipv4 && !ns->done_pside4)) &&
+ ((ie->supports_ipv4 || ie->use_nat64) && !ns->done_pside4)) &&
!can_have_last_resort(qstate->env, ns->name, ns->namelen,
iq->qchase.qclass, NULL)) {
log_nametypeclass(VERB_ALGO, "cannot pside lookup ns "
"because it is also a stub/forward,",
ns->name, LDNS_RR_TYPE_NS, iq->qchase.qclass);
if(ie->supports_ipv6) ns->done_pside6 = 1;
- if(ie->supports_ipv4) ns->done_pside4 = 1;
+ if(ie->supports_ipv4 || ie->use_nat64) ns->done_pside4 = 1;
continue;
}
/* query for parent-side A and AAAA for nameservers */
return 0;
}
}
- if(ie->supports_ipv4 && !ns->done_pside4) {
+ if((ie->supports_ipv4 || ie->use_nat64) && !ns->done_pside4) {
/* Send the A request. */
if(!generate_parentside_target_query(qstate, iq, id,
ns->name, ns->namelen,
int tf_policy;
struct delegpt_addr* target;
struct outbound_entry* outq;
+ struct sockaddr_storage real_addr;
+ socklen_t real_addrlen;
int auth_fallback = 0;
uint8_t* qout_orig = NULL;
size_t qout_orig_len = 0;
iq->num_current_queries, iq->sent_count);
/* Make sure that we haven't run away */
- /* FIXME: is this check even necessary? */
if(iq->referral_count > MAX_REFERRAL_COUNT) {
verbose(VERB_QUERY, "request has exceeded the maximum "
"number of referrrals with %d", iq->referral_count);
errinf(qstate, "exceeded the maximum of referrals");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
- if(iq->sent_count > MAX_SENT_COUNT) {
+ if(iq->sent_count > ie->max_sent_count) {
verbose(VERB_QUERY, "request has exceeded the maximum "
"number of sends with %d", iq->sent_count);
errinf(qstate, "exceeded the maximum number of sends");
}
if(!ie->supports_ipv6)
delegpt_no_ipv6(iq->dp);
- if(!ie->supports_ipv4)
+ if(!ie->supports_ipv4 && !ie->use_nat64)
delegpt_no_ipv4(iq->dp);
delegpt_log(VERB_ALGO, iq->dp);
* the original query is one that matched too, so we have
* caps_server+1 number of matching queries now */
if(iq->caps_server+1 >= naddr*3 ||
- iq->caps_server*2+2 >= MAX_SENT_COUNT) {
+ iq->caps_server*2+2 >= (size_t)ie->max_sent_count) {
/* *2 on sentcount check because ipv6 may fail */
/* we're done, process the response */
verbose(VERB_ALGO, "0x20 fallback had %d responses "
/* We have a valid target. */
if(verbosity >= VERB_QUERY) {
log_query_info(VERB_QUERY, "sending query:", &iq->qinfo_out);
- log_name_addr(VERB_QUERY, "sending to target:", iq->dp->name,
+ log_name_addr(VERB_QUERY, "sending to target:", iq->dp->name,
&target->addr, target->addrlen);
verbose(VERB_ALGO, "dnssec status: %s%s",
iq->dnssec_expected?"expected": "not expected",
iq->dnssec_lame_query?" but lame_query anyway": "");
}
+
+ real_addr = target->addr;
+ real_addrlen = target->addrlen;
+
+ if(ie->use_nat64 && target->addr.ss_family == AF_INET) {
+ addr_to_nat64(&target->addr, &ie->nat64_prefix_addr,
+ ie->nat64_prefix_addrlen, ie->nat64_prefix_net,
+ &real_addr, &real_addrlen);
+ log_name_addr(VERB_QUERY, "applied NAT64:",
+ iq->dp->name, &real_addr, real_addrlen);
+ }
+
fptr_ok(fptr_whitelist_modenv_send_query(qstate->env->send_query));
outq = (*qstate->env->send_query)(&iq->qinfo_out,
iq->chase_flags | (iq->chase_to_rd?BIT_RD:0),
!qstate->blacklist&&(!iter_qname_indicates_dnssec(qstate->env,
&iq->qinfo_out)||target->attempts==1)?0:BIT_CD),
iq->dnssec_expected, iq->caps_fallback || is_caps_whitelisted(
- ie, iq), sq_check_ratelimit, &target->addr, target->addrlen,
+ ie, iq), sq_check_ratelimit, &real_addr, real_addrlen,
iq->dp->name, iq->dp->namelen,
(iq->dp->tcp_upstream || qstate->env->cfg->tcp_upstream),
(iq->dp->ssl_upstream || qstate->env->cfg->ssl_upstream),
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
log_addr(VERB_QUERY, "error sending query to auth server",
- &target->addr, target->addrlen);
+ &real_addr, real_addrlen);
if(qstate->env->cfg->qname_minimisation)
iq->minimisation_state = SKIP_MINIMISE_STATE;
return next_state(iq, QUERYTARGETS_STATE);
processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
struct iter_env* ie, int id)
{
- int dnsseclame = 0;
+ int dnsseclame = 0, origtypecname = 0;
enum response_type type;
iq->num_current_queries--;
/* YXDOMAIN is a permanent error, no need to retry */
type = RESPONSE_TYPE_ANSWER;
}
+ if(type == RESPONSE_TYPE_CNAME)
+ origtypecname = 1;
if(type == RESPONSE_TYPE_CNAME && iq->response->rep->an_numrrsets >= 1
&& ntohs(iq->response->rep->rrsets[0]->rk.type) == LDNS_RR_TYPE_DNAME) {
uint8_t* sname = NULL;
iq->minimisation_state = DONOT_MINIMISE_STATE;
}
if(FLAGS_GET_RCODE(iq->response->rep->flags) ==
- LDNS_RCODE_NXDOMAIN) {
+ LDNS_RCODE_NXDOMAIN && !origtypecname) {
/* Stop resolving when NXDOMAIN is DNSSEC
* signed. Based on assumption that nameservers
* serving signed zones do not return NXDOMAIN
* for empty-non-terminals. */
+ /* If this response is actually a CNAME type,
+ * the nxdomain rcode may not be for the qname,
+ * and so it is not the final response. */
if(iq->dnssec_expected)
return final_state(iq);
/* Make subrequest to validate intermediate
(*qstate->env->detach_subs)(qstate);
iq->num_target_queries = 0;
iq->response = NULL;
- iq->fail_reply = NULL;
+ iq->fail_addr_type = 0;
verbose(VERB_ALGO, "cleared outbound list for next round");
return next_state(iq, QUERYTARGETS_STATE);
} else if(type == RESPONSE_TYPE_CNAME) {
} else {
verbose(VERB_ALGO, "iterator TargetResponse failed");
delegpt_mark_neg(dpns, qstate->qinfo.qtype);
- if((dpns->got4 == 2 || !ie->supports_ipv4) &&
+ if((dpns->got4 == 2 || (!ie->supports_ipv4 && !ie->use_nat64)) &&
(dpns->got6 == 2 || !ie->supports_ipv6)) {
dpns->resolved = 1; /* fail the target */
/* do not count cached answers */
/* make sure QR flag is on */
iq->response->rep->flags |= BIT_QR;
+ /* explicitly set the EDE string to NULL */
+ iq->response->rep->reason_bogus_str = NULL;
+
/* we have finished processing this query */
qstate->ext_state[id] = module_finished;
}
/* parse message */
- iq->fail_reply = qstate->reply;
+ fill_fail_addr(iq, &qstate->reply->remote_addr,
+ qstate->reply->remote_addrlen);
prs = (struct msg_parse*)regional_alloc(qstate->env->scratch,
sizeof(struct msg_parse));
if(!prs) {
/** max number of nxdomains allowed for target lookups for a query and
* its subqueries when fallback has kicked in */
#define MAX_TARGET_NX_FALLBACK (MAX_TARGET_NX*2)
-/** max number of query restarts. Determines max number of CNAME chain. */
-#define MAX_RESTART_COUNT 11
/** max number of referrals. Makes sure resolver does not run away */
#define MAX_REFERRAL_COUNT 130
-/** max number of queries-sent-out. Make sure large NS set does not loop */
-#define MAX_SENT_COUNT 32
/** max number of queries for which to perform dnsseclameness detection,
* (rrsigs missing detection) after that, just pick up that response */
#define DNSSEC_LAME_DETECT_COUNT 4
#define RTT_BAND 400
/**
- * Global state for the iterator.
+ * Global state for the iterator.
*/
struct iter_env {
/** A flag to indicate whether or not we have an IPv6 route */
/** A flag to indicate whether or not we have an IPv4 route */
int supports_ipv4;
+ /** A flag to locally apply NAT64 to make IPv4 addrs into IPv6 */
+ int use_nat64;
+
+ /** NAT64 prefix address, cf. dns64_env->prefix_addr */
+ struct sockaddr_storage nat64_prefix_addr;
+
+ /** sizeof(sockaddr_in6) */
+ socklen_t nat64_prefix_addrlen;
+
+ /** CIDR mask length of NAT64 prefix */
+ int nat64_prefix_net;
+
/** A set of inetaddrs that should never be queried. */
struct iter_donotq* donotq;
/** number of retries on outgoing queries */
int outbound_msg_retry;
+
+ /** number of queries_sent */
+ int max_sent_count;
+
+ /** max number of query restarts to limit length of CNAME chain */
+ int max_query_restarts;
};
/**
/** true if there have been parse failures of reply packets */
int parse_failures;
/** a failure printout address for last received answer */
- struct comm_reply* fail_reply;
+ union {
+ struct in_addr in;
+#ifdef AF_INET6
+ struct in6_addr in6;
+#endif
+ } fail_addr;
+ /** which fail_addr, 0 is nothing, 4 or 6 */
+ int fail_addr_type;
};
/**
} else {
log_init(cfg->logfile, cfg->use_syslog, NULL);
}
+ ctx->pipe_pid = getpid();
cfg_apply_local_port_policy(cfg, 65536);
config_apply(cfg);
if(!modstack_setup(&ctx->mods, cfg->module_conf, ctx->env))
pid_t bg_pid;
/** tid of bg worker thread */
ub_thread_type bg_tid;
+ /** pid when pipes are created. This was the process when the
+ * setup was called. Helps with clean up, so we can tell after a fork
+ * which side of the fork the delete is on. */
+ pid_t pipe_pid;
+ /** when threaded, the worker that exists in the created thread. */
+ struct libworker* thread_worker;
/** do threading (instead of forking) for async resolution */
int dothread;
int do_stop = 1;
if(!ctx) return;
+ /* if the delete is called but it has forked, and before the fork
+ * the context was finalized, then the bg worker is not stopped
+ * from here. There is one worker, but two contexts that refer to
+ * it and only one should clean up, the one with getpid == pipe_pid.*/
+ if(ctx->created_bg && ctx->pipe_pid != getpid()) {
+ do_stop = 0;
+#ifndef USE_WINSOCK
+ /* Stop events from getting deregistered, if the backend is
+ * epoll, the epoll fd is the same as the other process.
+ * That process should deregister them. */
+ if(ctx->qq_pipe->listen_com)
+ ctx->qq_pipe->listen_com->event_added = 0;
+ if(ctx->qq_pipe->res_com)
+ ctx->qq_pipe->res_com->event_added = 0;
+ if(ctx->rr_pipe->listen_com)
+ ctx->rr_pipe->listen_com->event_added = 0;
+ if(ctx->rr_pipe->res_com)
+ ctx->rr_pipe->res_com->event_added = 0;
+#endif
+ }
/* see if bg thread is created and if threads have been killed */
/* no locks, because those may be held by terminated threads */
/* for processes the read pipe is closed and we see that on read */
#ifdef HAVE_PTHREAD
- if(ctx->created_bg && ctx->dothread) {
+ if(ctx->created_bg && ctx->dothread && do_stop) {
if(pthread_kill(ctx->bg_tid, 0) == ESRCH) {
/* thread has been killed */
do_stop = 0;
#endif /* HAVE_PTHREAD */
if(do_stop)
ub_stop_bg(ctx);
+ if(ctx->created_bg && ctx->pipe_pid != getpid() && ctx->thread_worker) {
+ /* This delete is happening from a different process. Delete
+ * the thread worker from this process memory space. The
+ * thread is not there to do so, so it is freed here. */
+ struct ub_event_base* evbase = comm_base_internal(
+ ctx->thread_worker->base);
+ libworker_delete_event(ctx->thread_worker);
+ ctx->thread_worker = NULL;
+#ifdef USE_MINI_EVENT
+ ub_event_base_free(evbase);
+#else
+ /* cannot event_base_free, because the epoll_fd cleanup
+ * in libevent could stop the original event_base in the
+ * other process from working. */
+ free(evbase);
+#endif
+ }
libworker_delete_event(ctx->event_worker);
modstack_desetup(&ctx->mods, ctx->env);
hints_delete(w->env->hints);
w->env->hints = NULL;
}
- if(cfg->ssl_upstream || (cfg->tls_cert_bundle && cfg->tls_cert_bundle[0]) || cfg->tls_win_cert) {
- w->sslctx = connect_sslctx_create(NULL, NULL,
- cfg->tls_cert_bundle, cfg->tls_win_cert);
- if(!w->sslctx) {
- /* to make the setup fail after unlock */
- hints_delete(w->env->hints);
- w->env->hints = NULL;
- }
+ w->sslctx = connect_sslctx_create(NULL, NULL,
+ cfg->tls_cert_bundle, cfg->tls_win_cert);
+ if(!w->sslctx) {
+ /* to make the setup fail after unlock */
+ hints_delete(w->env->hints);
+ w->env->hints = NULL;
}
if(!w->is_bg || w->is_bg_thread) {
lock_basic_unlock(&ctx->cfglock);
w = libworker_setup(ctx, 1, NULL);
if(!w) return UB_NOMEM;
w->is_bg_thread = 1;
+ ctx->thread_worker = w;
#ifdef ENABLE_LOCK_CHECKS
w->thread_num = 1; /* for nicer DEBUG checklocks */
#endif
edns->opt_list_out = NULL;
edns->opt_list_inplace_cb_out = NULL;
edns->padding_block_size = 0;
+ edns->cookie_present = 0;
+ edns->cookie_valid = 0;
if(sldns_buffer_capacity(w->back->udp_buff) < 65535)
edns->udp_size = (uint16_t)sldns_buffer_capacity(
w->back->udp_buff);
* unbound was compiled with, otherwise it wouldn't work, the event and
* event_base structures would be different.
*/
-#ifndef _UB_UNBOUND_EVENT_H
-#define _UB_UNBOUND_EVENT_H
+#ifndef UB_UNBOUND_EVENT_H
+#define UB_UNBOUND_EVENT_H
#ifdef __cplusplus
extern "C" {
* @param callback: this is called on completion of the resolution.
* It is called as:
* void callback(void* mydata, int rcode, void* packet, int packet_len,
- * int sec, char* why_bogus)
+ * int sec, char* why_bogus, int was_ratelimited)
* with mydata: the same as passed here, you may pass NULL,
* with rcode: 0 on no error, nonzero for mostly SERVFAIL situations,
* this is a DNS rcode.
* with packet_len: length in bytes of the packet buffer.
* with sec: 0 if insecure, 1 if bogus, 2 if DNSSEC secure.
* with why_bogus: text string explaining why it is bogus (or NULL).
+ * with was_ratelimited: if the query was ratelimited.
* These point to buffers inside unbound; do not deallocate the packet or
* error string.
*
}
#endif
-#endif /* _UB_UNBOUND_H */
+#endif /* UB_UNBOUND_EVENT_H */
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
* This software is open source.
- *
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
- *
+ *
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
- *
+ *
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
- *
+ *
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
- *
+ *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
/**
* \file
*
- * This file contains functions to resolve DNS queries and
+ * This file contains functions to resolve DNS queries and
* validate the answers. Synchronously and asynchronously.
*
* Several ways to use this interface from an application wishing
* ... or process() calls my_callback() with results.
*
* ... if the application has nothing more to do, wait for answer
- * ub_wait(ctx);
+ * ub_wait(ctx);
*
* Application threaded. Blocking.
* Blocking, same as above. The current thread does the work.
* CRYPTO_set_id_callback and CRYPTO_set_locking_callback.
*
* If no threading is compiled in, the above async example uses fork(2) to
- * create a process to perform the work. The forked process exits when the
+ * create a process to perform the work. The forked process exits when the
* calling process exits, or ctx_delete() is called.
* Otherwise, for asynchronous with threading, a worker thread is created.
*
* The second calls another worker thread (or process) to perform the work.
* And no buffers need to be set up, but a context-switch happens.
*/
-#ifndef _UB_UNBOUND_H
-#define _UB_UNBOUND_H
+#ifndef UB_UNBOUND_H
+#define UB_UNBOUND_H
#ifdef __cplusplus
extern "C" {
/** the class asked for */
int qclass;
- /**
- * a list of network order DNS rdata items, terminated with a
+ /**
+ * a list of network order DNS rdata items, terminated with a
* NULL pointer, so that data[0] is the first result entry,
- * data[1] the second, and the last entry is NULL.
+ * data[1] the second, and the last entry is NULL.
* If there was no data, data[0] is NULL.
*/
char** data;
/** the length in bytes of the data items, len[i] for data[i] */
int* len;
- /**
- * canonical name for the result (the final cname).
+ /**
+ * canonical name for the result (the final cname).
* zero terminated string.
* May be NULL if no canonical name exists.
*/
*/
int havedata;
- /**
+ /**
* If there was no data, and the domain did not exist, this is true.
- * If it is false, and there was no data, then the domain name
+ * If it is false, and there was no data, then the domain name
* is purported to exist, but the requested data type is not available.
*/
int nxdomain;
*/
int secure;
- /**
- * If the result was not secure (secure==0), and this result is due
+ /**
+ * If the result was not secure (secure==0), and this result is due
* to a security failure, bogus is true.
* This means the data has been actively tampered with, signatures
- * failed, expected signatures were not present, timestamps on
+ * failed, expected signatures were not present, timestamps on
* signatures were out of date and so on.
*
- * If !secure and !bogus, this can happen if the data is not secure
- * because security is disabled for that domain name.
+ * If !secure and !bogus, this can happen if the data is not secure
+ * because security is disabled for that domain name.
* This means the data is from a domain where data is not signed.
*/
int bogus;
-
+
/**
* If the result is bogus this contains a string (zero terminated)
* that describes the failure. There may be other errors as well
* The readable function definition looks like:
* void my_callback(void* my_arg, int err, struct ub_result* result);
* It is called with
- * void* my_arg: your pointer to a (struct of) data of your choice,
+ * void* my_arg: your pointer to a (struct of) data of your choice,
* or NULL.
* int err: if 0 all is OK, otherwise an error occurred and no results
* are forthcoming.
* This is a power-users interface that lets you specify all sorts
* of options.
* @param str: the string is malloced and returned here. NULL on error.
- * The caller must free() the string. In cases with multiple
- * entries (auto-trust-anchor-file), a newline delimited list is
+ * The caller must free() the string. In cases with multiple
+ * entries (auto-trust-anchor-file), a newline delimited list is
* returned in the string.
* @return 0 if OK else an error code (malloc failure, syntax error).
*/
int ub_ctx_config(struct ub_ctx* ctx, const char* fname);
/**
- * Set machine to forward DNS queries to, the caching resolver to use.
- * IP4 or IP6 address. Forwards all DNS requests to that machine, which
- * is expected to run a recursive resolver. If the proxy is not
- * DNSSEC-capable, validation may fail. Can be called several times, in
+ * Set machine to forward DNS queries to, the caching resolver to use.
+ * IP4 or IP6 address. Forwards all DNS requests to that machine, which
+ * is expected to run a recursive resolver. If the proxy is not
+ * DNSSEC-capable, validation may fail. Can be called several times, in
* that case the addresses are used as backup servers.
*
* To read the list of nameservers from /etc/resolv.conf (from DHCP or so),
/**
* Read list of hosts from the filename given.
- * Usually "/etc/hosts".
+ * Usually "/etc/hosts".
* These addresses are not flagged as DNSSEC secure when queried for.
*
* @param ctx: context.
/**
* Add a trust anchor to the given context.
* The trust anchor is a string, on one line, that holds a valid DNSKEY or
- * DS RR.
+ * DS RR.
* @param ctx: context.
* At this time it is only possible to add trusted keys before the
* first resolve is done.
* Set debug verbosity for the context
* Output is directed to stderr.
* @param ctx: context.
- * @param d: debug level, 0 is off, 1 is very minimal, 2 is detailed,
+ * @param d: debug level, 0 is off, 1 is very minimal, 2 is detailed,
* and 3 is lots.
* @return 0 if OK, else error.
*/
/**
* Set a context behaviour for asynchronous action.
* @param ctx: context.
- * @param dothread: if true, enables threading and a call to resolve_async()
+ * @param dothread: if true, enables threading and a call to resolve_async()
* creates a thread to handle work in the background.
* If false, a process is forked to handle work in the background.
- * Changes to this setting after async() calls have been made have
+ * Changes to this setting after async() calls have been made have
* no effect (delete and re-create the context to change).
* @return 0 if OK, else error.
*/
/**
* Wait for a context to finish with results. Calls ub_process() after
- * the wait for you. After the wait, there are no more outstanding
+ * the wait for you. After the wait, there are no more outstanding
* asynchronous queries.
* @param ctx: context.
* @return: 0 if OK, else error.
* @param rrtype: type of RR in host order, 1 is A (address).
* @param rrclass: class of RR in host order, 1 is IN (for internet).
* @param result: the result data is returned in a newly allocated result
- * structure. May be NULL on return, return value is set to an error
+ * structure. May be NULL on return, return value is set to an error
* in that case (out of memory).
* @return 0 if OK, else error.
*/
-int ub_resolve(struct ub_ctx* ctx, const char* name, int rrtype,
+int ub_resolve(struct ub_ctx* ctx, const char* name, int rrtype,
int rrclass, struct ub_result** result);
/**
* If an error happens during processing, your callback will be called
* with error set to a nonzero value (and result==NULL).
* @param async_id: if you pass a non-NULL value, an identifier number is
- * returned for the query as it is in progress. It can be used to
+ * returned for the query as it is in progress. It can be used to
* cancel the query.
* @return 0 if OK, else error.
*/
-int ub_resolve_async(struct ub_ctx* ctx, const char* name, int rrtype,
+int ub_resolve_async(struct ub_ctx* ctx, const char* name, int rrtype,
int rrclass, void* mydata, ub_callback_type callback, int* async_id);
/**
*/
void ub_resolve_free(struct ub_result* result);
-/**
+/**
* Convert error value to a human readable string.
* @param err: error code from one of the libunbound functions.
* The error codes are from the type enum ub_ctx_err.
int ub_ctx_print_local_zones(struct ub_ctx* ctx);
/**
- * Add a new zone with the zonetype to the local authority info of the
+ * Add a new zone with the zonetype to the local authority info of the
* library.
* @param ctx: context. Is finalized by the routine.
* @param zone_name: name of the zone in text, "example.com"
* @param zone_type: type of the zone (like for unbound.conf) in text.
* @return 0 if OK, else error.
*/
-int ub_ctx_zone_add(struct ub_ctx* ctx, const char *zone_name,
+int ub_ctx_zone_add(struct ub_ctx* ctx, const char *zone_name,
const char *zone_type);
/**
*/
const char* ub_version(void);
-/**
+/**
* Some global statistics that are not in struct stats_info,
* this struct is shared on a shm segment (shm-key in unbound.conf)
*/
long long num_queries;
/** number of queries that have been dropped/ratelimited by ip. */
long long num_queries_ip_ratelimited;
+ /** number of queries with a valid DNS Cookie. */
+ long long num_queries_cookie_valid;
+ /** number of queries with only the client part of the DNS Cookie. */
+ long long num_queries_cookie_client;
+ /** number of queries with invalid DNS Cookie. */
+ long long num_queries_cookie_invalid;
/** number of queries that had a cache-miss. */
long long num_queries_missed_cache;
/** number of prefetch queries - cachehits with prefetch */
long long num_queries_prefetch;
-
+ /** number of queries which are too late to process */
+ long long num_queries_timed_out;
+ /** the longest wait time in the queue */
+ long long max_query_time_us;
/**
- * Sum of the querylistsize of the worker for
+ * Sum of the querylistsize of the worker for
* every query that missed cache. To calculate average.
*/
long long sum_query_list_size;
long long tcp_accept_usage;
/** expired answers served from cache */
long long ans_expired;
- /** histogram data exported to array
+ /** histogram data exported to array
* if the array is the same size, no data is lost, and
* if all histograms are same size (is so by default) then
* adding up works well. */
long long hist[UB_STATS_BUCKET_NUM];
-
+
/** number of message cache entries */
long long msg_cache_count;
/** number of rrset cache entries */
/** number of key cache entries */
long long key_cache_count;
+ /** maximum number of collisions in the msg cache */
+ long long msg_cache_max_collisions;
+ /** maximum number of collisions in the rrset cache */
+ long long rrset_cache_max_collisions;
+
/** number of queries that used dnscrypt */
long long num_query_dnscrypt_crypted;
/** number of queries that queried dnscrypt certificates */
/** number of queries answered from edns-subnet specific data, and
* the answer was from the edns-subnet cache. */
long long num_query_subnet_cache;
+ /** number of queries served from cachedb */
+ long long num_query_cachedb;
/** number of bytes in the stream wait buffers */
long long mem_stream_wait;
/** number of bytes in the HTTP2 query buffers */
long long rpz_action[UB_STATS_RPZ_ACTION_NUM];
};
-/**
+/**
* Statistics to send over the control pipe when asked
* This struct is made to be memcopied, sent in binary.
* shm mapped with (number+1) at num_threads+1, with first as total
}
#endif
-#endif /* _UB_UNBOUND_H */
+#endif /* UB_UNBOUND_H */
auth_data_delete(node);
}
if(z->rpz) {
- rpz_remove_rr(z->rpz, z->namelen, dname, dname_len, rr_type,
- rr_class, rdata, rdatalen);
+ rpz_remove_rr(z->rpz, z->name, z->namelen, dname, dname_len,
+ rr_type, rr_class, rdata, rdatalen);
}
return 1;
}
== 0) {
msg->rep->rrsets[i]->rk.dname = newname;
msg->rep->rrsets[i]->rk.dname_len = newlen;
+ msg->rep->rrsets[i]->entry.hash = rrset_key_hash(&msg->rep->rrsets[i]->rk);
}
}
}
edns.opt_list_out = NULL;
edns.opt_list_inplace_cb_out = NULL;
edns.padding_block_size = 0;
+ edns.cookie_present = 0;
+ edns.cookie_valid = 0;
if(sldns_buffer_capacity(buf) < 65535)
edns.udp_size = (uint16_t)sldns_buffer_capacity(buf);
else edns.udp_size = 65535;
edns.opt_list_out = NULL;
edns.opt_list_inplace_cb_out = NULL;
edns.padding_block_size = 0;
+ edns.cookie_present = 0;
+ edns.cookie_valid = 0;
if(sldns_buffer_capacity(buf) < 65535)
edns.udp_size = (uint16_t)sldns_buffer_capacity(buf);
else edns.udp_size = 65535;
size_t j;
if(!rrlist[i])
continue;
- if(rrlist[i] && rrlist[i]->type == LDNS_RR_TYPE_ZONEMD &&
+ if(rrlist[i]->type == LDNS_RR_TYPE_ZONEMD &&
query_dname_compare(z->name, node->name)==0) {
/* omit RRSIGs over type ZONEMD at apex */
continue;
slabhash_remove(env->msg_cache, h, &k);
}
-/** remove servfail msg cache entry */
-static void
-msg_del_servfail(struct module_env* env, struct query_info* qinfo,
- uint32_t flags)
-{
- struct msgreply_entry* e;
- /* see if the entry is servfail, and then remove it, so that
- * lookups move from the cacheresponse stage to the recursionresponse
- * stage */
- e = msg_cache_lookup(env, qinfo->qname, qinfo->qname_len,
- qinfo->qtype, qinfo->qclass, flags, 0, 0);
- if(!e) return;
- /* we don't check for the ttl here, also expired servfail entries
- * are removed. If the user uses serve-expired, they would still be
- * used to answer from cache */
- if(FLAGS_GET_RCODE(((struct reply_info*)e->entry.data)->flags)
- != LDNS_RCODE_SERVFAIL) {
- lock_rw_unlock(&e->entry.lock);
- return;
- }
- lock_rw_unlock(&e->entry.lock);
- msg_cache_remove(env, qinfo->qname, qinfo->qname_len, qinfo->qtype,
- qinfo->qclass, flags);
-}
-
void
dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
hashvalue_type hash, struct reply_info* rep, time_t leeway, int pside,
/* we do not store the message, but we did store the RRs,
* which could be useful for delegation information */
verbose(VERB_ALGO, "TTL 0: dropped msg from cache");
- free(rep);
- /* if the message is SERVFAIL in cache, remove that SERVFAIL,
+ reply_info_delete(rep, NULL);
+ /* if the message is in the cache, remove that msg,
* so that the TTL 0 response can be returned for future
- * responses (i.e. don't get answered by the servfail from
+ * responses (i.e. don't get answered from
* cache, but instead go to recursion to get this TTL0
- * response). */
- msg_del_servfail(env, qinfo, flags);
+ * response).
+ * Possible messages that could be in the cache:
+ * - SERVFAIL
+ * - NXDOMAIN
+ * - NODATA
+ * - an older record that is expired
+ * - an older record that did not yet expire */
+ msg_cache_remove(env, qinfo->qname, qinfo->qname_len,
+ qinfo->qtype, qinfo->qclass, flags);
return;
}
if(!msg->rep)
return NULL;
msg->rep->reason_bogus = LDNS_EDE_NONE;
+ msg->rep->reason_bogus_str = NULL;
if(num > RR_COUNT_MAX)
return NULL; /* integer overflow protection */
msg->rep->rrsets = (struct ub_packed_rrset_key**)
r->serve_expired_ttl < now) {
return NULL;
}
+ /* Ignore expired failure answers */
+ if(FLAGS_GET_RCODE(r->flags) !=
+ LDNS_RCODE_NOERROR &&
+ FLAGS_GET_RCODE(r->flags) !=
+ LDNS_RCODE_NXDOMAIN &&
+ FLAGS_GET_RCODE(r->flags) !=
+ LDNS_RCODE_YXDOMAIN)
+ return 0;
} else {
return NULL;
}
msg->rep->rrset_count = r->rrset_count;
msg->rep->authoritative = r->authoritative;
msg->rep->reason_bogus = r->reason_bogus;
+ if(r->reason_bogus_str) {
+ msg->rep->reason_bogus_str = regional_strdup(region, r->reason_bogus_str);
+ }
+
if(!rrset_array_lock(r->ref, r->rrset_count, now_control)) {
return NULL;
}
/* ttl must be relative ;i.e. 0..86400 not time(0)+86400.
* the env->now is added to message and RRsets in this routine. */
/* the leeway is used to invalidate other rrsets earlier */
-
if(is_referral) {
/* store rrsets */
struct rrset_ref ref;
((ntohs(ref.key->rk.type)==LDNS_RR_TYPE_NS
&& !pside) ? qstarttime:*env->now + leeway));
}
- free(rep);
+ reply_info_delete(rep, NULL);
return 1;
} else {
/* store msg, and rrsets */
* in queries per second. */
int infra_ip_ratelimit = 0;
+/** ratelimit value for client ip addresses,
+ * in queries per second.
+ * For clients with a valid DNS Cookie. */
+int infra_ip_ratelimit_cookie = 0;
+
size_t
infra_sizefunc(void* k, void* ATTR_UNUSED(d))
{
return s;
}
+/* Returns 1 if the limit has not been exceeded, 0 otherwise. */
+static int
+check_ip_ratelimit(struct sockaddr_storage* addr, socklen_t addrlen,
+ struct sldns_buffer* buffer, int premax, int max, int has_cookie)
+{
+ int limit;
+
+ if(has_cookie) limit = infra_ip_ratelimit_cookie;
+ else limit = infra_ip_ratelimit;
+
+ /* Disabled */
+ if(limit == 0) return 1;
+
+ if(premax <= limit && max > limit) {
+ char client_ip[128], qnm[LDNS_MAX_DOMAINLEN+1+12+12];
+ addr_to_str(addr, addrlen, client_ip, sizeof(client_ip));
+ qnm[0]=0;
+ if(sldns_buffer_limit(buffer)>LDNS_HEADER_SIZE &&
+ LDNS_QDCOUNT(sldns_buffer_begin(buffer))!=0) {
+ (void)sldns_wire2str_rrquestion_buf(
+ sldns_buffer_at(buffer, LDNS_HEADER_SIZE),
+ sldns_buffer_limit(buffer)-LDNS_HEADER_SIZE,
+ qnm, sizeof(qnm));
+ if(strlen(qnm)>0 && qnm[strlen(qnm)-1]=='\n')
+ qnm[strlen(qnm)-1] = 0; /*remove newline*/
+ if(strchr(qnm, '\t'))
+ *strchr(qnm, '\t') = ' ';
+ if(strchr(qnm, '\t'))
+ *strchr(qnm, '\t') = ' ';
+ verbose(VERB_OPS, "ip_ratelimit exceeded %s %d%s %s",
+ client_ip, limit,
+ has_cookie?"(cookie)":"", qnm);
+ } else {
+ verbose(VERB_OPS, "ip_ratelimit exceeded %s %d%s (no query name)",
+ client_ip, limit,
+ has_cookie?"(cookie)":"");
+ }
+ }
+ return (max <= limit);
+}
+
int infra_ip_ratelimit_inc(struct infra_cache* infra,
struct sockaddr_storage* addr, socklen_t addrlen, time_t timenow,
- int backoff, struct sldns_buffer* buffer)
+ int has_cookie, int backoff, struct sldns_buffer* buffer)
{
int max;
struct lruhash_entry* entry;
(*cur)++;
max = infra_rate_max(entry->data, timenow, backoff);
lock_rw_unlock(&entry->lock);
-
- if(premax <= infra_ip_ratelimit && max > infra_ip_ratelimit) {
- char client_ip[128], qnm[LDNS_MAX_DOMAINLEN+1+12+12];
- addr_to_str(addr, addrlen, client_ip, sizeof(client_ip));
- qnm[0]=0;
- if(sldns_buffer_limit(buffer)>LDNS_HEADER_SIZE &&
- LDNS_QDCOUNT(sldns_buffer_begin(buffer))!=0) {
- (void)sldns_wire2str_rrquestion_buf(
- sldns_buffer_at(buffer, LDNS_HEADER_SIZE),
- sldns_buffer_limit(buffer)-LDNS_HEADER_SIZE,
- qnm, sizeof(qnm));
- if(strlen(qnm)>0 && qnm[strlen(qnm)-1]=='\n')
- qnm[strlen(qnm)-1] = 0; /*remove newline*/
- if(strchr(qnm, '\t'))
- *strchr(qnm, '\t') = ' ';
- if(strchr(qnm, '\t'))
- *strchr(qnm, '\t') = ' ';
- verbose(VERB_OPS, "ip_ratelimit exceeded %s %d %s",
- client_ip, infra_ip_ratelimit, qnm);
- } else {
- verbose(VERB_OPS, "ip_ratelimit exceeded %s %d (no query name)",
- client_ip, infra_ip_ratelimit);
- }
- }
- return (max <= infra_ip_ratelimit);
+ return check_ip_ratelimit(addr, addrlen, buffer, premax, max,
+ has_cookie);
}
/* create */
/** ip ratelimit, 0 is off */
extern int infra_ip_ratelimit;
+/** ip ratelimit for DNS Cookie clients, 0 is off */
+extern int infra_ip_ratelimit_cookie;
/**
* key for ip_ratelimit lookups, a source IP.
* @param addr: client address
* @param addrlen: client address length
* @param timenow: what time it is now.
+ * @param has_cookie: if the request came with a DNS Cookie.
* @param backoff: if backoff is enabled.
* @param buffer: with query for logging.
* @return 1 if it could be incremented. 0 if the increment overshot the
* ratelimit and the query should be dropped. */
int infra_ip_ratelimit_inc(struct infra_cache* infra,
struct sockaddr_storage* addr, socklen_t addrlen, time_t timenow,
- int backoff, struct sldns_buffer* buffer);
+ int has_cookie, int backoff, struct sldns_buffer* buffer);
/**
* Get memory used by the infra cache.
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
* This software is open source.
- *
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
- *
+ *
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
- *
+ *
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
- *
+ *
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
- *
+ *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
#ifdef HAVE_NET_IF_H
#include <net/if.h>
#endif
-
+#ifdef HAVE_LINUX_NET_TSTAMP_H
+#include <linux/net_tstamp.h>
+#endif
/** number of queued TCP connections for listen() */
-#define TCP_BACKLOG 256
+#define TCP_BACKLOG 256
#ifndef THREADS_DISABLED
/** lock on the counter of stream buffer memory */
log_err("systemd sd_listen_fds(): %s", strerror(-r));
return -1;
}
-
+
for(i = 0; i < r; i++) {
if(sd_is_socket(SD_LISTEN_FDS_START + i, family, socktype, listen)) {
s = SD_LISTEN_FDS_START + i;
return -1;
}
#else
- if(WSAGetLastError() == WSAEAFNOSUPPORT ||
+ if(WSAGetLastError() == WSAEAFNOSUPPORT ||
WSAGetLastError() == WSAEPROTONOSUPPORT) {
*noproto = 1;
return -1;
#endif
if(listen) {
#ifdef SO_REUSEADDR
- if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&on,
+ if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
log_err("setsockopt(.. SO_REUSEADDR ..) failed: %s",
sock_strerror(errno));
socklen_t slen = (socklen_t)sizeof(got);
# ifdef SO_RCVBUFFORCE
/* Linux specific: try to use root permission to override
- * system limits on rcvbuf. The limit is stored in
+ * system limits on rcvbuf. The limit is stored in
* /proc/sys/net/core/rmem_max or sysctl net.core.rmem_max */
- if(setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, (void*)&rcv,
+ if(setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, (void*)&rcv,
(socklen_t)sizeof(rcv)) < 0) {
if(errno != EPERM) {
log_err("setsockopt(..., SO_RCVBUFFORCE, "
return -1;
}
# endif /* SO_RCVBUFFORCE */
- if(setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&rcv,
+ if(setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&rcv,
(socklen_t)sizeof(rcv)) < 0) {
log_err("setsockopt(..., SO_RCVBUF, "
"...) failed: %s", sock_strerror(errno));
}
/* check if we got the right thing or if system
* reduced to some system max. Warn if so */
- if(getsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&got,
+ if(getsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&got,
&slen) >= 0 && got < rcv/2) {
log_warn("so-rcvbuf %u was not granted. "
"Got %u. To fix: start with "
socklen_t slen = (socklen_t)sizeof(got);
# ifdef SO_SNDBUFFORCE
/* Linux specific: try to use root permission to override
- * system limits on sndbuf. The limit is stored in
+ * system limits on sndbuf. The limit is stored in
* /proc/sys/net/core/wmem_max or sysctl net.core.wmem_max */
- if(setsockopt(s, SOL_SOCKET, SO_SNDBUFFORCE, (void*)&snd,
+ if(setsockopt(s, SOL_SOCKET, SO_SNDBUFFORCE, (void*)&snd,
(socklen_t)sizeof(snd)) < 0) {
if(errno != EPERM) {
log_err("setsockopt(..., SO_SNDBUFFORCE, "
return -1;
}
# endif /* SO_SNDBUFFORCE */
- if(setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void*)&snd,
+ if(setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void*)&snd,
(socklen_t)sizeof(snd)) < 0) {
log_err("setsockopt(..., SO_SNDBUF, "
"...) failed: %s", sock_strerror(errno));
}
/* check if we got the right thing or if system
* reduced to some system max. Warn if so */
- if(getsockopt(s, SOL_SOCKET, SO_SNDBUF, (void*)&got,
+ if(getsockopt(s, SOL_SOCKET, SO_SNDBUF, (void*)&got,
&slen) >= 0 && got < snd/2) {
log_warn("so-sndbuf %u was not granted. "
"Got %u. To fix: start with "
# endif
) {
int val=(v6only==2)?0:1;
- if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
(void*)&val, (socklen_t)sizeof(val)) < 0) {
log_err("setsockopt(..., IPV6_V6ONLY"
", ...) failed: %s", sock_strerror(errno));
int action;
# if defined(IP_PMTUDISC_OMIT)
action = IP_PMTUDISC_OMIT;
- if (setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER,
+ if (setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER,
&action, (socklen_t)sizeof(action)) < 0) {
if (errno != EINVAL) {
/* the IP_DONTFRAG option if defined in the 11.0 OSX headers,
* but does not work on that version, so we exclude it */
int off = 0;
- if (setsockopt(s, IPPROTO_IP, IP_DONTFRAG,
+ if (setsockopt(s, IPPROTO_IP, IP_DONTFRAG,
&off, (socklen_t)sizeof(off)) < 0) {
log_err("setsockopt(..., IP_DONTFRAG, ...) failed: %s",
strerror(errno));
if(WSAGetLastError() != WSAEADDRINUSE &&
WSAGetLastError() != WSAEADDRNOTAVAIL &&
!(WSAGetLastError() == WSAEACCES && verbosity < 4 && !listen)) {
- log_err_addr("can't bind socket",
+ log_err_addr("can't bind socket",
wsa_strerror(WSAGetLastError()),
(struct sockaddr_storage*)addr, addrlen);
}
}
#endif
#ifdef SO_REUSEADDR
- if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&on,
+ if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
log_err("setsockopt(.. SO_REUSEADDR ..) failed: %s",
sock_strerror(errno));
&& !got_fd_from_systemd
# endif
) {
- if(setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
+ if(setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
(void*)&on, (socklen_t)sizeof(on)) < 0) {
log_err("setsockopt(..., IPV6_V6ONLY, ...) failed: %s",
sock_strerror(errno));
addr->ai_addrlen);
}
#else
- log_err_addr("can't bind socket",
+ log_err_addr("can't bind socket",
wsa_strerror(WSAGetLastError()),
(struct sockaddr_storage*)addr->ai_addr,
addr->ai_addrlen);
/* 5 is recommended on linux */
qlen = 5;
#endif
- if ((setsockopt(s, IPPROTO_TCP, TCP_FASTOPEN, &qlen,
+ if ((setsockopt(s, IPPROTO_TCP, TCP_FASTOPEN, &qlen,
sizeof(qlen))) == -1 ) {
#ifdef ENOPROTOOPT
/* squelch ENOPROTOOPT: freebsd server mode with kernel support
* Create socket from getaddrinfo results
*/
static int
-make_sock(int stype, const char* ifname, const char* port,
+make_sock(int stype, const char* ifname, const char* port,
struct addrinfo *hints, int v6only, int* noip6, size_t rcv, size_t snd,
int* reuseport, int transparent, int tcp_mss, int nodelay, int freebind,
int use_systemd, int dscp, struct unbound_socket* ub_sock)
return -1;
}
#endif
- log_err("node %s:%s getaddrinfo: %s %s",
+ log_err("node %s:%s getaddrinfo: %s %s",
ifname?ifname:"default", port, gai_strerror(r),
#ifdef EAI_SYSTEM
- r==EAI_SYSTEM?(char*)strerror(errno):""
+ (r==EAI_SYSTEM?(char*)strerror(errno):"")
#else
""
#endif
/** make socket and first see if ifname contains port override info */
static int
-make_sock_port(int stype, const char* ifname, const char* port,
+make_sock_port(int stype, const char* ifname, const char* port,
struct addrinfo *hints, int v6only, int* noip6, size_t rcv, size_t snd,
int* reuseport, int transparent, int tcp_mss, int nodelay, int freebind,
int use_systemd, int dscp, struct unbound_socket* ub_sock)
return 1;
}
+/** set fd to receive software timestamps */
+static int
+set_recvtimestamp(int s)
+{
+#ifdef HAVE_LINUX_NET_TSTAMP_H
+ int opt = SOF_TIMESTAMPING_RX_SOFTWARE | SOF_TIMESTAMPING_SOFTWARE;
+ if (setsockopt(s, SOL_SOCKET, SO_TIMESTAMPNS, (void*)&opt, (socklen_t)sizeof(opt)) < 0) {
+ log_err("setsockopt(..., SO_TIMESTAMPNS, ...) failed: %s",
+ strerror(errno));
+ return 0;
+ }
+ return 1;
+#else
+ log_err("packets timestamping is not supported on this platform");
+ (void)s;
+ return 0;
+#endif
+}
+
/** set fd to receive source address packet info */
static int
-set_recvpktinfo(int s, int family)
+set_recvpktinfo(int s, int family)
{
#if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO) || (defined(IP_RECVDSTADDR) && defined(IP_SENDSRCADDR)) || defined(IP_PKTINFO)
int on = 1;
* @param use_systemd: if true, fetch sockets from systemd.
* @param dnscrypt_port: dnscrypt service port number
* @param dscp: DSCP to use.
+ * @param sock_queue_timeout: the sock_queue_timeout from config. Seconds to
+ * wait to discard if UDP packets have waited for long in the socket
+ * buffer.
* @return: returns false on error.
*/
static int
struct config_strlist* tls_additional_port, int https_port,
struct config_strlist* proxy_protocol_port,
int* reuseport, int transparent, int tcp_mss, int freebind,
- int http2_nodelay, int use_systemd, int dnscrypt_port, int dscp)
+ int http2_nodelay, int use_systemd, int dnscrypt_port, int dscp,
+ int sock_queue_timeout)
{
int s, noip6=0;
int is_https = if_is_https(ifname, port, https_port);
if((s = make_sock_port(SOCK_DGRAM, ifname, port, hints, 1,
&noip6, rcv, snd, reuseport, transparent,
tcp_mss, nodelay, freebind, use_systemd, dscp, ub_sock)) == -1) {
- freeaddrinfo(ub_sock->addr);
+ if(ub_sock->addr)
+ freeaddrinfo(ub_sock->addr);
free(ub_sock);
if(noip6) {
log_warn("IPv6 protocol not available");
/* getting source addr packet info is highly non-portable */
if(!set_recvpktinfo(s, hints->ai_family)) {
sock_close(s);
- freeaddrinfo(ub_sock->addr);
+ if(ub_sock->addr)
+ freeaddrinfo(ub_sock->addr);
free(ub_sock);
return 0;
}
+ if (sock_queue_timeout && !set_recvtimestamp(s)) {
+ log_warn("socket timestamping is not available");
+ }
if(!port_insert(list, s, is_dnscrypt
?listen_type_udpancil_dnscrypt:listen_type_udpancil,
is_pp2, ub_sock)) {
sock_close(s);
- freeaddrinfo(ub_sock->addr);
+ if(ub_sock->addr)
+ freeaddrinfo(ub_sock->addr);
free(ub_sock);
return 0;
}
if((s = make_sock_port(SOCK_DGRAM, ifname, port, hints, 1,
&noip6, rcv, snd, reuseport, transparent,
tcp_mss, nodelay, freebind, use_systemd, dscp, ub_sock)) == -1) {
- freeaddrinfo(ub_sock->addr);
+ if(ub_sock->addr)
+ freeaddrinfo(ub_sock->addr);
free(ub_sock);
if(noip6) {
log_warn("IPv6 protocol not available");
}
return 0;
}
+ if (sock_queue_timeout && !set_recvtimestamp(s)) {
+ log_warn("socket timestamping is not available");
+ }
if(!port_insert(list, s, is_dnscrypt
?listen_type_udp_dnscrypt:listen_type_udp,
is_pp2, ub_sock)) {
sock_close(s);
- freeaddrinfo(ub_sock->addr);
+ if(ub_sock->addr)
+ freeaddrinfo(ub_sock->addr);
free(ub_sock);
return 0;
}
if((s = make_sock_port(SOCK_STREAM, ifname, port, hints, 1,
&noip6, 0, 0, reuseport, transparent, tcp_mss, nodelay,
freebind, use_systemd, dscp, ub_sock)) == -1) {
- freeaddrinfo(ub_sock->addr);
+ if(ub_sock->addr)
+ freeaddrinfo(ub_sock->addr);
free(ub_sock);
if(noip6) {
/*log_warn("IPv6 protocol not available");*/
verbose(VERB_ALGO, "setup TCP for SSL service");
if(!port_insert(list, s, port_type, is_pp2, ub_sock)) {
sock_close(s);
- freeaddrinfo(ub_sock->addr);
+ if(ub_sock->addr)
+ freeaddrinfo(ub_sock->addr);
free(ub_sock);
return 0;
}
return 1;
}
-/**
+/**
* Add items to commpoint list in front.
* @param c: commpoint to add.
* @param front: listen struct.
}
}
-struct listen_dnsport*
+struct listen_dnsport*
listen_create(struct comm_base* base, struct listen_port* ports,
size_t bufsize, int tcp_accept_count, int tcp_idle_timeout,
int harden_large_queries, uint32_t http_max_streams,
}
}
-void
+void
listen_delete(struct listen_dnsport* front)
{
- if(!front)
+ if(!front)
return;
listen_list_delete(front->cps);
#ifdef USE_DNSCRYPT
reuseport, cfg->ip_transparent,
cfg->tcp_mss, cfg->ip_freebind,
cfg->http_nodelay, cfg->use_systemd,
- cfg->dnscrypt_port, cfg->ip_dscp)) {
+ cfg->dnscrypt_port, cfg->ip_dscp, cfg->sock_queue_timeout)) {
listening_ports_free(list);
return NULL;
}
reuseport, cfg->ip_transparent,
cfg->tcp_mss, cfg->ip_freebind,
cfg->http_nodelay, cfg->use_systemd,
- cfg->dnscrypt_port, cfg->ip_dscp)) {
+ cfg->dnscrypt_port, cfg->ip_dscp, cfg->sock_queue_timeout)) {
listening_ports_free(list);
return NULL;
}
reuseport, cfg->ip_transparent,
cfg->tcp_mss, cfg->ip_freebind,
cfg->http_nodelay, cfg->use_systemd,
- cfg->dnscrypt_port, cfg->ip_dscp)) {
+ cfg->dnscrypt_port, cfg->ip_dscp, cfg->sock_queue_timeout)) {
listening_ports_free(list);
return NULL;
}
reuseport, cfg->ip_transparent,
cfg->tcp_mss, cfg->ip_freebind,
cfg->http_nodelay, cfg->use_systemd,
- cfg->dnscrypt_port, cfg->ip_dscp)) {
+ cfg->dnscrypt_port, cfg->ip_dscp, cfg->sock_queue_timeout)) {
listening_ports_free(list);
return NULL;
}
reuseport, cfg->ip_transparent,
cfg->tcp_mss, cfg->ip_freebind,
cfg->http_nodelay, cfg->use_systemd,
- cfg->dnscrypt_port, cfg->ip_dscp)) {
+ cfg->dnscrypt_port, cfg->ip_dscp, cfg->sock_queue_timeout)) {
listening_ports_free(list);
return NULL;
}
reuseport, cfg->ip_transparent,
cfg->tcp_mss, cfg->ip_freebind,
cfg->http_nodelay, cfg->use_systemd,
- cfg->dnscrypt_port, cfg->ip_dscp)) {
+ cfg->dnscrypt_port, cfg->ip_dscp, cfg->sock_queue_timeout)) {
listening_ports_free(list);
return NULL;
}
}
/* rc_ports don't have ub_socket */
if(list->socket) {
- freeaddrinfo(list->socket->addr);
+ if(list->socket->addr)
+ freeaddrinfo(list->socket->addr);
free(list->socket);
}
free(list);
size_t listen_get_mem(struct listen_dnsport* listen)
{
struct listen_list* p;
- size_t s = sizeof(*listen) + sizeof(*listen->base) +
- sizeof(*listen->udp_buff) +
+ size_t s = sizeof(*listen) + sizeof(*listen->base) +
+ sizeof(*listen->udp_buff) +
sldns_buffer_capacity(listen->udp_buff);
#ifdef USE_DNSCRYPT
s += sizeof(*listen->dnscrypt_udp_buff);
}
req->open_req_list = NULL;
req->num_open_req = 0;
-
+
/* free pending writable result packets */
item = req->done_req_list;
while(item) {
wr = 1;
if(!req->read_is_closed)
rd = 1;
-
+
if(wr) {
req->cp->tcp_is_reading = 0;
comm_point_stop_listening(req->cp);
}
req->in_worker_handle = 0;
/* it should be waiting in the mesh for recursion.
- * If mesh failed to add a new entry and called commpoint_drop_reply.
+ * If mesh failed to add a new entry and called commpoint_drop_reply.
* Then the mesh state has been cleared. */
if(req->is_drop) {
/* the reply has been dropped, stream has been closed. */
last = req->done_req_list;
while(last && last->next)
last = last->next;
-
+
/* create new element */
item = (struct tcp_req_done_item*)malloc(sizeof(*item));
if(!item) {
"buffer already assigned to stream");
return -1;
}
-
+
/* the c->buffer might be used by mesh_send_reply and no be cleard
* need to be cleared before use */
sldns_buffer_clear(h2_session->c->buffer);
else rep.ns_numrrsets = 1;
rep.rrset_count = 1;
rep.rrsets = &rrset;
+ rep.reason_bogus = LDNS_EDE_NONE;
udpsize = edns->udp_size;
edns->edns_version = EDNS_ADVERTISED_VERSION;
edns->udp_size = EDNS_ADVERTISED_SIZE;
struct local_data key;
struct local_data* ld = NULL;
struct local_rrset* lr = NULL;
- if(z->type == local_zone_always_transparent)
+ if(z->type == local_zone_always_transparent || z->type == local_zone_block_a)
return 1;
if(z->type != local_zone_transparent
&& z->type != local_zone_typetransparent
} else if(lz_type == local_zone_typetransparent
|| lz_type == local_zone_always_transparent) {
/* no NODATA or NXDOMAINS for this zone type */
+ return 0;
+ } else if(lz_type == local_zone_block_a) {
+ /* Return NODATA for all A queries */
+ if(qinfo->qtype == LDNS_RR_TYPE_A) {
+ local_error_encode(qinfo, env, edns, repinfo, buf, temp,
+ LDNS_RCODE_NOERROR, (LDNS_RCODE_NOERROR|BIT_AA),
+ LDNS_EDE_NONE, NULL);
+ return 1;
+ }
+
return 0;
} else if(lz_type == local_zone_always_null) {
/* 0.0.0.0 or ::0 or noerror/nodata for this zone type,
if(z && (lzt == local_zone_transparent ||
lzt == local_zone_typetransparent ||
lzt == local_zone_inform ||
- lzt == local_zone_always_transparent) &&
+ lzt == local_zone_always_transparent ||
+ lzt == local_zone_block_a) &&
local_zone_does_not_cover(z, qinfo, labs)) {
lock_rw_unlock(&z->lock);
z = NULL;
if(lzt != local_zone_always_refuse
&& lzt != local_zone_always_transparent
+ && lzt != local_zone_block_a
&& lzt != local_zone_always_nxdomain
&& lzt != local_zone_always_nodata
&& lzt != local_zone_always_deny
case local_zone_inform_deny: return "inform_deny";
case local_zone_inform_redirect: return "inform_redirect";
case local_zone_always_transparent: return "always_transparent";
+ case local_zone_block_a: return "block_a";
case local_zone_always_refuse: return "always_refuse";
case local_zone_always_nxdomain: return "always_nxdomain";
case local_zone_always_nodata: return "always_nodata";
*t = local_zone_inform_redirect;
else if(strcmp(type, "always_transparent") == 0)
*t = local_zone_always_transparent;
+ else if(strcmp(type, "block_a") == 0)
+ *t = local_zone_block_a;
else if(strcmp(type, "always_refuse") == 0)
*t = local_zone_always_refuse;
else if(strcmp(type, "always_nxdomain") == 0)
local_zone_inform_redirect,
/** resolve normally, even when there is local data */
local_zone_always_transparent,
+ /** resolve normally, even when there is local data but return NODATA for A queries */
+ local_zone_block_a,
/** answer with error, even when there is local data */
local_zone_always_refuse,
/** answer with nxdomain, even when there is local data */
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
* This software is open source.
- *
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
- *
+ *
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
- *
+ *
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
- *
+ *
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
- *
+ *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
#include "util/data/dname.h"
#include "respip/respip.h"
#include "services/listen_dnsport.h"
+#include "util/timeval_func.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)
-{
-#ifndef S_SPLINT_S
- time_t end_usec = end->tv_usec;
- d->tv_sec = end->tv_sec - start->tv_sec;
- if(end_usec < start->tv_usec) {
- end_usec += 1000000;
- d->tv_sec--;
- }
- d->tv_usec = end_usec - start->tv_usec;
-#endif
-}
-
-/** add timers and the values do not overflow or become negative */
-static void
-timeval_add(struct timeval* d, const struct timeval* add)
-{
-#ifndef S_SPLINT_S
- d->tv_sec += add->tv_sec;
- d->tv_usec += add->tv_usec;
- if(d->tv_usec >= 1000000 ) {
- d->tv_usec -= 1000000;
- d->tv_sec++;
- }
-#endif
-}
-
-/** divide sum of timers to get average */
-static void
-timeval_divide(struct timeval* avg, const struct timeval* sum, size_t d)
-{
-#ifndef S_SPLINT_S
- size_t leftover;
- if(d <= 0) {
- avg->tv_sec = 0;
- avg->tv_usec = 0;
- return;
- }
- avg->tv_sec = sum->tv_sec / d;
- avg->tv_usec = sum->tv_usec / d;
- /* handle fraction from seconds divide */
- leftover = sum->tv_sec - avg->tv_sec*d;
- if(leftover <= 0)
- leftover = 0;
- avg->tv_usec += (((long long)leftover)*((long long)1000000))/d;
- if(avg->tv_sec < 0)
- avg->tv_sec = 0;
- if(avg->tv_usec < 0)
- avg->tv_usec = 0;
-#endif
-}
-
-/** histogram compare of time values */
-static int
-timeval_smaller(const struct timeval* x, const struct timeval* y)
-{
-#ifndef S_SPLINT_S
- if(x->tv_sec < y->tv_sec)
- return 1;
- else if(x->tv_sec == y->tv_sec) {
- if(x->tv_usec <= y->tv_usec)
- return 1;
- else return 0;
- }
- else return 0;
-#endif
-}
-
/**
* Compare two response-ip client info entries for the purpose of mesh state
* compare. It returns 0 if ci_a and ci_b are considered equal; otherwise
return mesh_state_compare(a->s, b->s);
}
-struct mesh_area*
+struct mesh_area*
mesh_create(struct module_stack* stack, struct module_env* env)
{
struct mesh_area* mesh = calloc(1, sizeof(struct mesh_area));
mesh->stats_jostled = 0;
mesh->stats_dropped = 0;
mesh->ans_expired = 0;
+ mesh->ans_cachedb = 0;
mesh->max_reply_states = env->cfg->num_queries_per_thread;
mesh->max_forever_states = (mesh->max_reply_states+1)/2;
#ifndef S_SPLINT_S
* traversal and rbtree rebalancing do not work together */
}
-void
+void
mesh_delete(struct mesh_area* mesh)
{
if(!mesh)
if(m && m->reply_list && m->list_select == mesh_jostle_list) {
/* how old is it? */
struct timeval age;
- timeval_subtract(&age, mesh->env->now_tv,
+ timeval_subtract(&age, mesh->env->now_tv,
&m->reply_list->start_time);
if(timeval_smaller(&mesh->jostle_max, &age)) {
/* its a goner */
comm_point_send_reply(rep);
return;
}
+ /* set detached (it is now) */
+ mesh->num_detached_states++;
if(unique)
mesh_state_make_unique(s);
s->s.rpz_passthru = rpz_passthru;
s->s.edns_opts_front_in = edns_opt_copy_region(edns->opt_list_in,
s->s.region);
if(!s->s.edns_opts_front_in) {
- log_err("mesh_state_create: out of memory; SERVFAIL");
+ log_err("edns_opt_copy_region: out of memory; SERVFAIL");
if(!inplace_cb_reply_servfail_call(mesh->env, qinfo, NULL,
NULL, LDNS_RCODE_SERVFAIL, edns, rep, mesh->env->scratch, mesh->env->now_tv))
edns->opt_list_inplace_cb_out = NULL;
error_encode(r_buffer, LDNS_RCODE_SERVFAIL,
qinfo, qid, qflags, edns);
comm_point_send_reply(rep);
+ mesh_state_delete(&s->s);
return;
}
}
#endif
rbtree_insert(&mesh->all, &s->node);
log_assert(n != NULL);
- /* set detached (it is now) */
- mesh->num_detached_states++;
added = 1;
}
if(!s->reply_list && !s->cb_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_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;
}
return;
}
-int
+int
mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
- uint16_t qflags, struct edns_data* edns, sldns_buffer* buf,
+ uint16_t qflags, struct edns_data* edns, sldns_buffer* buf,
uint16_t qid, mesh_cb_func_type cb, void* cb_arg, int rpz_passthru)
{
struct mesh_state* s = NULL;
if(!s) {
return 0;
}
+ /* set detached (it is now) */
+ mesh->num_detached_states++;
if(unique)
mesh_state_make_unique(s);
s->s.rpz_passthru = rpz_passthru;
s->s.edns_opts_front_in = edns_opt_copy_region(edns->opt_list_in,
s->s.region);
if(!s->s.edns_opts_front_in) {
+ mesh_state_delete(&s->s);
return 0;
}
}
#endif
rbtree_insert(&mesh->all, &s->node);
log_assert(n != NULL);
- /* set detached (it is now) */
- mesh->num_detached_states++;
added = 1;
}
if(!s->reply_list && !s->cb_list) {
}
/* add serve expired timer if not already there */
if(timeout && !mesh_serve_expired_init(s, timeout)) {
+ if(added)
+ mesh_state_delete(&s->s);
return 0;
}
/* update statistics */
* 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)
+ int rpz_passthru, struct sockaddr_storage* addr, struct edns_option* edns_list)
{
struct mesh_state* s = NULL;
struct edns_option* opt = NULL;
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->client_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, s->s.region);
- if(!s->s.edns_opts_front_in) {
- log_err("prefetch_subnet subnet_ecs_opt_list_append: out of memory");
- return;
- }
+ /* Store the client's address. Later in the subnet module,
+ * it is decided whether to include an ECS option or not.
+ */
+ s->s.client_addr = *addr;
}
#ifdef UNBOUND_DEBUG
n =
void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo,
uint16_t qflags, time_t leeway, int rpz_passthru,
- struct comm_reply* rep, struct edns_option* opt_list)
+ struct sockaddr_storage* addr, struct edns_option* opt_list)
{
+ (void)addr;
(void)opt_list;
- (void)rep;
#ifdef CLIENT_SUBNET
- if(rep)
+ if(addr)
mesh_schedule_prefetch_subnet(mesh, qinfo, qflags, leeway, 1,
- rpz_passthru, rep, opt_list);
+ rpz_passthru, addr, opt_list);
else
#endif
mesh_schedule_prefetch(mesh, qinfo, qflags, leeway, 1,
int i;
if(!region)
return NULL;
- mstate = (struct mesh_state*)regional_alloc(region,
+ mstate = (struct mesh_state*)regional_alloc(region,
sizeof(struct mesh_state));
if(!mstate) {
alloc_reg_release(env->alloc, region);
return mstate;
}
-int
-mesh_state_is_unique(struct mesh_state* mstate)
-{
- return mstate->unique != NULL;
-}
-
void
mesh_state_make_unique(struct mesh_state* mstate)
{
mstate->unique = mstate;
}
-void
+void
mesh_state_cleanup(struct mesh_state* mstate)
{
struct mesh_area* mesh;
alloc_reg_release(mstate->s.env->alloc, mstate->s.region);
}
-void
+void
mesh_state_delete(struct module_qstate* qstate)
{
struct mesh_area* mesh;
mesh_detach_subs(&mstate->s);
if(mstate->list_select == mesh_forever_list) {
mesh->num_forever_states --;
- mesh_list_remove(mstate, &mesh->forever_first,
+ mesh_list_remove(mstate, &mesh->forever_first,
&mesh->forever_last);
} else if(mstate->list_select == mesh_jostle_list) {
- mesh_list_remove(mstate, &mesh->jostle_first,
+ mesh_list_remove(mstate, &mesh->jostle_first,
&mesh->jostle_last);
}
if(!mstate->reply_list && !mstate->cb_list
if(!ref->s->reply_list && !ref->s->cb_list
&& ref->s->super_set.count == 0) {
mesh->num_detached_states++;
- log_assert(mesh->num_detached_states +
+ log_assert(mesh->num_detached_states +
mesh->num_reply_states <= mesh->all.count);
}
}
if(!mesh_state_attachment(qstate->mesh_info, sub))
return 0;
/* if it was a duplicate attachment, the count was not zero before */
- if(!sub->reply_list && !sub->cb_list && was_detached &&
+ if(!sub->reply_list && !sub->cb_list && was_detached &&
sub->super_set.count == 1) {
/* it used to be detached, before this one got added */
log_assert(mesh->num_detached_states > 0);
else secure = 0;
if(!rep && rcode == LDNS_RCODE_NOERROR)
rcode = LDNS_RCODE_SERVFAIL;
- if(!rcode && (rep->security == sec_status_bogus ||
+ if(!rcode && rep && (rep->security == sec_status_bogus ||
rep->security == sec_status_secure_sentinel_fail)) {
if(!(reason = errinf_to_str_bogus(&m->s)))
rcode = LDNS_RCODE_SERVFAIL;
if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep,
LDNS_RCODE_NOERROR, &r->edns, NULL, m->s.region, start_time) ||
- !reply_info_answer_encode(&m->s.qinfo, rep, r->qid,
- r->qflags, r->buf, 0, 1,
- m->s.env->scratch, udp_size, &r->edns,
- (int)(r->edns.bits & EDNS_DO), secure))
+ !reply_info_answer_encode(&m->s.qinfo, rep, r->qid,
+ r->qflags, r->buf, 0, 1,
+ m->s.env->scratch, udp_size, &r->edns,
+ (int)(r->edns.bits & EDNS_DO), secure))
{
fptr_ok(fptr_whitelist_mesh_cb(r->cb));
(*r->cb)(r->cb_arg, LDNS_RCODE_SERVFAIL, r->buf,
} else {
fptr_ok(fptr_whitelist_mesh_cb(r->cb));
(*r->cb)(r->cb_arg, LDNS_RCODE_NOERROR, r->buf,
- rep->security, reason, was_ratelimited);
+ (rep?rep->security:sec_status_unchecked),
+ reason, was_ratelimited);
}
}
free(reason);
}
static inline int
-mesh_is_udp(struct mesh_reply const* r) {
+mesh_is_udp(struct mesh_reply const* r)
+{
return r->query_reply.c->type == comm_udp;
}
+static inline void
+mesh_find_and_attach_ede_and_reason(struct mesh_state* m,
+ struct reply_info* rep, struct mesh_reply* r)
+{
+ /* OLD note:
+ * 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
+ * NEW note:
+ * The compelling reason is that with caching support, the value
+ * in the reply_info is cached.
+ * The reason members of the reply_info struct should be
+ * updated as they are already cached. No reason to
+ * try and find the EDE information in errinf anymore.
+ */
+ if(rep->reason_bogus != LDNS_EDE_NONE) {
+ edns_opt_list_append_ede(&r->edns.opt_list_out,
+ m->s.region, rep->reason_bogus, rep->reason_bogus_str);
+ }
+}
+
/**
* Send reply to mesh reply entry
* @param m: mesh state to send it for.
/* examine security status */
if(m->s.env->need_to_validate && (!(r->qflags&BIT_CD) ||
- m->s.env->cfg->ignore_cd) && rep &&
+ m->s.env->cfg->ignore_cd) && rep &&
(rep->security <= sec_status_bogus ||
rep->security == sec_status_secure_sentinel_fail)) {
rcode = LDNS_RCODE_SERVFAIL;
if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s,
rep, rcode, &r->edns, &r->query_reply, m->s.region, &r->start_time))
r->edns.opt_list_inplace_cb_out = NULL;
- } else {
+ } else {
if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep, rcode,
&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);
+ /* Send along EDE EDNS0 option when SERVFAILing; usually
+ * DNSSEC validation failures */
+ /* Since we are SERVFAILing here, CD bit and rep->security
+ * is already handled. */
+ if(m->s.env->cfg->ede && rep) {
+ mesh_find_and_attach_ede_and_reason(m, rep, r);
}
error_encode(r_buffer, rcode, &m->s.qinfo, r->qid,
r->qflags, &r->edns);
r->edns.bits &= EDNS_DO;
m->s.qinfo.qname = r->qname;
m->s.qinfo.local_alias = r->local_alias;
+
+ /* Attach EDE without SERVFAIL if the validation failed.
+ * Need to explicitly check for rep->security otherwise failed
+ * validation paths may attach to a secure answer. */
+ if(m->s.env->cfg->ede && rep &&
+ (rep->security <= sec_status_bogus ||
+ rep->security == sec_status_secure_sentinel_fail)) {
+ mesh_find_and_attach_ede_and_reason(m, rep, r);
+ }
+
if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep,
LDNS_RCODE_NOERROR, &r->edns, &r->query_reply, m->s.region, &r->start_time) ||
- !reply_info_answer_encode(&m->s.qinfo, rep, r->qid,
+ !reply_info_answer_encode(&m->s.qinfo, rep, r->qid,
r->qflags, r_buffer, 0, 1, m->s.env->scratch,
udp_size, &r->edns, (int)(r->edns.bits & EDNS_DO),
- secure))
+ secure))
{
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))
struct reply_info* rep = (mstate->s.return_msg?
mstate->s.return_msg->rep:NULL);
struct timeval tv = {0, 0};
+ int i = 0;
/* No need for the serve expired timer anymore; we are going to reply. */
if(mstate->s.serve_expired_data) {
comm_timer_delete(mstate->s.serve_expired_data->timer);
}
}
for(r = mstate->reply_list; r; r = r->next) {
+ i++;
tv = r->start_time;
/* if a response-ip address block has been stored the
mstate->s.qinfo.qclass, r->local_alias,
&r->query_reply.client_addr,
r->query_reply.client_addrlen);
- if(mstate->s.env->cfg->stat_extended &&
- mstate->s.respip_action_info->rpz_used) {
- if(mstate->s.respip_action_info->rpz_disabled)
- mstate->s.env->mesh->rpz_action[RPZ_DISABLED_ACTION]++;
- if(mstate->s.respip_action_info->rpz_cname_override)
- mstate->s.env->mesh->rpz_action[RPZ_CNAME_OVERRIDE_ACTION]++;
- else
- mstate->s.env->mesh->rpz_action[respip_action_to_rpz_action(
- mstate->s.respip_action_info->action)]++;
- }
}
/* if this query is determined to be dropped during the
prev_buffer = r_buffer;
}
}
+ /* Account for each reply sent. */
+ if(i > 0 && mstate->s.respip_action_info &&
+ mstate->s.respip_action_info->addrinfo &&
+ mstate->s.env->cfg->stat_extended &&
+ mstate->s.respip_action_info->rpz_used) {
+ if(mstate->s.respip_action_info->rpz_disabled)
+ mstate->s.env->mesh->rpz_action[RPZ_DISABLED_ACTION] += i;
+ if(mstate->s.respip_action_info->rpz_cname_override)
+ mstate->s.env->mesh->rpz_action[RPZ_CNAME_OVERRIDE_ACTION] += i;
+ else
+ mstate->s.env->mesh->rpz_action[respip_action_to_rpz_action(
+ mstate->s.respip_action_info->action)] += i;
+ }
+ if(!mstate->s.is_drop && i > 0) {
+ if(mstate->s.env->cfg->stat_extended
+ && mstate->s.is_cachedb_answer) {
+ mstate->s.env->mesh->ans_cachedb += i;
+ }
+ }
+
+ /* Mesh area accounting */
if(mstate->reply_list) {
mstate->reply_list = NULL;
if(!mstate->reply_list && !mstate->cb_list) {
mstate->s.env->mesh->num_detached_states++;
}
mstate->replies_sent = 1;
+
while((c = mstate->cb_list) != NULL) {
/* take this cb off the list; so that the list can be
* changed, eg. by adds from the callback routine */
/* callback the function to inform super of result */
fptr_ok(fptr_whitelist_mod_inform_super(
mesh->mods.mod[ref->s->s.curmod]->inform_super));
- (*mesh->mods.mod[ref->s->s.curmod]->inform_super)(&mstate->s,
+ (*mesh->mods.mod[ref->s->s.curmod]->inform_super)(&mstate->s,
ref->s->s.curmod, &ref->s->s);
/* copy state that is always relevant to super */
copy_state_to_super(&mstate->s, ref->s->s.curmod, &ref->s->s);
* desire aggregation).*/
key.unique = NULL;
key.s.client_info = cinfo;
-
+
result = (struct mesh_state*)rbtree_search(&mesh->all, &key);
return result;
}
sldns_buffer* buf, mesh_cb_func_type cb, void* cb_arg,
uint16_t qid, uint16_t qflags)
{
- struct mesh_cb* r = regional_alloc(s->s.region,
+ struct mesh_cb* r = regional_alloc(s->s.region,
sizeof(struct mesh_cb));
if(!r)
return 0;
* Handles module finished.
* @param mesh: the mesh area.
* @param mstate: currently active mesh state.
- * Deleted if finished, calls _done and _supers to
+ * Deleted if finished, calls _done and _supers to
* send replies to clients and inform other mesh states.
* This in turn may create additional runnable mesh states.
* @param s: state at which the current module exited.
}
if(s == module_restart_next) {
int curmod = mstate->s.curmod;
- for(; mstate->s.curmod < mesh->mods.num;
+ for(; mstate->s.curmod < mesh->mods.num;
mstate->s.curmod++) {
fptr_ok(fptr_whitelist_mod_clear(
mesh->mods.mod[mstate->s.curmod]->clear));
if(s == module_finished) {
if(mstate->s.curmod == 0) {
struct query_info* qinfo = NULL;
+ struct edns_option* opt_list = NULL;
+ struct sockaddr_storage addr;
uint16_t qflags;
int rpz_p = 0;
+#ifdef CLIENT_SUBNET
+ struct edns_option* ecs;
+ if(mstate->s.need_refetch && mstate->reply_list &&
+ modstack_find(&mesh->mods, "subnetcache") != -1 &&
+ mstate->s.env->unique_mesh) {
+ addr = mstate->reply_list->query_reply.client_addr;
+ } else
+#endif
+ memset(&addr, 0, sizeof(addr));
+
mesh_query_done(mstate);
mesh_walk_supers(mesh, mstate);
* we need to make a copy of the query info here. */
if(mstate->s.need_refetch) {
mesh_copy_qinfo(mstate, &qinfo, &qflags);
+#ifdef CLIENT_SUBNET
+ /* Make also a copy of the ecs option if any */
+ if((ecs = edns_opt_list_find(
+ mstate->s.edns_opts_front_in,
+ mstate->s.env->cfg->client_subnet_opcode)) != NULL) {
+ (void)edns_opt_list_append(&opt_list,
+ ecs->opt_code, ecs->opt_len,
+ ecs->opt_data,
+ mstate->s.env->scratch);
+ }
+#endif
rpz_p = mstate->s.rpz_passthru;
}
- mesh_state_delete(&mstate->s);
if(qinfo) {
- mesh_schedule_prefetch(mesh, qinfo, qflags,
- 0, 1, rpz_p);
+ mesh_state_delete(&mstate->s);
+ mesh_new_prefetch(mesh, qinfo, qflags, 0,
+ rpz_p,
+ addr.ss_family!=AF_UNSPEC?&addr:NULL,
+ opt_list);
+ } else {
+ mesh_state_delete(&mstate->s);
}
return 0;
}
mstate->s.reply = NULL;
regional_free_all(mstate->s.env->scratch);
s = mstate->s.ext_state[mstate->s.curmod];
- verbose(VERB_ALGO, "mesh_run: %s module exit state is %s",
+ verbose(VERB_ALGO, "mesh_run: %s module exit state is %s",
mesh->mods.mod[mstate->s.curmod]->name, strextstate(s));
e = NULL;
if(mesh_continue(mesh, mstate, s, &ev))
}
}
-void
+void
mesh_log_list(struct mesh_area* mesh)
{
char buf[30];
struct mesh_state* m;
int num = 0;
RBTREE_FOR(m, struct mesh_state*, &mesh->all) {
- snprintf(buf, sizeof(buf), "%d%s%s%s%s%s%s mod%d %s%s",
+ snprintf(buf, sizeof(buf), "%d%s%s%s%s%s%s mod%d %s%s",
num++, (m->s.is_priming)?"p":"", /* prime */
(m->s.is_valrec)?"v":"", /* prime */
(m->s.query_flags&BIT_RD)?"RD":"",
(m->sub_set.count!=0)?"c":"", /* children */
m->s.curmod, (m->reply_list)?"rep":"", /*hasreply*/
(m->cb_list)?"cb":"" /* callbacks */
- );
+ );
log_query_info(VERB_ALGO, buf, &m->s.qinfo);
}
}
-void
+void
mesh_stats(struct mesh_area* mesh, const char* str)
{
verbose(VERB_DETAIL, "%s %u recursion states (%u with reply, "
"%u detached), %u waiting replies, %u recursion replies "
- "sent, %d replies dropped, %d states jostled out",
- str, (unsigned)mesh->all.count,
+ "sent, %d replies dropped, %d states jostled out",
+ str, (unsigned)mesh->all.count,
(unsigned)mesh->num_reply_states,
(unsigned)mesh->num_detached_states,
(unsigned)mesh->num_reply_addrs,
(unsigned)mesh->stats_jostled);
if(mesh->replies_sent > 0) {
struct timeval avg;
- timeval_divide(&avg, &mesh->replies_sum_wait,
+ timeval_divide(&avg, &mesh->replies_sum_wait,
mesh->replies_sent);
log_info("average recursion processing time "
ARG_LL "d.%6.6d sec",
}
}
-void
+void
mesh_stats_clear(struct mesh_area* mesh)
{
if(!mesh)
mesh->ans_secure = 0;
mesh->ans_bogus = 0;
mesh->ans_expired = 0;
+ mesh->ans_cachedb = 0;
memset(&mesh->ans_rcode[0], 0, sizeof(size_t)*UB_STATS_RCODE_NUM);
memset(&mesh->rpz_action[0], 0, sizeof(size_t)*UB_STATS_RPZ_ACTION_NUM);
mesh->ans_nodata = 0;
}
-size_t
+size_t
mesh_get_mem(struct mesh_area* mesh)
{
struct mesh_state* m;
return s;
}
-int
+int
mesh_detect_cycle(struct module_qstate* qstate, struct query_info* qinfo,
uint16_t flags, int prime, int valrec)
{
struct timeval tv = {0, 0};
int must_validate = (!(qstate->query_flags&BIT_CD)
|| qstate->env->cfg->ignore_cd) && qstate->env->need_to_validate;
+ int i = 0;
if(!qstate->serve_expired_data) return;
verbose(VERB_ALGO, "Serve expired: Trying to reply with expired data");
comm_timer_delete(qstate->serve_expired_data->timer);
log_dns_msg("Serve expired lookup", &qstate->qinfo, msg->rep);
for(r = mstate->reply_list; r; r = r->next) {
+ i++;
tv = r->start_time;
/* If address info is returned, it means the action should be an
qstate->qinfo.qtype, qstate->qinfo.qclass,
r->local_alias, &r->query_reply.client_addr,
r->query_reply.client_addrlen);
-
- if(qstate->env->cfg->stat_extended && actinfo.rpz_used) {
- if(actinfo.rpz_disabled)
- qstate->env->mesh->rpz_action[RPZ_DISABLED_ACTION]++;
- if(actinfo.rpz_cname_override)
- qstate->env->mesh->rpz_action[RPZ_CNAME_OVERRIDE_ACTION]++;
- else
- qstate->env->mesh->rpz_action[
- respip_action_to_rpz_action(actinfo.action)]++;
- }
}
/* Add EDE Stale Answer (RCF8914). Ignore global ede as this is
tcp_req_info_remove_mesh_state(r->query_reply.c->tcp_req_info, mstate);
prev = r;
prev_buffer = r_buffer;
-
- /* Account for each reply sent. */
- mesh->ans_expired++;
-
}
+ /* Account for each reply sent. */
+ if(i > 0) {
+ mesh->ans_expired += i;
+ if(actinfo.addrinfo && qstate->env->cfg->stat_extended &&
+ actinfo.rpz_used) {
+ if(actinfo.rpz_disabled)
+ qstate->env->mesh->rpz_action[RPZ_DISABLED_ACTION] += i;
+ if(actinfo.rpz_cname_override)
+ qstate->env->mesh->rpz_action[RPZ_CNAME_OVERRIDE_ACTION] += i;
+ else
+ qstate->env->mesh->rpz_action[
+ respip_action_to_rpz_action(actinfo.action)] += i;
+ }
+ }
+
+ /* Mesh area accounting */
if(mstate->reply_list) {
mstate->reply_list = NULL;
if(!mstate->reply_list && !mstate->cb_list) {
}
}
}
+
while((c = mstate->cb_list) != NULL) {
/* take this cb off the list; so that the list can be
* changed, eg. by adds from the callback routine */
size_t stats_dropped;
/** stats, number of expired replies sent */
size_t ans_expired;
+ /** stats, number of cached replies from cachedb */
+ size_t ans_cachedb;
/** number of replies sent */
size_t replies_sent;
/** sum of waiting times for the replies */
* @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 addr: sockaddr_storage for the client; to be used with subnet.
* @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, int rpz_passthru,
- struct comm_reply* rep, struct edns_option* opt_list);
+ struct sockaddr_storage* addr, struct edns_option* opt_list);
/**
* Handle new event from the wire. A serviced query has returned.
struct query_info* qinfo, struct respip_client_info* cinfo,
uint16_t qflags, int prime, int valrec);
-/**
- * Check if the mesh state is unique.
- * A unique mesh state uses it's unique member to point to itself, else NULL.
- * @param mstate: mesh state to check.
- * @return true if the mesh state is unique, false otherwise.
- */
-int mesh_state_is_unique(struct mesh_state* mstate);
-
/**
* Make a mesh state unique.
* A unique mesh state uses it's unique member to point to itself.
stack->mod[i] = module_factory(&module_conf);
if(!stack->mod[i]) {
char md[256];
+ char * s = md;
snprintf(md, sizeof(md), "%s", module_conf);
- if(strchr(md, ' ')) *(strchr(md, ' ')) = 0;
- if(strchr(md, '\t')) *(strchr(md, '\t')) = 0;
+ /* Leading spaces are present on errors. */
+ while (*s && isspace((unsigned char)*s))
+ s++;
+ if(strchr(s, ' ')) *(strchr(s, ' ')) = 0;
+ if(strchr(s, '\t')) *(strchr(s, '\t')) = 0;
log_err("Unknown value in module-config, module: '%s'."
" This module is not present (not compiled in),"
- " See the list of linked modules with unbound -V", md);
+ " See the list of linked modules with unbound -V", s);
return 0;
}
}
log_assert(&key_p != ((struct reuse_tcp*)result)->pending);
}
/* not found, return null */
+
+ /* It is possible that we search for something before the first element
+ * in the tree. Replace a null pointer with the first element.
+ */
+ if (!result) {
+ verbose(VERB_CLIENT, "reuse_tcp_find: taking first");
+ result = rbtree_first(&outnet->tcp_reuse);
+ }
+
if(!result || result == RBTREE_NULL)
return NULL;
+
+ /* It is possible that we got the previous address, but that the
+ * address we are looking for is in the tree. If the address we got
+ * is less than the address we are looking, then take the next entry.
+ */
+ if (reuse_cmp_addrportssl(result->key, &key_p.reuse) < 0) {
+ verbose(VERB_CLIENT, "reuse_tcp_find: key too low");
+ result = rbtree_next(result);
+ }
+
verbose(VERB_CLIENT, "reuse_tcp_find check inexact match");
/* inexact match, find one of possibly several connections to the
* same destination address, with the correct port, ssl, and
log_assert(w->addrlen > 0);
pend->c->tcp_do_toggle_rw = 0;
pend->c->tcp_do_close = 0;
+
+ /* Consistency check, if we have ssl_upstream but no sslctx, then
+ * log an error and return failure.
+ */
+ if (w->ssl_upstream && !w->outnet->sslctx) {
+ log_err("SSL upstream requested but no SSL context");
+ return 0;
+ }
+
/* open socket */
s = outnet_get_tcp_fd(&w->addr, w->addrlen, w->outnet->tcp_mss, w->outnet->ip_dscp);
/* TYPEXX representation */
if (strlen(name) > 4 && strncasecmp(name, "TYPE", 4) == 0) {
- return atoi(name + 4);
+ unsigned int a = atoi(name + 4);
+ if (a > LDNS_RR_TYPE_LAST) {
+ return (enum sldns_enum_rr_type)0;
+ }
+ return a;
}
/* Normal types */
/* CLASSXX representation */
if (strlen(name) > 5 && strncasecmp(name, "CLASS", 5) == 0) {
- return atoi(name + 5);
+ unsigned int a = atoi(name + 5);
+ if (a > LDNS_RR_CLASS_LAST) {
+ return (enum sldns_enum_rr_class)0;
+ }
+ return a;
}
/* Normal types */
LDNS_EDNS_DHU = 6, /* RFC6975 */
LDNS_EDNS_N3U = 7, /* RFC6975 */
LDNS_EDNS_CLIENT_SUBNET = 8, /* RFC7871 */
+ LDNS_EDNS_COOKIE = 10, /* RFC7873 */
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 */
+ LDNS_EDNS_CLIENT_TAG = 16, /* draft-bellis-dnsop-edns-tags-01 */
+ LDNS_EDNS_UNBOUND_CACHEDB_TESTFRAME_TEST = 65534
};
typedef enum sldns_enum_edns_option sldns_edns_option;
#define LDNS_TSIG_ERROR_BADNAME 20
#define LDNS_TSIG_ERROR_BADALG 21
+/** DNS Cookie extended rcode */
+#define LDNS_EXT_RCODE_BADCOOKIE 23
+
/**
* Contains all information about resource record types.
*
break;
default : break;
}
- return "\n\t ";
+ return "\n\t ";
}
/* Syntactic sugar for sldns_rr_new_frm_str_internal */
sldns_buffer_position(strbuf));
}
hex_data_size = (size_t)atoi(token);
- if(hex_data_size > LDNS_MAX_RDFLEN ||
+ if(hex_data_size > LDNS_MAX_RDFLEN ||
*rr_cur_len + hex_data_size > *rr_len) {
return RET_ERR(LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL,
sldns_buffer_position(strbuf));
/* check if not quoted yet, and we have encountered quotes */
if(!*quoted && sldns_rdf_type_maybe_quoted(rdftype) &&
slen >= 2 &&
- (token[0] == '"' || token[0] == '\'') &&
+ (token[0] == '"' || token[0] == '\'') &&
(token[slen-1] == '"' || token[slen-1] == '\'')) {
/* move token two smaller (quotes) with endnull */
memmove(token, token+1, slen-2);
mandatory = svcparams[i];
}
- /* 4. verify that all the SvcParamKeys in mandatory are present */
+ /* Verify that all the SvcParamKeys in mandatory are present */
if(mandatory) {
/* Divide by sizeof(uint16_t)*/
uint16_t mandatory_nkeys = sldns_read_uint16(mandatory + 2) / sizeof(uint16_t);
token[2]=='\t')) {
was_unknown_rr_format = 1;
if((status=rrinternal_parse_unknown(strbuf, token,
- token_len, rr, rr_len, &rr_cur_len,
+ token_len, rr, rr_len, &rr_cur_len,
pre_data_pos)) != 0)
return status;
} else if(token_strlen > 0 || quoted) {
if (rr_type == LDNS_RR_TYPE_SVCB || rr_type == LDNS_RR_TYPE_HTTPS) {
size_t rdata_len = rr_cur_len - dname_len - 10;
uint8_t *rdata = rr+dname_len + 10;
-
+
/* skip 1st rdata field SvcPriority (uint16_t) */
if (rdata_len < sizeof(uint16_t))
return LDNS_WIREPARSE_ERR_OK;
return key_value;
} else switch (key_len) {
- case sizeof("mandatory")-1:
- 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" */
+ case 3:
+ if (!strncmp(key, "ech", key_len))
+ return SVCB_KEY_ECH;
break;
- case sizeof("alpn")-1:
- if (!strncmp(key, "alpn", sizeof("alpn")-1))
+ case 4:
+ if (!strncmp(key, "alpn", key_len))
return SVCB_KEY_ALPN;
- if (!strncmp(key, "port", sizeof("port")-1))
+ if (!strncmp(key, "port", key_len))
return SVCB_KEY_PORT;
break;
- case sizeof("no-default-alpn")-1:
- if (!strncmp( key , "no-default-alpn"
- , sizeof("no-default-alpn")-1))
- return SVCB_KEY_NO_DEFAULT_ALPN;
+ case 7:
+ if (!strncmp(key, "dohpath", key_len))
+ return SVCB_KEY_DOHPATH;
break;
- case sizeof("ipv4hint")-1:
- if (!strncmp(key, "ipv4hint", sizeof("ipv4hint")-1))
+ case 8:
+ if (!strncmp(key, "ipv4hint", key_len))
return SVCB_KEY_IPV4HINT;
- if (!strncmp(key, "ipv6hint", sizeof("ipv6hint")-1))
+ if (!strncmp(key, "ipv6hint", key_len))
return SVCB_KEY_IPV6HINT;
break;
- case sizeof("ech")-1:
- if (!strncmp(key, "ech", sizeof("ech")-1))
- return SVCB_KEY_ECH;
+ case 9:
+ if (!strncmp(key, "mandatory", key_len))
+ return SVCB_KEY_MANDATORY;
+ if (!strncmp(key, "echconfig", key_len))
+ return SVCB_KEY_ECH; /* allow "echconfig" as well as "ech" */
+ break;
+
+ case 15:
+ if (!strncmp(key, "no-default-alpn", key_len))
+ return SVCB_KEY_NO_DEFAULT_ALPN;
break;
default:
size_t str_len;
size_t dst_len;
size_t val_len;
-
+
val_len = strlen(val);
if (val_len > sizeof(unescaped_dst)) {
sldns_write_uint16(rd + 2, dst_len);
memcpy(rd + 4, unescaped_dst, dst_len);
*rd_len = 4 + dst_len;
-
+
+ return LDNS_WIREPARSE_ERR_OK;
+}
+
+static int
+sldns_str2wire_svcbparam_dohpath_value(const char* val,
+ uint8_t* rd, size_t* rd_len)
+{
+ size_t val_len;
+
+ /* RFC6570#section-2.1
+ * "The characters outside of expressions in a URI Template string are
+ * intended to be copied literally"
+ * Practically this means we do not have to look for "double escapes"
+ * like in the alpn value list.
+ */
+
+ val_len = strlen(val);
+
+ if (*rd_len < 4 + val_len) {
+ return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
+ }
+
+ sldns_write_uint16(rd, SVCB_KEY_DOHPATH);
+ sldns_write_uint16(rd + 2, val_len);
+ memcpy(rd + 4, val, val_len);
+ *rd_len = 4 + val_len;
+
return LDNS_WIREPARSE_ERR_OK;
}
case SVCB_KEY_PORT:
case SVCB_KEY_IPV4HINT:
case SVCB_KEY_IPV6HINT:
+ case SVCB_KEY_DOHPATH:
return LDNS_WIREPARSE_ERR_SVCB_MISSING_PARAM;
#endif
default:
return sldns_str2wire_svcbparam_ech_value(val, rd, rd_len);
case SVCB_KEY_ALPN:
return sldns_str2wire_svcbparam_alpn_value(val, rd, rd_len);
+ case SVCB_KEY_DOHPATH:
+ return sldns_str2wire_svcbparam_dohpath_value(val, rd, rd_len);
default:
str_len = strlen(val);
if (*rd_len < 4 + str_len)
/* case: key=value */
if (eq_pos != NULL && eq_pos[1]) {
val_in = eq_pos + 1;
-
+
/* unescape characters and "" blocks */
if (*val_in == '"') {
val_in++;
}
*val_out = 0;
- return sldns_str2wire_svcparam_value(str, eq_pos - str,
- unescaped_val[0] ? unescaped_val : NULL, rd, rd_len);
+ return sldns_str2wire_svcparam_value(str, eq_pos - str,
+ unescaped_val[0] ? unescaped_val : NULL, rd, rd_len);
}
/* case: key= */
- else if (eq_pos != NULL && !(eq_pos[1])) {
+ else if (eq_pos != NULL && !(eq_pos[1])) {
return sldns_str2wire_svcparam_value(str, eq_pos - str, NULL, rd, rd_len);
}
/* case: key */
#define SVCB_KEY_IPV4HINT 4
#define SVCB_KEY_ECH 5
#define SVCB_KEY_IPV6HINT 6
-#define SVCPARAMKEY_COUNT 7
+#define SVCB_KEY_DOHPATH 7
+#define SVCPARAMKEY_COUNT 8
#define MAX_NUMBER_OF_SVCPARAMS 64
#define LDNS_WIREPARSE_ERR_SVCB_NO_DEFAULT_ALPN_VALUE 385
#define LDNS_WIREPARSE_ERR_SVCPARAM_BROKEN_RDATA 386
+
/**
* Get reference to a constant string for the (parse) error.
* @param e: error return value
"Mandatory SvcParamKey is missing"},
{ LDNS_WIREPARSE_ERR_SVCB_MANDATORY_DUPLICATE_KEY,
"Keys in SvcParam mandatory MUST be unique" },
- { LDNS_WIREPARSE_ERR_SVCB_MANDATORY_IN_MANDATORY,
+ { LDNS_WIREPARSE_ERR_SVCB_MANDATORY_IN_MANDATORY,
"mandatory MUST not be included as mandatory parameter" },
{ LDNS_WIREPARSE_ERR_SVCB_PORT_VALUE_SYNTAX,
"Could not parse port SvcParamValue" },
/* draft-ietf-dnsop-svcb-https-06: 6. Initial SvcParamKeys */
const char *svcparamkey_strs[] = {
"mandatory", "alpn", "no-default-alpn", "port",
- "ipv4hint", "ech", "ipv6hint"
+ "ipv4hint", "ech", "ipv6hint", "dohpath"
};
char* sldns_wire2str_pkt(uint8_t* data, size_t len)
uint8_t* rr = *d;
size_t rrlen = *dlen, dname_off, rdlen, ordlen;
uint16_t rrtype = 0;
-
+
if(*dlen >= 3 && (*d)[0]==0 &&
sldns_read_uint16((*d)+1)==LDNS_RR_TYPE_OPT) {
/* perform EDNS OPT processing */
w += sldns_str_print(s, slen, "%s", ",");
}
w += sldns_str_print(s, slen, "\"");
-
+
return w;
}
(*s) += size;
(*slen) -= size;
- w += sldns_str_print(s, slen, "\"");
+ w += sldns_str_print(s, slen, "\"");
return w + size;
}
/* verify that we have data_len data */
if (data_len > *dlen)
- return -1;
+ return -1;
written_chars += sldns_print_svcparamkey(s, slen, svcparamkey);
if (!data_len) {
case SVCB_KEY_IPV4HINT:
case SVCB_KEY_IPV6HINT:
case SVCB_KEY_MANDATORY:
+ case SVCB_KEY_DOHPATH:
return -1;
default:
return written_chars;
case SVCB_KEY_ECH:
r = sldns_wire2str_svcparam_ech2str(s, slen, data_len, *d);
break;
+ case SVCB_KEY_DOHPATH:
+ /* fallthrough */
default:
r = sldns_str_print(s, slen, "=\"");
}
if (r <= 0)
return -1; /* wireformat error */
-
+
written_chars += r;
*d += data_len;
*dlen -= data_len;
unsigned i, bit, window, block_len;
uint16_t t;
int w = 0;
-
+
/* check for errors */
while(pl) {
if(pl < 2) return -1;
/**
* Perform XML parsing of the root-anchors file
- * Its format description can be read here
- * https://data.iana.org/root-anchors/draft-icann-dnssec-trust-anchor.txt
+ * Its format description can be found in RFC 7958.
* It uses libexpat.
* @param xml: BIO with xml data.
* @param now: the current time for checking DS validity periods.
cfg->chrootdir, cfg);
}
#endif
- /* remove chroot setting so that modules are not stripping pathnames*/
+ /* remove chroot setting so that modules are not stripping pathnames */
free(cfg->chrootdir);
cfg->chrootdir = NULL;
* Copyright (c) 2008, NLnet Labs. All rights reserved.
*
* This software is open source.
- *
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
- *
+ *
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
- *
+ *
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
- *
+ *
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
- *
+ *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
#include "util/locks.h"
#include "util/net_help.h"
#include "util/shm_side/shm_main.h"
+#include "util/timeval_func.h"
#include "daemon/stats.h"
#include "sldns/wire2str.h"
#include "sldns/pkthdr.h"
printf(" stop stops the server\n");
printf(" reload reloads the server\n");
printf(" (this flushes data, stats, requestlist)\n");
+ printf(" reload_keep_cache reloads the server but tries to\n");
+ printf(" keep the RRset and message cache\n");
+ printf(" if (re)configuration allows for it.\n");
+ printf(" That means the caches sizes and\n");
+ printf(" the number of threads must not\n");
+ printf(" change between reloads.\n");
printf(" stats print statistics\n");
printf(" stats_noreset peek at statistics\n");
#ifdef HAVE_SHMGET
#ifdef HAVE_SHMGET
/** what to put on statistics lines between var and value, ": " or "=" */
#define SQ "="
-/** if true, inhibits a lot of =0 lines from the stats output */
-static const int inhibit_zero = 1;
-/** divide sum of timers to get average */
-static void
-timeval_divide(struct timeval* avg, const struct timeval* sum, long long d)
-{
-#ifndef S_SPLINT_S
- size_t leftover;
- if(d <= 0) {
- avg->tv_sec = 0;
- avg->tv_usec = 0;
- return;
- }
- avg->tv_sec = sum->tv_sec / d;
- avg->tv_usec = sum->tv_usec / d;
- /* handle fraction from seconds divide */
- leftover = sum->tv_sec - avg->tv_sec*d;
- if(leftover <= 0)
- leftover = 0;
- avg->tv_usec += (((long long)leftover)*((long long)1000000))/d;
- if(avg->tv_sec < 0)
- avg->tv_sec = 0;
- if(avg->tv_usec < 0)
- avg->tv_usec = 0;
-#endif
-}
-
/** print unsigned long stats value */
#define PR_UL_NM(str, var) printf("%s."str SQ"%lu\n", nm, (unsigned long)(var));
#define PR_UL(str, var) printf(str SQ"%lu\n", (unsigned long)(var));
{
struct timeval sumwait, avg;
PR_UL_NM("num.queries", s->svr.num_queries);
- PR_UL_NM("num.queries_ip_ratelimited",
+ PR_UL_NM("num.queries_ip_ratelimited",
s->svr.num_queries_ip_ratelimited);
+ PR_UL_NM("num.queries_cookie_valid",
+ s->svr.num_queries_cookie_valid);
+ PR_UL_NM("num.queries_cookie_client",
+ s->svr.num_queries_cookie_client);
+ PR_UL_NM("num.queries_cookie_invalid",
+ s->svr.num_queries_cookie_invalid);
PR_UL_NM("num.cachehits",
s->svr.num_queries - s->svr.num_queries_missed_cache);
PR_UL_NM("num.cachemiss", s->svr.num_queries_missed_cache);
PR_UL_NM("num.prefetch", s->svr.num_queries_prefetch);
+ PR_UL_NM("num.queries_timed_out", s->svr.num_queries_timed_out);
+ PR_UL_NM("query.queue_time_us.max", s->svr.max_query_time_us);
PR_UL_NM("num.expired", s->svr.ans_expired);
PR_UL_NM("num.recursivereplies", s->mesh_replies_sent);
#ifdef USE_DNSCRYPT
}
/** print extended */
-static void print_extended(struct ub_stats_info* s)
+static void print_extended(struct ub_stats_info* s, int inhibit_zero)
{
int i;
char nm[16];
PR_UL("rrset.cache.count", s->svr.rrset_cache_count);
PR_UL("infra.cache.count", s->svr.infra_cache_count);
PR_UL("key.cache.count", s->svr.key_cache_count);
+ /* max collisions */
+ PR_UL("msg.cache.max_collisions", s->svr.msg_cache_max_collisions);
+ PR_UL("rrset.cache.max_collisions", s->svr.rrset_cache_max_collisions);
/* applied RPZ actions */
for(i=0; i<UB_STATS_RPZ_ACTION_NUM; i++) {
if(i == RPZ_NO_OVERRIDE_ACTION)
PR_UL("num.query.subnet", s->svr.num_query_subnet);
PR_UL("num.query.subnet_cache", s->svr.num_query_subnet_cache);
#endif
+#ifdef USE_CACHEDB
+ PR_UL("num.query.cachedb", s->svr.num_query_cachedb);
+#endif
}
/** print statistics out of memory structures */
if(cfg->stat_extended) {
print_mem(shm_stat, &stats[0]);
print_hist(stats);
- print_extended(stats);
+ print_extended(stats, cfg->stat_inhibit_zero);
}
}
#endif /* HAVE_SHMGET */
fatal_exit("could not exec unbound: %s",
strerror(ENOSYS));
#else
- if(execlp("unbound", "unbound", "-c", cfgfile,
+ if(execlp("unbound", "unbound", "-c", cfgfile,
(char*)NULL) < 0) {
fatal_exit("could not exec unbound: %s",
strerror(errno));
case '?':
case 'h':
default:
+ ub_ctx_delete(ctx);
usage();
}
}
}
argc -= optind;
argv += optind;
- if(argc != 1)
+ if(argc != 1) {
+ ub_ctx_delete(ctx);
usage();
+ }
#ifdef HAVE_SSL
#ifdef HAVE_ERR_LOAD_CRYPTO_STRINGS
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
* This software is open source.
- *
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
- *
+ *
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
- *
+ *
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
- *
+ *
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
- *
+ *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
#include "util/regional.h"
#include "util/fptr_wlist.h"
#include "util/data/dname.h"
+#include "util/random.h"
#include "util/rtt.h"
#include "services/cache/infra.h"
#include "sldns/wire2str.h"
/** init ports possible for use */
static void init_outgoing_availports(int* array, int num);
-struct config_file*
+/** init cookie with random data */
+static void init_cookie_secret(uint8_t* cookie_secret, size_t cookie_secret_len);
+
+struct config_file*
config_create(void)
{
struct config_file* cfg;
cfg->stat_interval = 0;
cfg->stat_cumulative = 0;
cfg->stat_extended = 0;
+ cfg->stat_inhibit_zero = 1;
cfg->num_threads = 1;
cfg->port = UNBOUND_DNS_PORT;
cfg->do_ip4 = 1;
cfg->tcp_auth_query_timeout = 3 * 1000; /* 3s in millisecs */
cfg->do_tcp_keepalive = 0;
cfg->tcp_keepalive_timeout = 120 * 1000; /* 120s in millisecs */
+ cfg->sock_queue_timeout = 0; /* do not check timeout */
cfg->ssl_service_key = NULL;
cfg->ssl_service_pem = NULL;
cfg->ssl_port = UNBOUND_DNS_OVER_TLS_PORT;
cfg->outgoing_num_ports = 48; /* windows is limited in num fds */
cfg->num_queries_per_thread = 24;
cfg->outgoing_num_tcp = 2; /* leaves 64-52=12 for: 4if,1stop,thread4 */
- cfg->incoming_num_tcp = 2;
+ cfg->incoming_num_tcp = 2;
#endif
cfg->stream_wait_size = 4 * 1024 * 1024;
cfg->edns_buffer_size = 1232; /* from DNS flagday recommendation */
cfg->harden_below_nxdomain = 1;
cfg->harden_referral_path = 0;
cfg->harden_algo_downgrade = 0;
+ cfg->harden_unknown_additional = 0;
cfg->use_caps_bits_for_id = 0;
cfg->caps_whitelist = NULL;
cfg->private_address = NULL;
cfg->minimal_responses = 1;
cfg->rrset_roundrobin = 1;
cfg->unknown_server_time_limit = 376;
- cfg->max_udp_size = 4096;
- if(!(cfg->server_key_file = strdup(RUN_DIR"/unbound_server.key")))
+ cfg->max_udp_size = 1232; /* value taken from edns_buffer_size */
+ if(!(cfg->server_key_file = strdup(RUN_DIR"/unbound_server.key")))
goto error_exit;
- if(!(cfg->server_cert_file = strdup(RUN_DIR"/unbound_server.pem")))
+ if(!(cfg->server_cert_file = strdup(RUN_DIR"/unbound_server.pem")))
goto error_exit;
- if(!(cfg->control_key_file = strdup(RUN_DIR"/unbound_control.key")))
+ if(!(cfg->control_key_file = strdup(RUN_DIR"/unbound_control.key")))
goto error_exit;
- if(!(cfg->control_cert_file = strdup(RUN_DIR"/unbound_control.pem")))
+ if(!(cfg->control_cert_file = strdup(RUN_DIR"/unbound_control.pem")))
goto error_exit;
#ifdef CLIENT_SUBNET
#else
if(!(cfg->module_conf = strdup("validator iterator"))) goto error_exit;
#endif
- if(!(cfg->val_nsec3_key_iterations =
+ if(!(cfg->val_nsec3_key_iterations =
strdup("1024 150 2048 150 4096 150"))) goto error_exit;
#if defined(DNSTAP_SOCKET_PATH)
if(!(cfg->dnstap_socket_path = strdup(DNSTAP_SOCKET_PATH)))
cfg->dnstap_bidirectional = 1;
cfg->dnstap_tls = 1;
cfg->disable_dnssec_lame_check = 0;
+ cfg->ip_ratelimit_cookie = 0;
cfg->ip_ratelimit = 0;
cfg->ratelimit = 0;
cfg->ip_ratelimit_slabs = 4;
cfg->ip_ratelimit_backoff = 0;
cfg->ratelimit_backoff = 0;
cfg->outbound_msg_retry = 5;
+ cfg->max_sent_count = 32;
+ cfg->max_query_restarts = 11;
cfg->qname_minimisation = 1;
cfg->qname_minimisation_strict = 0;
cfg->shm_enable = 0;
cfg->ipsecmod_whitelist = NULL;
cfg->ipsecmod_strict = 0;
#endif
+ cfg->do_answer_cookie = 0;
+ memset(cfg->cookie_secret, 0, sizeof(cfg->cookie_secret));
+ cfg->cookie_secret_len = 16;
+ init_cookie_secret(cfg->cookie_secret, cfg->cookie_secret_len);
#ifdef USE_CACHEDB
if(!(cfg->cachedb_backend = strdup("testframe"))) goto error_exit;
if(!(cfg->cachedb_secret = strdup("default"))) goto error_exit;
#ifdef USE_REDIS
if(!(cfg->redis_server_host = strdup("127.0.0.1"))) goto error_exit;
+ cfg->redis_server_path = NULL;
+ cfg->redis_server_password = NULL;
cfg->redis_timeout = 100;
cfg->redis_server_port = 6379;
cfg->redis_expire_records = 0;
/* not supported, library must have 1 thread in bgworker */
return 0;
} else if(strcmp(opt, "outgoing-port-permit:") == 0) {
- return cfg_mark_ports(val, 1,
+ return cfg_mark_ports(val, 1,
cfg->outgoing_avail_ports, 65536);
} else if(strcmp(opt, "outgoing-port-avoid:") == 0) {
- return cfg_mark_ports(val, 0,
+ return cfg_mark_ports(val, 0,
cfg->outgoing_avail_ports, 65536);
} else if(strcmp(opt, "local-zone:") == 0) {
return cfg_parse_local_zone(cfg, val);
if(atoi(val) == 0) return 0;
cfg->val_date_override = (uint32_t)atoi(val);
}
- } else if(strcmp(opt, "local-data-ptr:") == 0) {
+ } else if(strcmp(opt, "local-data-ptr:") == 0) {
char* ptr = cfg_ptr_reverse((char*)opt);
return cfg_strlist_insert(&cfg->local_data, ptr);
} else if(strcmp(opt, "logfile:") == 0) {
else S_YNO("use-syslog:", use_syslog)
else S_STR("log-identity:", log_identity)
else S_YNO("extended-statistics:", stat_extended)
+ else S_YNO("statistics-inhibit-zero:", stat_inhibit_zero)
else S_YNO("statistics-cumulative:", stat_cumulative)
else S_YNO("shm-enable:", shm_enable)
else S_NUMBER_OR_ZERO("shm-key:", shm_key)
else S_NUMBER_NONZERO("tcp-reuse-timeout:", tcp_reuse_timeout)
else S_YNO("edns-tcp-keepalive:", do_tcp_keepalive)
else S_NUMBER_NONZERO("edns-tcp-keepalive-timeout:", tcp_keepalive_timeout)
+ else S_NUMBER_OR_ZERO("sock-queue-timeout:", sock_queue_timeout)
else S_YNO("ssl-upstream:", ssl_upstream)
else S_YNO("tls-upstream:", ssl_upstream)
else S_STR("ssl-service-key:", ssl_service_key)
else S_YNO("harden-below-nxdomain:", harden_below_nxdomain)
else S_YNO("harden-referral-path:", harden_referral_path)
else S_YNO("harden-algo-downgrade:", harden_algo_downgrade)
+ else S_YNO("harden-unknown-additional:", harden_unknown_additional)
else S_YNO("use-caps-for-id:", use_caps_bits_for_id)
else S_STRLIST("caps-whitelist:", caps_whitelist)
else S_SIZET_OR_ZERO("unwanted-reply-threshold:", unwanted_threshold)
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:", 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_POW2("dnscrypt-nonce-cache-slabs:",
dnscrypt_nonce_cache_slabs)
#endif
+ else if(strcmp(opt, "ip-ratelimit-cookie:") == 0) {
+ IS_NUMBER_OR_ZERO; cfg->ip_ratelimit_cookie = atoi(val);
+ infra_ip_ratelimit_cookie=cfg->ip_ratelimit_cookie;
+ }
else if(strcmp(opt, "ip-ratelimit:") == 0) {
IS_NUMBER_OR_ZERO; cfg->ip_ratelimit = atoi(val);
infra_ip_ratelimit=cfg->ip_ratelimit;
else S_YNO("ip-ratelimit-backoff:", ip_ratelimit_backoff)
else S_YNO("ratelimit-backoff:", ratelimit_backoff)
else S_NUMBER_NONZERO("outbound-msg-retry:", outbound_msg_retry)
+ else S_NUMBER_NONZERO("max-sent-count:", max_sent_count)
+ else S_NUMBER_NONZERO("max-query-restarts:", max_query_restarts)
else S_SIZET_NONZERO("fast-server-num:", fast_server_num)
else S_NUMBER_OR_ZERO("fast-server-permil:", fast_server_permil)
else S_YNO("qname-minimisation:", qname_minimisation)
{ IS_NUMBER_OR_ZERO; cfg->val_max_restart = (int32_t)atoi(val); }
else if (strcmp(opt, "outgoing-interface:") == 0) {
char* d = strdup(val);
- char** oi =
+ char** oi =
(char**)reallocarray(NULL, (size_t)cfg->num_out_ifs+1, sizeof(char*));
if(!d || !oi) { free(d); free(oi); return -1; }
if(cfg->out_ifs && cfg->num_out_ifs) {
for(s=list; s; s=s->next)
total += strlen(s->str) + 1; /* len + newline */
left = total+1; /* one extra for nul at end */
- r = malloc(left);
+ r = malloc(left);
if(!r)
return NULL;
w = r;
}
int
-config_get_option(struct config_file* cfg, const char* opt,
+config_get_option(struct config_file* cfg, const char* opt,
void (*func)(char*,void*), void* arg)
{
char buf[1024], nopt[64];
else O_DEC(opt, "statistics-interval", stat_interval)
else O_YNO(opt, "statistics-cumulative", stat_cumulative)
else O_YNO(opt, "extended-statistics", stat_extended)
+ else O_YNO(opt, "statistics-inhibit-zero", stat_inhibit_zero)
else O_YNO(opt, "shm-enable", shm_enable)
else O_DEC(opt, "shm-key", shm_key)
else O_YNO(opt, "use-syslog", use_syslog)
else O_DEC(opt, "tcp-reuse-timeout", tcp_reuse_timeout)
else O_YNO(opt, "edns-tcp-keepalive", do_tcp_keepalive)
else O_DEC(opt, "edns-tcp-keepalive-timeout", tcp_keepalive_timeout)
+ else O_DEC(opt, "sock-queue-timeout", sock_queue_timeout)
else O_YNO(opt, "ssl-upstream", ssl_upstream)
else O_YNO(opt, "tls-upstream", ssl_upstream)
else O_STR(opt, "ssl-service-key", ssl_service_key)
else O_YNO(opt, "harden-below-nxdomain", harden_below_nxdomain)
else O_YNO(opt, "harden-referral-path", harden_referral_path)
else O_YNO(opt, "harden-algo-downgrade", harden_algo_downgrade)
+ else O_YNO(opt, "harden-unknown-additional", harden_unknown_additional)
else O_YNO(opt, "use-caps-for-id", use_caps_bits_for_id)
else O_LST(opt, "caps-whitelist", caps_whitelist)
else O_DEC(opt, "unwanted-reply-threshold", unwanted_threshold)
else O_LST(opt, "python-script", python_script)
else O_LST(opt, "dynlib-file", dynlib_file)
else O_YNO(opt, "disable-dnssec-lame-check", disable_dnssec_lame_check)
+ else O_DEC(opt, "ip-ratelimit-cookie", ip_ratelimit_cookie)
else O_DEC(opt, "ip-ratelimit", ip_ratelimit)
else O_DEC(opt, "ratelimit", ratelimit)
else O_MEM(opt, "ip-ratelimit-size", ip_ratelimit_size)
else O_YNO(opt, "ip-ratelimit-backoff", ip_ratelimit_backoff)
else O_YNO(opt, "ratelimit-backoff", ratelimit_backoff)
else O_UNS(opt, "outbound-msg-retry", outbound_msg_retry)
+ else O_UNS(opt, "max-sent-count", max_sent_count)
+ else O_UNS(opt, "max-query-restarts", max_query_restarts)
else O_DEC(opt, "fast-server-num", fast_server_num)
else O_DEC(opt, "fast-server-permil", fast_server_permil)
else O_DEC(opt, "val-sig-skew-min", val_sig_skew_min)
#ifdef USE_REDIS
else O_STR(opt, "redis-server-host", redis_server_host)
else O_DEC(opt, "redis-server-port", redis_server_port)
+ else O_STR(opt, "redis-server-path", redis_server_path)
+ else O_STR(opt, "redis-server-password", redis_server_password)
else O_DEC(opt, "redis-timeout", redis_timeout)
else O_YNO(opt, "redis-expire-records", redis_expire_records)
#endif /* USE_REDIS */
init_cfg_parse();
}
-int
+int
config_read(struct config_file* cfg, const char* filename, const char* chroot)
{
FILE *in;
if(r == GLOB_NOMATCH) {
verbose(VERB_QUERY, "include: "
"no matches for %s", fname);
- return 1;
+ return 1;
} else if(r == GLOB_NOSPACE) {
log_err("include: %s: "
"fnametern out of memory", fname);
}
}
-void
+void
config_delete(struct config_file* cfg)
{
if(!cfg) return;
free(cfg->server_cert_file);
free(cfg->control_key_file);
free(cfg->control_cert_file);
+ free(cfg->nat64_prefix);
free(cfg->dns64_prefix);
config_delstrlist(cfg->dns64_ignore_aaaa);
free(cfg->dnstap_socket_path);
free(cfg->cachedb_secret);
#ifdef USE_REDIS
free(cfg->redis_server_host);
+ free(cfg->redis_server_path);
+ free(cfg->redis_server_password);
#endif /* USE_REDIS */
#endif /* USE_CACHEDB */
#ifdef USE_IPSET
free(cfg);
}
-static void
+static void
+init_cookie_secret(uint8_t* cookie_secret, size_t cookie_secret_len)
+{
+ struct ub_randstate *rand = ub_initstate(NULL);
+
+ if (!rand)
+ fatal_exit("could not init random generator");
+ while (cookie_secret_len) {
+ *cookie_secret++ = (uint8_t)ub_random(rand);
+ cookie_secret_len--;
+ }
+ ub_randfree(rand);
+}
+
+static void
init_outgoing_availports(int* a, int num)
{
/* generated with make iana_update */
for(i=1024; i<num; i++) {
a[i] = i;
}
- /* create empty spot at 49152 to keep ephemeral ports available
+ /* create empty spot at 49152 to keep ephemeral ports available
* to other programs */
for(i=49152; i<49152+256; i++)
a[i] = 0;
}
}
-int
+int
cfg_mark_ports(const char* str, int allow, int* avail, int num)
{
char* mid = strchr(str, '-');
return 1;
}
-int
+int
cfg_scan_ports(int* avail, int num)
{
int i;
return 1;
}
-int
+int
cfg_region_strlist_insert(struct regional* region,
struct config_strlist** head, char* item)
{
return NULL;
}
-int
+int
cfg_strlist_insert(struct config_strlist** head, char* item)
{
struct config_strlist *s;
return 0;
s->str = item;
s->next = NULL;
-
+
if (*head==NULL) {
*head = s;
} else {
}
last->next = s;
}
-
- return 1;
+
+ return 1;
}
-int
+int
cfg_str2list_insert(struct config_str2list** head, char* item, char* i2)
{
struct config_str2list *s;
return 1;
}
-int
+int
cfg_str3list_insert(struct config_str3list** head, char* item, char* i2,
char* i3)
{
return 1;
}
-time_t
+time_t
cfg_convert_timeval(const char* str)
{
time_t t;
memset(&tm, 0, sizeof(tm));
if(strlen(str) < 14)
return 0;
- if(sscanf(str, "%4d%2d%2d%2d%2d%2d", &tm.tm_year, &tm.tm_mon,
+ if(sscanf(str, "%4d%2d%2d%2d%2d%2d", &tm.tm_year, &tm.tm_mon,
&tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6)
return 0;
tm.tm_year -= 1900;
return t;
}
-int
+int
cfg_count_numbers(const char* s)
{
/* format ::= (sp num)+ sp */
return 1;
}
-int
+int
cfg_parse_memsize(const char* str, size_t* res)
{
size_t len;
/* check appended num */
while(len>0 && str[len-1]==' ')
len--;
- if(len > 1 && str[len-1] == 'b')
+ if(len > 1 && str[len-1] == 'b')
len--;
- else if(len > 1 && str[len-1] == 'B')
+ else if(len > 1 && str[len-1] == 'B')
len--;
-
+
if(len > 1 && tolower((unsigned char)str[len-1]) == 'g')
mult = 1024*1024*1024;
else if(len > 1 && tolower((unsigned char)str[len-1]) == 'm')
log_err("out of memory");
return 0;
}
-
+
/* parse */
s = str;
while((p=strsep(&s, " \t\n")) != NULL) {
return 0;
}
-void
+void
config_apply(struct config_file* config)
{
MAX_TTL = (time_t)config->max_ttl;
#endif
}
-/**
+/**
* Calculate string length of full pathname in original filesys
* @param fname: the path name to convert.
* Must not be null or empty.
{
size_t len = 0;
int slashit = 0;
- if(cfg->chrootdir && cfg->chrootdir[0] &&
+ if(cfg->chrootdir && cfg->chrootdir[0] &&
strncmp(cfg->chrootdir, fname, strlen(cfg->chrootdir)) == 0) {
/* already full pathname, return it */
return strlen(fname);
/* prepend chdir */
if(slashit && cfg->directory[0] != '/')
len++;
- if(cfg->chrootdir && cfg->chrootdir[0] &&
- strncmp(cfg->chrootdir, cfg->directory,
+ if(cfg->chrootdir && cfg->chrootdir[0] &&
+ strncmp(cfg->chrootdir, cfg->directory,
strlen(cfg->chrootdir)) == 0)
len += strlen(cfg->directory)-strlen(cfg->chrootdir);
else len += strlen(cfg->directory);
return NULL;
buf[0] = 0;
/* is fname already in chroot ? */
- if(cfg->chrootdir && cfg->chrootdir[0] &&
+ if(cfg->chrootdir && cfg->chrootdir[0] &&
strncmp(cfg->chrootdir, fname, strlen(cfg->chrootdir)) == 0) {
/* already full pathname, return it */
(void)strlcpy(buf, fname, len);
if(slashit && cfg->directory[0] != '/')
(void)strlcat(buf, "/", len);
/* is the directory already in the chroot? */
- if(cfg->chrootdir && cfg->chrootdir[0] &&
- strncmp(cfg->chrootdir, cfg->directory,
+ if(cfg->chrootdir && cfg->chrootdir[0] &&
+ strncmp(cfg->chrootdir, cfg->directory,
strlen(cfg->chrootdir)) == 0)
- (void)strlcat(buf, cfg->directory+strlen(cfg->chrootdir),
+ (void)strlcat(buf, cfg->directory+strlen(cfg->chrootdir),
len);
else (void)strlcat(buf, cfg->directory, len);
slashit = 1;
return (sp>tab)?sp:tab;
}
-int
+int
cfg_parse_local_zone(struct config_file* cfg, const char* val)
{
const char *type, *name_end, *name;
}
if(strcmp(type, "nodefault")==0) {
- return cfg_strlist_insert(&cfg->local_zones_nodefault,
+ return cfg_strlist_insert(&cfg->local_zones_nodefault,
strdup(name));
#ifdef USE_IPSET
} else if(strcmp(type, "ipset")==0) {
- return cfg_strlist_insert(&cfg->local_zones_ipset,
+ return cfg_strlist_insert(&cfg->local_zones_ipset,
strdup(name));
#endif
} else {
const char* hex = "0123456789abcdef";
char *p = buf;
int i;
- memmove(ad, &((struct sockaddr_in6*)&addr)->sin6_addr,
+ memmove(ad, &((struct sockaddr_in6*)&addr)->sin6_addr,
sizeof(ad));
for(i=15; i>=0; i--) {
uint8_t b = ad[i];
snprintf(buf+16*4, sizeof(buf)-16*4, "ip6.arpa. ");
} else {
uint8_t ad[4];
- memmove(ad, &((struct sockaddr_in*)&addr)->sin_addr,
+ memmove(ad, &((struct sockaddr_in*)&addr)->sin_addr,
sizeof(ad));
snprintf(buf, sizeof(buf), "%u.%u.%u.%u.in-addr.arpa. ",
(unsigned)ad[3], (unsigned)ad[2],
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
* This software is open source.
- *
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
- *
+ *
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
- *
+ *
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
- *
+ *
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
- *
+ *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
int stat_cumulative;
/** if true, the statistics are kept in greater detail */
int stat_extended;
+ /** if true, inhibits a lot of =0 lines from the extended stats output */
+ int stat_inhibit_zero;
/** number of threads to create */
int num_threads;
int do_ip4;
/** do ip6 query support. */
int do_ip6;
+ /** do nat64 on queries */
+ int do_nat64;
/** prefer ip4 upstream queries. */
int prefer_ip4;
/** prefer ip6 upstream queries. */
int do_tcp_keepalive;
/** tcp keepalive timeout, in msec */
int tcp_keepalive_timeout;
+ /** timeout of packets sitting in the socket queue */
+ int sock_queue_timeout;
/** proxy protocol ports */
struct config_strlist* proxy_protocol_port;
/** interface description strings (IP addresses) */
char **ifs;
- /** number of outgoing interfaces to open.
+ /** number of outgoing interfaces to open.
* If 0 default all interfaces. */
int num_out_ifs;
/** outgoing interface description strings (IP addresses) */
/** list of donotquery addresses, linked list */
struct config_strlist* donotqueryaddrs;
#ifdef CLIENT_SUBNET
- /** list of servers we send edns-client-subnet option to and
+ /** list of servers we send edns-client-subnet option to and
* accept option from, linked list */
struct config_strlist* client_subnet;
/** list of zones we send edns-client-subnet option for */
int harden_referral_path;
/** harden against algorithm downgrade */
int harden_algo_downgrade;
+ /** harden against unknown records in the authority section and in
+ * the additional section */
+ int harden_unknown_additional;
/** use 0x20 bits in query as random ID bits */
int use_caps_bits_for_id;
/** 0x20 whitelist, domains that do not use capsforid */
/** the module configuration string */
char* module_conf;
-
+
/** files with trusted DS and DNSKEYs in zonefile format, list */
struct config_strlist* trust_anchor_file_list;
/** list of trustanchor keys, linked list */
/** max number of query restarts, number of IPs to probe */
int32_t val_max_restart;
/** this value sets the number of seconds before revalidating bogus */
- int bogus_ttl;
+ int bogus_ttl;
/** should validator clean additional section for secure msgs */
int val_clean_additional;
/** log bogus messages by the validator */
/** ignore AAAAs for these domain names and use A record anyway */
struct config_strlist* dns64_ignore_aaaa;
+ /* NAT64 prefix; if unset defaults to dns64_prefix */
+ char* nat64_prefix;
+
/** true to enable dnstap support */
int dnstap;
/** using bidirectional frame streams if true */
/** ratelimit for ip addresses. 0 is off, otherwise qps (unless overridden) */
int ip_ratelimit;
+ /** ratelimit for ip addresses with a valid DNS Cookie. 0 is off,
+ * otherwise qps (unless overridden) */
+ int ip_ratelimit_cookie;
/** number of slabs for ip_ratelimit cache */
size_t ip_ratelimit_slabs;
/** memory size in bytes for ip_ratelimit cache */
/** number of retries on outgoing queries */
int outbound_msg_retry;
+ /** max sent queries per qstate; resets on query restarts (e.g.,
+ * CNAMES) and referrals */
+ int max_sent_count;
+ /** max number of query restarts; determines max length of CNAME chain */
+ int max_query_restarts;
/** minimise outgoing QNAME and hide original QTYPE if possible */
int qname_minimisation;
/** minimise QNAME in strict mode, minimise according to RFC.
char* redis_server_host;
/** redis server's TCP port */
int redis_server_port;
+ /** redis server's unix path. Or "", NULL if unused */
+ char* redis_server_path;
+ /** redis server's AUTH password. Or "", NULL if unused */
+ char* redis_server_password;
/** timeout (in ms) for communication with the redis server */
int redis_timeout;
/** set timeout on redis records based on DNS response ttl */
int redis_expire_records;
#endif
#endif
+ /** Downstream DNS Cookies */
+ /** do answer with server cookie when request contained cookie option */
+ int do_answer_cookie;
+ /** cookie secret */
+ uint8_t cookie_secret[40];
+ /** cookie secret length */
+ size_t cookie_secret_len;
/* ipset module */
#ifdef USE_IPSET
struct config_strlist* local_zones_ipset;
#endif
/** Fallback to global local_zones when there is no match in the view
- * view specific tree. 1 for yes, 0 for no */
+ * view specific tree. 1 for yes, 0 for no */
int isfirst;
/** predefined actions for particular IP address responses */
struct config_str2list* respip_actions;
* @param config: where options are stored into, must be freshly created.
* @param filename: name of configfile. If NULL nothing is done.
* @param chroot: if not NULL, the chroot dir currently in use (for include).
- * @return: false on error. In that case errno is set, ENOENT means
+ * @return: false on error. In that case errno is set, ENOENT means
* file not found.
*/
int config_read(struct config_file* config, const char* filename,
int config_set_option(struct config_file* config, const char* option,
const char* value);
-/**
+/**
* Call print routine for the given option.
* @param cfg: config.
- * @param opt: option name without trailing :.
+ * @param opt: option name without trailing :.
* This is different from config_set_option.
* @param func: print func, called as (str, arg) for every data element.
* @param arg: user argument for print func.
* @return false if the option name is not supported (syntax error).
*/
-int config_get_option(struct config_file* cfg, const char* opt,
+int config_get_option(struct config_file* cfg, const char* opt,
void (*func)(char*,void*), void* arg);
/**
* @param str: string. malloced, caller must free it.
* @return 0=OK, 1=syntax error, 2=malloc failed.
*/
-int config_get_option_collate(struct config_file* cfg, const char* opt,
+int config_get_option_collate(struct config_file* cfg, const char* opt,
char** str);
/**
* k=1024, m=1024*1024, g=1024*1024*1024.
* @param str: string
* @param res: result is stored here, size in bytes.
- * @return: true if parsed correctly, or 0 on a parse error (and an error
+ * @return: true if parsed correctly, or 0 on a parse error (and an error
* is logged).
*/
int cfg_parse_memsize(const char* str, size_t* res);
/**
* parse taglist from string into bytestring with bitlist.
* @param cfg: the config structure (with tagnames)
- * @param str: the string to parse. Parse puts 0 bytes in string.
+ * @param str: the string to parse. Parse puts 0 bytes in string.
* @param listlen: returns length of in bytes.
* @return malloced bytes with a bitlist of the tags. or NULL on parse error
* or malloc failure.
* @param allow: give true if this range is permitted.
* @param avail: the array from cfg.
* @param num: size of the array (65536).
- * @return: true if parsed correctly, or 0 on a parse error (and an error
+ * @return: true if parsed correctly, or 0 on a parse error (and an error
* is logged).
*/
int cfg_mark_ports(const char* str, int allow, int* avail, int num);
*/
int cfg_scan_ports(int* avail, int num);
-/**
+/**
* Convert a filename to full pathname in original filesys
* @param fname: the path name to convert.
* Must not be null or empty.
* @return pointer to malloced buffer which is: [chroot][chdir]fname
* or NULL on malloc failure.
*/
-char* fname_after_chroot(const char* fname, struct config_file* cfg,
+char* fname_after_chroot(const char* fname, struct config_file* cfg,
int use_chdir);
/**
#endif
#endif /* UTIL_CONFIG_FILE_H */
-
%x quotedstring singlequotedstr include include_quoted val include_toplevel include_toplevel_quoted
%%
-<INITIAL,val>{SPACE}* {
+<INITIAL,val>{SPACE}* {
LEXOUT(("SP ")); /* ignore */ }
-<INITIAL,val>{SPACE}*{COMMENT}.* {
+<INITIAL,val>{SPACE}*{COMMENT}.* {
/* note that flex makes the longest match and '.' is any but not nl */
LEXOUT(("comment(%s) ", yytext)); /* ignore */ }
server{COLON} { YDVAR(0, VAR_SERVER) }
incoming-num-tcp{COLON} { YDVAR(1, VAR_INCOMING_NUM_TCP) }
do-ip4{COLON} { YDVAR(1, VAR_DO_IP4) }
do-ip6{COLON} { YDVAR(1, VAR_DO_IP6) }
+do-nat64{COLON} { YDVAR(1, VAR_DO_NAT64) }
prefer-ip4{COLON} { YDVAR(1, VAR_PREFER_IP4) }
prefer-ip6{COLON} { YDVAR(1, VAR_PREFER_IP6) }
do-udp{COLON} { YDVAR(1, VAR_DO_UDP) }
tcp-auth-query-timeout{COLON} { YDVAR(1, VAR_TCP_AUTH_QUERY_TIMEOUT) }
edns-tcp-keepalive{COLON} { YDVAR(1, VAR_EDNS_TCP_KEEPALIVE) }
edns-tcp-keepalive-timeout{COLON} { YDVAR(1, VAR_EDNS_TCP_KEEPALIVE_TIMEOUT) }
+sock-queue-timeout{COLON} { YDVAR(1, VAR_SOCK_QUEUE_TIMEOUT) }
ssl-upstream{COLON} { YDVAR(1, VAR_SSL_UPSTREAM) }
tls-upstream{COLON} { YDVAR(1, VAR_SSL_UPSTREAM) }
ssl-service-key{COLON} { YDVAR(1, VAR_SSL_SERVICE_KEY) }
harden-below-nxdomain{COLON} { YDVAR(1, VAR_HARDEN_BELOW_NXDOMAIN) }
harden-referral-path{COLON} { YDVAR(1, VAR_HARDEN_REFERRAL_PATH) }
harden-algo-downgrade{COLON} { YDVAR(1, VAR_HARDEN_ALGO_DOWNGRADE) }
+harden-unknown-additional{COLON} { YDVAR(1, VAR_HARDEN_UNKNOWN_ADDITIONAL) }
use-caps-for-id{COLON} { YDVAR(1, VAR_USE_CAPS_FOR_ID) }
caps-whitelist{COLON} { YDVAR(1, VAR_CAPS_WHITELIST) }
caps-exempt{COLON} { YDVAR(1, VAR_CAPS_WHITELIST) }
key-cache-size{COLON} { YDVAR(1, VAR_KEY_CACHE_SIZE) }
key-cache-slabs{COLON} { YDVAR(1, VAR_KEY_CACHE_SLABS) }
neg-cache-size{COLON} { YDVAR(1, VAR_NEG_CACHE_SIZE) }
-val-nsec3-keysize-iterations{COLON} {
+val-nsec3-keysize-iterations{COLON} {
YDVAR(1, VAR_VAL_NSEC3_KEYSIZE_ITERATIONS) }
zonemd-permissive-mode{COLON} { YDVAR(1, VAR_ZONEMD_PERMISSIVE_MODE) }
zonemd-check{COLON} { YDVAR(1, VAR_ZONEMD_CHECK) }
statistics-interval{COLON} { YDVAR(1, VAR_STATISTICS_INTERVAL) }
statistics-cumulative{COLON} { YDVAR(1, VAR_STATISTICS_CUMULATIVE) }
extended-statistics{COLON} { YDVAR(1, VAR_EXTENDED_STATISTICS) }
+statistics-inhibit-zero{COLON} { YDVAR(1, VAR_STATISTICS_INHIBIT_ZERO) }
shm-enable{COLON} { YDVAR(1, VAR_SHM_ENABLE) }
shm-key{COLON} { YDVAR(1, VAR_SHM_KEY) }
remote-control{COLON} { YDVAR(0, VAR_REMOTE_CONTROL) }
dns64-prefix{COLON} { YDVAR(1, VAR_DNS64_PREFIX) }
dns64-synthall{COLON} { YDVAR(1, VAR_DNS64_SYNTHALL) }
dns64-ignore-aaaa{COLON} { YDVAR(1, VAR_DNS64_IGNORE_AAAA) }
+nat64-prefix{COLON} { YDVAR(1, VAR_NAT64_PREFIX) }
define-tag{COLON} { YDVAR(1, VAR_DEFINE_TAG) }
local-zone-tag{COLON} { YDVAR(2, VAR_LOCAL_ZONE_TAG) }
access-control-tag{COLON} { YDVAR(2, VAR_ACCESS_CONTROL_TAG) }
YDVAR(1, VAR_DNSTAP_LOG_FORWARDER_RESPONSE_MESSAGES) }
disable-dnssec-lame-check{COLON} { YDVAR(1, VAR_DISABLE_DNSSEC_LAME_CHECK) }
ip-ratelimit{COLON} { YDVAR(1, VAR_IP_RATELIMIT) }
+ip-ratelimit-cookie{COLON} { YDVAR(1, VAR_IP_RATELIMIT_COOKIE) }
ratelimit{COLON} { YDVAR(1, VAR_RATELIMIT) }
ip-ratelimit-slabs{COLON} { YDVAR(1, VAR_IP_RATELIMIT_SLABS) }
ratelimit-slabs{COLON} { YDVAR(1, VAR_RATELIMIT_SLABS) }
ip-ratelimit-backoff{COLON} { YDVAR(1, VAR_IP_RATELIMIT_BACKOFF) }
ratelimit-backoff{COLON} { YDVAR(1, VAR_RATELIMIT_BACKOFF) }
outbound-msg-retry{COLON} { YDVAR(1, VAR_OUTBOUND_MSG_RETRY) }
+max-sent-count{COLON} { YDVAR(1, VAR_MAX_SENT_COUNT) }
+max-query-restarts{COLON} { YDVAR(1, VAR_MAX_QUERY_RESTARTS) }
low-rtt{COLON} { YDVAR(1, VAR_LOW_RTT) }
fast-server-num{COLON} { YDVAR(1, VAR_FAST_SERVER_NUM) }
low-rtt-pct{COLON} { YDVAR(1, VAR_FAST_SERVER_PERMIL) }
secret-seed{COLON} { YDVAR(1, VAR_CACHEDB_SECRETSEED) }
redis-server-host{COLON} { YDVAR(1, VAR_CACHEDB_REDISHOST) }
redis-server-port{COLON} { YDVAR(1, VAR_CACHEDB_REDISPORT) }
+redis-server-path{COLON} { YDVAR(1, VAR_CACHEDB_REDISPATH) }
+redis-server-password{COLON} { YDVAR(1, VAR_CACHEDB_REDISPASSWORD) }
redis-timeout{COLON} { YDVAR(1, VAR_CACHEDB_REDISTIMEOUT) }
redis-expire-records{COLON} { YDVAR(1, VAR_CACHEDB_REDISEXPIRERECORDS) }
ipset{COLON} { YDVAR(0, VAR_IPSET) }
name-v6{COLON} { YDVAR(1, VAR_IPSET_NAME_V6) }
udp-upstream-without-downstream{COLON} { YDVAR(1, VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM) }
tcp-connection-limit{COLON} { YDVAR(2, VAR_TCP_CONNECTION_LIMIT) }
+answer-cookie{COLON} { YDVAR(1, VAR_ANSWER_COOKIE ) }
+cookie-secret{COLON} { YDVAR(1, VAR_COOKIE_SECRET) }
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 ) }
else { BEGIN(val); }
}
<quotedstring>{DQANY}* { LEXOUT(("STR(%s) ", yytext)); yymore(); }
-<quotedstring>{NEWLINE} { yyerror("newline inside quoted string, no end \"");
+<quotedstring>{NEWLINE} { yyerror("newline inside quoted string, no end \"");
cfg_parser->line++; BEGIN(INITIAL); }
<quotedstring>\" {
LEXOUT(("QE "));
else { BEGIN(val); }
}
<singlequotedstr>{SQANY}* { LEXOUT(("STR(%s) ", yytext)); yymore(); }
-<singlequotedstr>{NEWLINE} { yyerror("newline inside quoted string, no end '");
+<singlequotedstr>{NEWLINE} { yyerror("newline inside quoted string, no end '");
cfg_parser->line++; BEGIN(INITIAL); }
<singlequotedstr>\' {
LEXOUT(("SQE "));
}
/* include: directive */
-<INITIAL,val>include{COLON} {
+<INITIAL,val>include{COLON} {
LEXOUT(("v(%s) ", yytext)); inc_prev = YYSTATE; BEGIN(include); }
<include><<EOF>> {
yyerror("EOF inside include directive");
BEGIN(inc_prev);
}
<include_quoted>{DQANY}* { LEXOUT(("ISTR(%s) ", yytext)); yymore(); }
-<include_quoted>{NEWLINE} { yyerror("newline before \" in include name");
+<include_quoted>{NEWLINE} { yyerror("newline before \" in include name");
cfg_parser->line++; BEGIN(inc_prev); }
<include_quoted>\" {
LEXOUT(("IQE "));
return (VAR_FORCE_TOPLEVEL);
}
-<val>{UNQUOTEDLETTER}* { LEXOUT(("unquotedstr(%s) ", yytext));
+<val>{UNQUOTEDLETTER}* { LEXOUT(("unquotedstr(%s) ", yytext));
if(--num_args == 0) { BEGIN(INITIAL); }
yylval.str = strdup(yytext); return STRING_ARG; }
#include "util/configyyrename.h"
#include "util/config_file.h"
#include "util/net_help.h"
+#include "sldns/str2wire.h"
int ub_c_lex(void);
void ub_c_error(const char *message);
%token VAR_FORCE_TOPLEVEL
%token VAR_SERVER VAR_VERBOSITY VAR_NUM_THREADS VAR_PORT
%token VAR_OUTGOING_RANGE VAR_INTERFACE VAR_PREFER_IP4
-%token VAR_DO_IP4 VAR_DO_IP6 VAR_PREFER_IP6 VAR_DO_UDP VAR_DO_TCP
+%token VAR_DO_IP4 VAR_DO_IP6 VAR_DO_NAT64 VAR_PREFER_IP6 VAR_DO_UDP VAR_DO_TCP
%token VAR_TCP_MSS VAR_OUTGOING_TCP_MSS VAR_TCP_IDLE_TIMEOUT
%token VAR_EDNS_TCP_KEEPALIVE VAR_EDNS_TCP_KEEPALIVE_TIMEOUT
+%token VAR_SOCK_QUEUE_TIMEOUT
%token VAR_CHROOT VAR_USERNAME VAR_DIRECTORY VAR_LOGFILE VAR_PIDFILE
%token VAR_MSG_CACHE_SIZE VAR_MSG_CACHE_SLABS VAR_NUM_QUERIES_PER_THREAD
%token VAR_RRSET_CACHE_SIZE VAR_RRSET_CACHE_SLABS VAR_OUTGOING_NUM_TCP
%token VAR_UNBLOCK_LAN_ZONES VAR_INSECURE_LAN_ZONES
%token VAR_INFRA_CACHE_MIN_RTT VAR_INFRA_CACHE_MAX_RTT VAR_INFRA_KEEP_PROBING
%token VAR_DNS64_PREFIX VAR_DNS64_SYNTHALL VAR_DNS64_IGNORE_AAAA
+%token VAR_NAT64_PREFIX
%token VAR_DNSTAP VAR_DNSTAP_ENABLE VAR_DNSTAP_SOCKET_PATH VAR_DNSTAP_IP
%token VAR_DNSTAP_TLS VAR_DNSTAP_TLS_SERVER_NAME VAR_DNSTAP_TLS_CERT_BUNDLE
%token VAR_DNSTAP_TLS_CLIENT_KEY_FILE VAR_DNSTAP_TLS_CLIENT_CERT_FILE
%token VAR_DISABLE_DNSSEC_LAME_CHECK
%token VAR_IP_RATELIMIT VAR_IP_RATELIMIT_SLABS VAR_IP_RATELIMIT_SIZE
%token VAR_RATELIMIT VAR_RATELIMIT_SLABS VAR_RATELIMIT_SIZE
-%token VAR_OUTBOUND_MSG_RETRY
+%token VAR_OUTBOUND_MSG_RETRY VAR_MAX_SENT_COUNT VAR_MAX_QUERY_RESTARTS
%token VAR_RATELIMIT_FOR_DOMAIN VAR_RATELIMIT_BELOW_DOMAIN
%token VAR_IP_RATELIMIT_FACTOR VAR_RATELIMIT_FACTOR
%token VAR_IP_RATELIMIT_BACKOFF VAR_RATELIMIT_BACKOFF
%token VAR_IPSECMOD_MAX_TTL VAR_IPSECMOD_WHITELIST VAR_IPSECMOD_STRICT
%token VAR_CACHEDB VAR_CACHEDB_BACKEND VAR_CACHEDB_SECRETSEED
%token VAR_CACHEDB_REDISHOST VAR_CACHEDB_REDISPORT VAR_CACHEDB_REDISTIMEOUT
-%token VAR_CACHEDB_REDISEXPIRERECORDS
+%token VAR_CACHEDB_REDISEXPIRERECORDS VAR_CACHEDB_REDISPATH VAR_CACHEDB_REDISPASSWORD
%token VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM VAR_FOR_UPSTREAM
%token VAR_AUTH_ZONE VAR_ZONEFILE VAR_MASTER VAR_URL VAR_FOR_DOWNSTREAM
%token VAR_FALLBACK_ENABLED VAR_TLS_ADDITIONAL_PORT VAR_LOW_RTT VAR_LOW_RTT_PERMIL
%token VAR_FAST_SERVER_PERMIL VAR_FAST_SERVER_NUM
%token VAR_ALLOW_NOTIFY VAR_TLS_WIN_CERT VAR_TCP_CONNECTION_LIMIT
+%token VAR_ANSWER_COOKIE VAR_COOKIE_SECRET VAR_IP_RATELIMIT_COOKIE
%token VAR_FORWARD_NO_CACHE VAR_STUB_NO_CACHE VAR_LOG_SERVFAIL VAR_DENY_ANY
%token VAR_UNKNOWN_SERVER_TIME_LIMIT VAR_LOG_TAG_QUERYREPLY
%token VAR_STREAM_WAIT_SIZE VAR_TLS_CIPHERS VAR_TLS_CIPHERSUITES VAR_TLS_USE_SNI
%token VAR_RPZ_SIGNAL_NXDOMAIN_RA VAR_INTERFACE_AUTOMATIC_PORTS VAR_EDE
%token VAR_INTERFACE_ACTION VAR_INTERFACE_VIEW VAR_INTERFACE_TAG
%token VAR_INTERFACE_TAG_ACTION VAR_INTERFACE_TAG_DATA
-%token VAR_PROXY_PROTOCOL_PORT
+%token VAR_PROXY_PROTOCOL_PORT VAR_STATISTICS_INHIBIT_ZERO
+%token VAR_HARDEN_UNKNOWN_ADDITIONAL
%%
toplevelvars: /* empty */ | toplevelvars toplevelvar ;
| ;
content_server: server_num_threads | server_verbosity | server_port |
server_outgoing_range | server_do_ip4 |
- server_do_ip6 | server_prefer_ip4 | server_prefer_ip6 |
- server_do_udp | server_do_tcp |
+ server_do_ip6 | server_do_nat64 | server_prefer_ip4 |
+ server_prefer_ip6 | server_do_udp | server_do_tcp |
server_tcp_mss | server_outgoing_tcp_mss | server_tcp_idle_timeout |
server_tcp_keepalive | server_tcp_keepalive_timeout |
+ server_sock_queue_timeout |
server_interface | server_chroot | server_username |
server_directory | server_logfile | server_pidfile |
server_msg_cache_size | server_msg_cache_slabs |
server_so_reuseport | server_delay_close | server_udp_connect |
server_unblock_lan_zones | server_insecure_lan_zones |
server_dns64_prefix | server_dns64_synthall | server_dns64_ignore_aaaa |
+ server_nat64_prefix |
server_infra_cache_min_rtt | server_infra_cache_max_rtt | server_harden_algo_downgrade |
server_ip_transparent | server_ip_ratelimit | server_ratelimit |
server_ip_dscp | server_infra_keep_probing |
server_ratelimit_below_domain | server_ratelimit_factor |
server_ip_ratelimit_factor | server_ratelimit_backoff |
server_ip_ratelimit_backoff | server_outbound_msg_retry |
+ server_max_sent_count | server_max_query_restarts |
server_send_client_subnet | server_client_subnet_zone |
server_client_subnet_always_forward | server_client_subnet_opcode |
server_max_client_subnet_ipv4 | server_max_client_subnet_ipv6 |
server_serve_expired |
server_serve_expired_ttl | server_serve_expired_ttl_reset |
server_serve_expired_reply_ttl | server_serve_expired_client_timeout |
- server_ede_serve_expired | 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_unknown_server_time_limit | server_log_tag_queryreply |
server_stream_wait_size | server_tls_ciphers |
server_tls_ciphersuites | server_tls_session_ticket_keys |
+ server_answer_cookie | server_cookie_secret | server_ip_ratelimit_cookie |
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_interface_automatic_ports | server_ede |
- server_proxy_protocol_port
+ server_proxy_protocol_port | server_statistics_inhibit_zero |
+ server_harden_unknown_additional
;
stubstart: VAR_STUB_ZONE
{
rpzstart: VAR_RPZ
{
struct config_auth* s;
- OUTYY(("\nP(rpz:)\n"));
+ OUTYY(("\nP(rpz:)\n"));
cfg_parser->started_toplevel = 1;
s = (struct config_auth*)calloc(1, sizeof(struct config_auth));
if(s) {
}
}
;
-contents_rpz: contents_rpz content_rpz
+contents_rpz: contents_rpz content_rpz
| ;
content_rpz: auth_name | auth_zonefile | rpz_tag | auth_master | auth_url |
auth_allow_notify | rpz_action_override | rpz_cname_override |
free($2);
}
;
+server_statistics_inhibit_zero: VAR_STATISTICS_INHIBIT_ZERO STRING_ARG
+ {
+ OUTYY(("P(server_statistics_inhibit_zero:%s)\n", $2));
+ if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+ yyerror("expected yes or no.");
+ else cfg_parser->cfg->stat_inhibit_zero = (strcmp($2, "yes")==0);
+ free($2);
+ }
+ ;
server_shm_enable: VAR_SHM_ENABLE STRING_ARG
{
OUTYY(("P(server_shm_enable:%s)\n", $2));
free($2);
}
;
+server_do_nat64: VAR_DO_NAT64 STRING_ARG
+ {
+ OUTYY(("P(server_do_nat64:%s)\n", $2));
+ if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+ yyerror("expected yes or no.");
+ else cfg_parser->cfg->do_nat64 = (strcmp($2, "yes")==0);
+ free($2);
+ }
+ ;
server_do_udp: VAR_DO_UDP STRING_ARG
{
OUTYY(("P(server_do_udp:%s)\n", $2));
free($2);
}
;
+server_sock_queue_timeout: VAR_SOCK_QUEUE_TIMEOUT STRING_ARG
+ {
+ OUTYY(("P(server_sock_queue_timeout:%s)\n", $2));
+ if(atoi($2) == 0 && strcmp($2, "0") != 0)
+ yyerror("number expected");
+ else if (atoi($2) > 6553500)
+ cfg_parser->cfg->sock_queue_timeout = 6553500;
+ else if (atoi($2) < 1)
+ cfg_parser->cfg->sock_queue_timeout = 0;
+ else cfg_parser->cfg->sock_queue_timeout = atoi($2);
+ free($2);
+ }
+ ;
server_tcp_upstream: VAR_TCP_UPSTREAM STRING_ARG
{
OUTYY(("P(server_tcp_upstream:%s)\n", $2));
yyerror("expected yes or no.");
else cfg_parser->cfg->http_nodelay = (strcmp($2, "yes")==0);
free($2);
- }
+ };
server_http_notls_downstream: VAR_HTTP_NOTLS_DOWNSTREAM STRING_ARG
{
OUTYY(("P(server_http_notls_downstream:%s)\n", $2));
free($2);
}
;
+server_harden_unknown_additional: VAR_HARDEN_UNKNOWN_ADDITIONAL STRING_ARG
+ {
+ OUTYY(("P(server_harden_unknown_additional:%s)\n", $2));
+ if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+ yyerror("expected yes or no.");
+ else cfg_parser->cfg->harden_unknown_additional =
+ (strcmp($2, "yes")==0);
+ free($2);
+ }
+ ;
server_use_caps_for_id: VAR_USE_CAPS_FOR_ID STRING_ARG
{
OUTYY(("P(server_use_caps_for_id:%s)\n", $2));
(strcmp($2, "yes")==0);
free($2);
}
+ ;
server_key_cache_size: VAR_KEY_CACHE_SIZE STRING_ARG
{
OUTYY(("P(server_key_cache_size:%s)\n", $2));
strcmp($3, "transparent")!=0 && strcmp($3, "nodefault")!=0
&& strcmp($3, "typetransparent")!=0
&& strcmp($3, "always_transparent")!=0
+ && strcmp($3, "block_a")!=0
&& strcmp($3, "always_refuse")!=0
&& strcmp($3, "always_nxdomain")!=0
&& strcmp($3, "always_nodata")!=0
yyerror("local-zone type: expected static, deny, "
"refuse, redirect, transparent, "
"typetransparent, inform, inform_deny, "
- "inform_redirect, always_transparent, "
+ "inform_redirect, always_transparent, block_a,"
"always_refuse, always_nxdomain, "
"always_nodata, always_deny, always_null, "
"noview, nodefault or ipset");
fatal_exit("out of memory adding dns64-ignore-aaaa");
}
;
+server_nat64_prefix: VAR_NAT64_PREFIX STRING_ARG
+ {
+ OUTYY(("P(nat64_prefix:%s)\n", $2));
+ free(cfg_parser->cfg->nat64_prefix);
+ cfg_parser->cfg->nat64_prefix = $2;
+ }
+ ;
server_define_tag: VAR_DEFINE_TAG STRING_ARG
{
char* p, *s = $2;
free($2);
}
;
+server_ip_ratelimit_cookie: VAR_IP_RATELIMIT_COOKIE STRING_ARG
+ {
+ OUTYY(("P(server_ip_ratelimit_cookie:%s)\n", $2));
+ if(atoi($2) == 0 && strcmp($2, "0") != 0)
+ yyerror("number expected");
+ else cfg_parser->cfg->ip_ratelimit_cookie = atoi($2);
+ free($2);
+ }
+ ;
server_ratelimit: VAR_RATELIMIT STRING_ARG
{
OUTYY(("P(server_ratelimit:%s)\n", $2));
free($2);
}
;
+server_max_sent_count: VAR_MAX_SENT_COUNT STRING_ARG
+ {
+ OUTYY(("P(server_max_sent_count:%s)\n", $2));
+ if(atoi($2) == 0 && strcmp($2, "0") != 0)
+ yyerror("number expected");
+ else cfg_parser->cfg->max_sent_count = atoi($2);
+ free($2);
+ }
+ ;
+server_max_query_restarts: VAR_MAX_QUERY_RESTARTS STRING_ARG
+ {
+ OUTYY(("P(server_max_query_restarts:%s)\n", $2));
+ if(atoi($2) == 0 && strcmp($2, "0") != 0)
+ yyerror("number expected");
+ else cfg_parser->cfg->max_query_restarts = atoi($2);
+ free($2);
+ }
+ ;
server_low_rtt: VAR_LOW_RTT STRING_ARG
{
OUTYY(("P(low-rtt option is deprecated, use fast-server-num instead)\n"));
OUTYY(("P(server_pad_responses:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
- else cfg_parser->cfg->pad_responses =
+ else cfg_parser->cfg->pad_responses =
(strcmp($2, "yes")==0);
free($2);
}
OUTYY(("P(server_pad_queries:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
- else cfg_parser->cfg->pad_queries =
+ else cfg_parser->cfg->pad_queries =
(strcmp($2, "yes")==0);
free($2);
}
if(!cfg_strlist_append_ex(&cfg_parser->cfg->python_script, $2))
yyerror("out of memory");
}
+ ;
dynlibstart: VAR_DYNLIB
- {
- OUTYY(("\nP(dynlib:)\n"));
+ {
+ OUTYY(("\nP(dynlib:)\n"));
cfg_parser->started_toplevel = 1;
}
;
if(!cfg_strlist_append_ex(&cfg_parser->cfg->dynlib_file, $2))
yyerror("out of memory");
}
+ ;
server_disable_dnssec_lame_check: VAR_DISABLE_DNSSEC_LAME_CHECK STRING_ARG
{
OUTYY(("P(disable_dnssec_lame_check:%s)\n", $2));
free($2);
}
;
-
dnsc_dnscrypt_port: VAR_DNSCRYPT_PORT STRING_ARG
{
OUTYY(("P(dnsc_dnscrypt_port:%s)\n", $2));
| ;
content_cachedb: cachedb_backend_name | cachedb_secret_seed |
redis_server_host | redis_server_port | redis_timeout |
- redis_expire_records
+ redis_expire_records | redis_server_path | redis_server_password
;
cachedb_backend_name: VAR_CACHEDB_BACKEND STRING_ARG
{
free($2);
}
;
+redis_server_path: VAR_CACHEDB_REDISPATH STRING_ARG
+ {
+ #if defined(USE_CACHEDB) && defined(USE_REDIS)
+ OUTYY(("P(redis_server_path:%s)\n", $2));
+ free(cfg_parser->cfg->redis_server_path);
+ cfg_parser->cfg->redis_server_path = $2;
+ #else
+ OUTYY(("P(Compiled without cachedb or redis, ignoring)\n"));
+ free($2);
+ #endif
+ }
+ ;
+redis_server_password: VAR_CACHEDB_REDISPASSWORD STRING_ARG
+ {
+ #if defined(USE_CACHEDB) && defined(USE_REDIS)
+ OUTYY(("P(redis_server_password:%s)\n", $2));
+ free(cfg_parser->cfg->redis_server_password);
+ cfg_parser->cfg->redis_server_password = $2;
+ #else
+ OUTYY(("P(Compiled without cachedb or redis, ignoring)\n"));
+ free($2);
+ #endif
+ }
+ ;
redis_timeout: VAR_CACHEDB_REDISTIMEOUT STRING_ARG
{
#if defined(USE_CACHEDB) && defined(USE_REDIS)
}
}
;
+server_answer_cookie: VAR_ANSWER_COOKIE STRING_ARG
+ {
+ OUTYY(("P(server_answer_cookie:%s)\n", $2));
+ if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+ yyerror("expected yes or no.");
+ else cfg_parser->cfg->do_answer_cookie = (strcmp($2, "yes")==0);
+ free($2);
+ }
+ ;
+server_cookie_secret: VAR_COOKIE_SECRET STRING_ARG
+ {
+ uint8_t secret[32];
+ size_t secret_len = sizeof(secret);
+
+ OUTYY(("P(server_cookie_secret:%s)\n", $2));
+ if(sldns_str2wire_hex_buf($2, secret, &secret_len)
+ || (secret_len != 16))
+ yyerror("expected 128 bit hex string");
+ else {
+ cfg_parser->cfg->cookie_secret_len = secret_len;
+ memcpy(cfg_parser->cfg->cookie_secret, secret, sizeof(secret));
+ }
+ free($2);
+ }
+ ;
ipsetstart: VAR_IPSET
{
OUTYY(("\nP(ipset:)\n"));
strcmp(action, "refuse_non_local")!=0 &&
strcmp(action, "allow_setrd")!=0 &&
strcmp(action, "allow")!=0 &&
- strcmp(action, "allow_snoop")!=0)
+ strcmp(action, "allow_snoop")!=0 &&
+ strcmp(action, "allow_cookie")!=0)
{
yyerror("expected deny, refuse, deny_non_local, "
- "refuse_non_local, allow, allow_setrd or "
- "allow_snoop as access control action");
+ "refuse_non_local, allow, allow_setrd, "
+ "allow_snoop or allow_cookie as access control action");
}
}
return 1 + 2 + 2 + 4 + 2 + rdatalen;
}
+uint16_t
+calc_edns_option_size(struct edns_data* edns, uint16_t code)
+{
+ size_t rdatalen = 0;
+ struct edns_option* opt;
+ if(!edns || !edns->edns_present)
+ return 0;
+ for(opt = edns->opt_list_inplace_cb_out; opt; opt = opt->next) {
+ if(opt->opt_code == code)
+ rdatalen += 4 + opt->opt_len;
+ }
+ for(opt = edns->opt_list_out; opt; opt = opt->next) {
+ if(opt->opt_code == code)
+ rdatalen += 4 + opt->opt_len;
+ }
+ return rdatalen;
+}
+
+uint16_t
+calc_ede_option_size(struct edns_data* edns, uint16_t* txt_size)
+{
+ size_t rdatalen = 0;
+ struct edns_option* opt;
+ *txt_size = 0;
+ if(!edns || !edns->edns_present)
+ return 0;
+ for(opt = edns->opt_list_inplace_cb_out; opt; opt = opt->next) {
+ if(opt->opt_code == LDNS_EDNS_EDE) {
+ rdatalen += 4 + opt->opt_len;
+ if(opt->opt_len > 2) *txt_size += opt->opt_len - 2;
+ if(opt->opt_len >= 2 && sldns_read_uint16(
+ opt->opt_data) == LDNS_EDE_OTHER) {
+ *txt_size += 4 + 2;
+ }
+ }
+ }
+ for(opt = edns->opt_list_out; opt; opt = opt->next) {
+ if(opt->opt_code == LDNS_EDNS_EDE) {
+ rdatalen += 4 + opt->opt_len;
+ if(opt->opt_len > 2) *txt_size += opt->opt_len - 2;
+ if(opt->opt_len >= 2 && sldns_read_uint16(
+ opt->opt_data) == LDNS_EDE_OTHER) {
+ *txt_size += 4 + 2;
+ }
+ }
+ }
+ return rdatalen;
+}
+
+/* Trims the EDE OPTION-DATA to not include any EXTRA-TEXT data.
+ * Also removes any LDNS_EDE_OTHER options from the list since they are useless
+ * without the extra text. */
+static void
+ede_trim_text(struct edns_option** list)
+{
+ struct edns_option* curr, *prev = NULL;
+ if(!list || !(*list)) return;
+ /* Unlink and repoint if LDNS_EDE_OTHER are first in list */
+ while(list && *list && (*list)->opt_code == LDNS_EDNS_EDE
+ && (*list)->opt_len >= 2
+ && sldns_read_uint16((*list)->opt_data) == LDNS_EDE_OTHER ) {
+ *list = (*list)->next;
+ }
+ if(!list || !(*list)) return;
+ curr = *list;
+ while(curr) {
+ if(curr->opt_code == LDNS_EDNS_EDE) {
+ if(curr->opt_len >= 2 && sldns_read_uint16(
+ curr->opt_data) == LDNS_EDE_OTHER) {
+ /* LDNS_EDE_OTHER cannot be the first option in
+ * this while, so prev is always initialized at
+ * this point from the other branches;
+ * cut this option off */
+ prev->next = curr->next;
+ curr = curr->next;
+ } else if(curr->opt_len > 2) {
+ /* trim this option's EXTRA-TEXT */
+ curr->opt_len = 2;
+ prev = curr;
+ curr = curr->next;
+ }
+ } else {
+ /* continue */
+ prev = curr;
+ curr = curr->next;
+ }
+ }
+}
+
static void
attach_edns_record_max_msg_sz(sldns_buffer* pkt, struct edns_data* edns,
uint16_t max_msg_sz)
{
uint16_t flags;
unsigned int attach_edns = 0;
+ uint16_t edns_field_size, ede_size, ede_txt_size;
if(!cached || rep->authoritative) {
/* original flags, copy RD and CD bits from query. */
log_assert(flags & BIT_QR); /* QR bit must be on in our replies */
if(udpsize < LDNS_HEADER_SIZE)
return 0;
+ /* currently edns does not change during calculations;
+ * calculate sizes once here */
+ edns_field_size = calc_edns_field_size(edns);
+ ede_size = calc_ede_option_size(edns, &ede_txt_size);
if(sldns_buffer_capacity(pkt) < udpsize)
udpsize = sldns_buffer_capacity(pkt);
- if(udpsize < LDNS_HEADER_SIZE + calc_edns_field_size(edns)) {
+ /* EDEs are optional, try to fit anything else before them */
+ if(udpsize < LDNS_HEADER_SIZE + edns_field_size - ede_size) {
/* packet too small to contain edns, omit it. */
attach_edns = 0;
} else {
/* reserve space for edns record */
- attach_edns = (unsigned int)calc_edns_field_size(edns);
- udpsize -= attach_edns;
+ attach_edns = (unsigned int)edns_field_size - ede_size;
}
if(!reply_info_encode(qinf, rep, id, flags, pkt, timenow, region,
- udpsize, dnssec, MINIMAL_RESPONSES)) {
+ udpsize - attach_edns, dnssec, MINIMAL_RESPONSES)) {
log_err("reply encode: out of memory");
return 0;
}
- if(attach_edns && sldns_buffer_capacity(pkt) >=
- sldns_buffer_limit(pkt)+attach_edns)
- attach_edns_record_max_msg_sz(pkt, edns, udpsize+attach_edns);
+ if(attach_edns) {
+ if(udpsize >= sldns_buffer_limit(pkt) + edns_field_size)
+ attach_edns_record_max_msg_sz(pkt, edns, udpsize);
+ else if(udpsize >= sldns_buffer_limit(pkt) + edns_field_size - ede_txt_size) {
+ ede_trim_text(&edns->opt_list_inplace_cb_out);
+ ede_trim_text(&edns->opt_list_out);
+ attach_edns_record_max_msg_sz(pkt, edns, udpsize);
+ } else if(udpsize >= sldns_buffer_limit(pkt) + edns_field_size - ede_size) {
+ edns_opt_list_remove(&edns->opt_list_inplace_cb_out, LDNS_EDNS_EDE);
+ edns_opt_list_remove(&edns->opt_list_out, LDNS_EDNS_EDE);
+ attach_edns_record_max_msg_sz(pkt, edns, udpsize);
+ }
+ }
return 1;
}
sldns_buffer_flip(pkt);
}
-void
-error_encode(sldns_buffer* buf, int r, struct query_info* qinfo,
- uint16_t qid, uint16_t qflags, struct edns_data* edns)
+void
+extended_error_encode(sldns_buffer* buf, uint16_t rcode,
+ struct query_info* qinfo, uint16_t qid, uint16_t qflags,
+ uint16_t xflags, struct edns_data* edns)
{
uint16_t flags;
sldns_buffer_clear(buf);
sldns_buffer_write(buf, &qid, sizeof(uint16_t));
- flags = (uint16_t)(BIT_QR | BIT_RA | r); /* QR and retcode*/
+ flags = (uint16_t)(BIT_QR | BIT_RA | (rcode & 0xF)); /* QR and retcode*/
+ flags |= xflags;
flags |= (qflags & (BIT_RD|BIT_CD)); /* copy RD and CD bit */
sldns_buffer_write_u16(buf, flags);
if(qinfo) flags = 1;
struct edns_data es = *edns;
es.edns_version = EDNS_ADVERTISED_VERSION;
es.udp_size = EDNS_ADVERTISED_SIZE;
- es.ext_rcode = 0;
+ es.ext_rcode = (uint8_t)(rcode >> 4);
es.bits &= EDNS_DO;
if(sldns_buffer_limit(buf) + calc_edns_field_size(&es) >
- edns->udp_size)
- return;
+ edns->udp_size) {
+ edns_opt_list_remove(&es.opt_list_inplace_cb_out, LDNS_EDNS_EDE);
+ edns_opt_list_remove(&es.opt_list_out, LDNS_EDNS_EDE);
+ if(sldns_buffer_limit(buf) + calc_edns_field_size(&es) >
+ edns->udp_size) {
+ return;
+ }
+ }
attach_edns_record(buf, &es);
}
}
+
+void
+error_encode(sldns_buffer* buf, int r, struct query_info* qinfo,
+ uint16_t qid, uint16_t qflags, struct edns_data* edns)
+{
+ extended_error_encode(buf, (r & 0x000F), qinfo, qid, qflags,
+ (r & 0xFFF0), edns);
+}
#include "util/netevent.h"
#include "util/storage/lookup3.h"
#include "util/regional.h"
+#include "util/rfc_1982.h"
+#include "util/edns.h"
#include "sldns/rrdef.h"
#include "sldns/sbuffer.h"
#include "sldns/parseutil.h"
return 0;
}
-static int
-edns_opt_list_append_keepalive(struct edns_option** list, int msec,
- struct regional* region)
-{
- uint8_t data[2]; /* For keepalive value */
- data[0] = (uint8_t)((msec >> 8) & 0xff);
- data[1] = (uint8_t)(msec & 0xff);
- return edns_opt_list_append(list, LDNS_EDNS_KEEPALIVE, sizeof(data),
- data, region);
-}
-
/** parse EDNS options from EDNS wireformat rdata */
static int
parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len,
struct edns_data* edns, struct config_file* cfg, struct comm_point* c,
- struct regional* region)
+ struct comm_reply* repinfo, uint32_t now, struct regional* region)
{
/* To respond with a Keepalive option, the client connection must have
* received one message with a TCP Keepalive EDNS option, and that
while(rdata_len >= 4) {
uint16_t opt_code = sldns_read_uint16(rdata_ptr);
uint16_t opt_len = sldns_read_uint16(rdata_ptr+2);
+ uint8_t server_cookie[40];
+ enum edns_cookie_val_status cookie_val_status;
+ int cookie_is_v4 = 1;
+
rdata_ptr += 4;
rdata_len -= 4;
if(opt_len > rdata_len)
edns->padding_block_size = cfg->pad_responses_block_size;
break;
+ case LDNS_EDNS_COOKIE:
+ if(!cfg || !cfg->do_answer_cookie || !repinfo)
+ break;
+ if(opt_len != 8 && (opt_len < 16 || opt_len > 40)) {
+ verbose(VERB_ALGO, "worker request: "
+ "badly formatted cookie");
+ return LDNS_RCODE_FORMERR;
+ }
+ edns->cookie_present = 1;
+
+ /* Copy client cookie, version and timestamp for
+ * validation and creation purposes.
+ */
+ if(opt_len >= 16) {
+ memmove(server_cookie, rdata_ptr, 16);
+ } else {
+ memset(server_cookie, 0, 16);
+ memmove(server_cookie, rdata_ptr, opt_len);
+ }
+
+ /* Copy client ip for validation and creation
+ * purposes. It will be overwritten if (re)creation
+ * is needed.
+ */
+ if(repinfo->remote_addr.ss_family == AF_INET) {
+ memcpy(server_cookie + 16,
+ &((struct sockaddr_in*)&repinfo->remote_addr)->sin_addr, 4);
+ } else {
+ cookie_is_v4 = 0;
+ memcpy(server_cookie + 16,
+ &((struct sockaddr_in6*)&repinfo->remote_addr)->sin6_addr, 16);
+ }
+
+ cookie_val_status = edns_cookie_server_validate(
+ rdata_ptr, opt_len, cfg->cookie_secret,
+ cfg->cookie_secret_len, cookie_is_v4,
+ server_cookie, now);
+ switch(cookie_val_status) {
+ case COOKIE_STATUS_VALID:
+ case COOKIE_STATUS_VALID_RENEW:
+ edns->cookie_valid = 1;
+ /* Reuse cookie */
+ if(!edns_opt_list_append(
+ &edns->opt_list_out, LDNS_EDNS_COOKIE,
+ opt_len, rdata_ptr, region)) {
+ log_err("out of memory");
+ return LDNS_RCODE_SERVFAIL;
+ }
+ /* Cookie to be reused added to outgoing
+ * options. Done!
+ */
+ break;
+ case COOKIE_STATUS_CLIENT_ONLY:
+ edns->cookie_client = 1;
+ /* fallthrough */
+ case COOKIE_STATUS_FUTURE:
+ case COOKIE_STATUS_EXPIRED:
+ case COOKIE_STATUS_INVALID:
+ default:
+ edns_cookie_server_write(server_cookie,
+ cfg->cookie_secret, cookie_is_v4, now);
+ if(!edns_opt_list_append(&edns->opt_list_out,
+ LDNS_EDNS_COOKIE, 24, server_cookie,
+ region)) {
+ log_err("out of memory");
+ return LDNS_RCODE_SERVFAIL;
+ }
+ break;
+ }
+ break;
default:
break;
}
edns->opt_list_out = NULL;
edns->opt_list_inplace_cb_out = NULL;
edns->padding_block_size = 0;
+ edns->cookie_present = 0;
+ edns->cookie_valid = 0;
/* take the options */
rdata_len = found->rr_first->size-2;
int
parse_edns_from_query_pkt(sldns_buffer* pkt, struct edns_data* edns,
- struct config_file* cfg, struct comm_point* c, struct regional* region)
+ struct config_file* cfg, struct comm_point* c,
+ struct comm_reply* repinfo, time_t now, struct regional* region)
{
size_t rdata_len;
uint8_t* rdata_ptr;
edns->opt_list_out = NULL;
edns->opt_list_inplace_cb_out = NULL;
edns->padding_block_size = 0;
+ edns->cookie_present = 0;
+ edns->cookie_valid = 0;
/* take the options */
rdata_len = sldns_buffer_read_u16(pkt);
rdata_ptr = sldns_buffer_current(pkt);
/* ignore rrsigs */
return parse_edns_options_from_query(rdata_ptr, rdata_len, edns, cfg,
- c, region);
+ c, repinfo, now, region);
}
void
struct edns_option;
struct config_file;
struct comm_point;
+struct comm_reply;
/** number of buckets in parse rrset hash table. Must be power of 2. */
#define PARSE_TABLE_SIZE 32
* region.
*/
struct edns_data {
- /** if EDNS OPT record was present */
- int edns_present;
/** Extended RCODE */
uint8_t ext_rcode;
/** The EDNS version number */
struct edns_option* opt_list_inplace_cb_out;
/** block size to pad */
uint16_t padding_block_size;
-};
+ /** if EDNS OPT record was present */
+ unsigned int edns_present : 1;
+ /** if a cookie was present */
+ unsigned int cookie_present : 1;
+ /** if the cookie validated */
+ unsigned int cookie_valid : 1;
+ /** if the cookie holds only the client part */
+ unsigned int cookie_client : 1;
+};
/**
* EDNS option
* initialised.
* @param cfg: the configuration (with nsid value etc.)
* @param c: commpoint to determine transport (if needed)
+ * @param repinfo: commreply to determine the client address
+ * @param now: current time
* @param region: region to alloc results in (edns option contents)
* @return: 0 on success, or an RCODE on error.
* RCODE formerr if OPT is badly formatted and so on.
*/
int parse_edns_from_query_pkt(struct sldns_buffer* pkt, struct edns_data* edns,
- struct config_file* cfg, struct comm_point* c, struct regional* region);
+ struct config_file* cfg, struct comm_point* c,
+ struct comm_reply* repinfo, time_t now, struct regional* region);
/**
* Calculate hash value for rrset in packet.
struct reply_info*
construct_reply_info_base(struct regional* region, uint16_t flags, size_t qd,
time_t ttl, time_t prettl, time_t expttl, size_t an, size_t ns,
- size_t ar, size_t total, enum sec_status sec)
+ size_t ar, size_t total, enum sec_status sec, sldns_ede_code reason_bogus)
{
struct reply_info* rep;
/* rrset_count-1 because the first ref is part of the struct. */
rep->ar_numrrsets = ar;
rep->rrset_count = total;
rep->security = sec;
- rep->reason_bogus = LDNS_EDE_NONE;
+ rep->reason_bogus = reason_bogus;
+ /* this is only allocated and used for caching on copy */
+ rep->reason_bogus_str = NULL;
rep->authoritative = 0;
/* array starts after the refs */
if(region)
{
*rep = construct_reply_info_base(region, msg->flags, msg->qdcount, 0,
0, 0, msg->an_rrsets, msg->ns_rrsets, msg->ar_rrsets,
- msg->rrset_count, sec_status_unchecked);
+ msg->rrset_count, sec_status_unchecked, LDNS_EDE_NONE);
if(!*rep)
return 0;
return 1;
new_rep = construct_reply_info_base(region, rep->flags,
rep->qdcount, rep->ttl, rep->prefetch_ttl,
rep->serve_expired_ttl, an_numrrsets, 0, 0, an_numrrsets,
- sec_status_insecure);
+ sec_status_insecure, LDNS_EDE_NONE);
if(!new_rep)
return NULL;
if(!reply_info_alloc_rrset_keys(new_rep, NULL, region))
for(i=0; i<rep->rrset_count; i++) {
ub_packed_rrset_parsedelete(rep->rrsets[i], alloc);
}
+ if(rep->reason_bogus_str) {
+ free(rep->reason_bogus_str);
+ rep->reason_bogus_str = NULL;
+ }
free(rep);
}
reply_info_delete(void* d, void* ATTR_UNUSED(arg))
{
struct reply_info* r = (struct reply_info*)d;
+ if(r->reason_bogus_str) {
+ free(r->reason_bogus_str);
+ r->reason_bogus_str = NULL;
+ }
free(r);
}
return 1;
}
-struct reply_info*
-reply_info_copy(struct reply_info* rep, struct alloc_cache* alloc,
+struct reply_info*
+reply_info_copy(struct reply_info* rep, struct alloc_cache* alloc,
struct regional* region)
{
struct reply_info* cp;
- cp = construct_reply_info_base(region, rep->flags, rep->qdcount,
- rep->ttl, rep->prefetch_ttl, rep->serve_expired_ttl,
+ cp = construct_reply_info_base(region, rep->flags, rep->qdcount,
+ rep->ttl, rep->prefetch_ttl, rep->serve_expired_ttl,
rep->an_numrrsets, rep->ns_numrrsets, rep->ar_numrrsets,
- rep->rrset_count, rep->security);
+ rep->rrset_count, rep->security, rep->reason_bogus);
if(!cp)
return NULL;
+
+ if(rep->reason_bogus_str && *rep->reason_bogus_str != 0) {
+ if(region) {
+ cp->reason_bogus_str = (char*)regional_alloc(region,
+ sizeof(char)
+ * (strlen(rep->reason_bogus_str)+1));
+ } else {
+ cp->reason_bogus_str = malloc(sizeof(char)
+ * (strlen(rep->reason_bogus_str)+1));
+ }
+ if(!cp->reason_bogus_str) {
+ if(!region)
+ reply_info_parsedelete(cp, alloc);
+ return NULL;
+ }
+ memcpy(cp->reason_bogus_str, rep->reason_bogus_str,
+ strlen(rep->reason_bogus_str)+1);
+ }
+
/* allocate ub_key structures special or not */
if(!reply_info_alloc_rrset_keys(cp, alloc, region)) {
if(!region)
return 1;
}
+int edns_opt_list_append_keepalive(struct edns_option** list, int msec,
+ struct regional* region)
+{
+ uint8_t data[2]; /* For keepalive value */
+ data[0] = (uint8_t)((msec >> 8) & 0xff);
+ data[1] = (uint8_t)(msec & 0xff);
+ return edns_opt_list_append(list, LDNS_EDNS_KEEPALIVE, sizeof(data),
+ data, region);
+}
+
int edns_opt_list_append(struct edns_option** list, uint16_t code, size_t len,
uint8_t* data, struct regional* region)
{
}
struct edns_option* edns_opt_copy_region(struct edns_option* list,
- struct regional* region)
+ struct regional* region)
{
struct edns_option* result = NULL, *cur = NULL, *s;
while(list) {
return result;
}
+struct edns_option* edns_opt_copy_filter_region(struct edns_option* list,
+ uint16_t* filter_list, size_t filter_list_len, struct regional* region)
+{
+ struct edns_option* result = NULL, *cur = NULL, *s;
+ size_t i;
+ while(list) {
+ for(i=0; i<filter_list_len; i++)
+ if(filter_list[i] == list->opt_code) goto found;
+ if(i == filter_list_len) goto next;
+found:
+ /* copy edns option structure */
+ s = regional_alloc_init(region, list, sizeof(*list));
+ if(!s) return NULL;
+ s->next = NULL;
+
+ /* copy option data */
+ if(s->opt_data) {
+ s->opt_data = regional_alloc_init(region, s->opt_data,
+ s->opt_len);
+ if(!s->opt_data)
+ return NULL;
+ }
+
+ /* link into list */
+ if(cur)
+ cur->next = s;
+ else result = s;
+ cur = s;
+
+next:
+ /* examine next element */
+ list = list->next;
+ }
+ return result;
+}
+
int edns_opt_compare(struct edns_option* p, struct edns_option* q)
{
if(!p && !q) return 0;
/**
* EDE (rfc8914) code with reason for DNSSEC bogus status.
+ * Used for caching the EDE.
*/
sldns_ede_code reason_bogus;
+ /**
+ * EDE (rfc8914) NULL-terminated string with human-readable reason
+ * for DNSSEC bogus status.
+ * Used for caching the EDE.
+ */
+ char* reason_bogus_str;
+
/**
* Number of RRsets in each section.
* The answer section. Add up the RRs in every RRset to calculate
* @param ar: ar count
* @param total: total rrset count (presumably an+ns+ar).
* @param sec: security status of the reply info.
+ * @param reason_bogus: the Extended DNS Error for DNSSEC bogus status
* @return the reply_info base struct with the array for putting the rrsets
* in. The array has been zeroed. Returns NULL on malloc failure.
*/
struct reply_info*
construct_reply_info_base(struct regional* region, uint16_t flags, size_t qd,
- time_t ttl, time_t prettl, time_t expttl, size_t an, size_t ns,
- size_t ar, size_t total, enum sec_status sec);
+ time_t ttl, time_t prettl, time_t expttl, size_t an, size_t ns,
+ size_t ar, size_t total, enum sec_status sec,
+ sldns_ede_code reason_bogus);
/**
* Parse wire query into a queryinfo structure, return 0 on parse error.
int edns_opt_list_append_ede(struct edns_option** list, struct regional* region,
sldns_ede_code code, const char *txt);
+/**
+ * Append edns keep alive option to edns options list
+ * @param list: the edns option list to append the edns option to.
+ * @param msec: the duration in msecs for the keep alive.
+ * @param region: region to allocate the new edns option.
+ * @return false on failure.
+ */
+int edns_opt_list_append_keepalive(struct edns_option** list, int msec,
+ struct regional* region);
+
/**
* Remove any option found on the edns option list that matches the code.
* @param list: the list of edns options.
struct edns_option* edns_opt_copy_region(struct edns_option* list,
struct regional* region);
+/**
+ * Copy a filtered edns option list allocated to the new region
+ */
+struct edns_option* edns_opt_copy_filter_region(struct edns_option* list,
+ uint16_t* filter_list, size_t filter_list_len, struct regional* region);
+
/**
* Copy edns option list allocated with malloc
*/
#else
(void)fptr;
#endif
+#ifdef WITH_PYTHONMODULE
+ if(fptr == &python_inplace_cb_edns_back_parsed_call)
+ return 1;
+#endif
#ifdef WITH_DYNLIBMODULE
if(fptr == &dynlib_inplace_cb_edns_back_parsed)
return 1;
#else
(void)fptr;
#endif
+#ifdef WITH_PYTHONMODULE
+ if(fptr == &python_inplace_cb_query_response)
+ return 1;
+#endif
#ifdef WITH_DYNLIBMODULE
if(fptr == &dynlib_inplace_cb_query_response)
return 1;
911,
912,
913,
+914,
+915,
989,
990,
991,
2256,
2257,
2258,
+2259,
2260,
2261,
2262,
2366,
2367,
2368,
+2369,
2370,
2372,
2378,
8403,
8416,
8417,
+8433,
8442,
8443,
8444,
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)
+ if(!str || (qstate->env->cfg->val_log_level < 2 &&
+ !qstate->env->cfg->log_servfail)) {
return;
+ }
p = (struct errinf_strlist*)regional_alloc(qstate->region, sizeof(*p));
if(!p) {
log_err("malloc failure in validator-error-info string");
return p;
}
+/* Try to find the latest (most specific) dnssec failure */
sldns_ede_code errinf_to_reason_bogus(struct module_qstate* qstate)
{
struct errinf_strlist* s;
+ sldns_ede_code ede = LDNS_EDE_NONE;
for(s=qstate->errinf; s; s=s->next) {
- if (s->reason_bogus != LDNS_EDE_NONE) {
- return s->reason_bogus;
- }
+ if(s->reason_bogus == LDNS_EDE_NONE) continue;
+ if(ede != LDNS_EDE_NONE
+ && ede != LDNS_EDE_DNSSEC_BOGUS
+ && s->reason_bogus == LDNS_EDE_DNSSEC_BOGUS) continue;
+ ede = s->reason_bogus;
}
- return LDNS_EDE_NONE;
+ return ede;
}
char* errinf_to_str_servfail(struct module_qstate* qstate)
/** if this is a validation recursion query that does not get
* validation itself */
int is_valrec;
+#ifdef CLIENT_SUBNET
+ /** the client network address is needed for the client-subnet option
+ * when prefetching, but we can't use reply_list in mesh_info, because
+ * we don't want to send a reply for the internal query. */
+ struct sockaddr_storage client_addr;
+#endif
/** comm_reply contains server replies */
struct comm_reply* reply;
* those servers. By comparing expiry time with qstarttime for type NS.
*/
time_t qstarttime;
+ /** whether a message from cachedb will be used for the reply */
+ int is_cachedb_answer;
/**
* Attributes of clients that share the qstate that may affect IP-based
* 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.
+ * Check the sldns_ede_code of the qstate->errinf.
* @param qstate: query state.
- * @return LDNS_EDE_DNSSEC_BOGUS by default, or the first explicitly set
- * sldns_ede_code.
+ * @return the latest explicitly set sldns_ede_code or LDNS_EDE_NONE.
*/
sldns_ede_code errinf_to_reason_bogus(struct module_qstate* qstate);
return match;
}
-void
-addr_to_str(struct sockaddr_storage* addr, socklen_t addrlen,
+void
+addr_to_str(struct sockaddr_storage* addr, socklen_t addrlen,
char* buf, size_t len)
{
int af = (int)((struct sockaddr_in*)addr)->sin_family;
}
}
-int
+int
+prefixnet_is_nat64(int prefixnet)
+{
+ return (prefixnet == 32 || prefixnet == 40 ||
+ prefixnet == 48 || prefixnet == 56 ||
+ prefixnet == 64 || prefixnet == 96);
+}
+
+void
+addr_to_nat64(const struct sockaddr_storage* addr,
+ const struct sockaddr_storage* nat64_prefix,
+ socklen_t nat64_prefixlen, int nat64_prefixnet,
+ struct sockaddr_storage* nat64_addr, socklen_t* nat64_addrlen)
+{
+ struct sockaddr_in *sin = (struct sockaddr_in *)addr;
+ struct sockaddr_in6 *sin6;
+ uint8_t *v4_byte;
+
+ /* This needs to be checked by the caller */
+ log_assert(addr->ss_family == AF_INET);
+ /* Current usage is only from config values; prefix lengths enforced
+ * during config validation */
+ log_assert(prefixnet_is_nat64(nat64_prefixnet));
+
+ *nat64_addr = *nat64_prefix;
+ *nat64_addrlen = nat64_prefixlen;
+
+ sin6 = (struct sockaddr_in6 *)nat64_addr;
+ sin6->sin6_flowinfo = 0;
+ sin6->sin6_port = sin->sin_port;
+
+ nat64_prefixnet = nat64_prefixnet / 8;
+
+ v4_byte = (uint8_t *)&sin->sin_addr.s_addr;
+ for(int i = 0; i < 4; i++) {
+ if(nat64_prefixnet == 8) {
+ /* bits 64...71 are MBZ */
+ sin6->sin6_addr.s6_addr[nat64_prefixnet++] = 0;
+ }
+ sin6->sin6_addr.s6_addr[nat64_prefixnet++] = *v4_byte++;
+ }
+}
+
+int
addr_is_ip4mapped(struct sockaddr_storage* addr, socklen_t addrlen)
{
/* prefix for ipv4 into ipv6 mapping is ::ffff:x.x.x.x */
log_crypto_err("could not set cipher list with SSL_CTX_set_cipher_list");
}
#endif
+#if defined(SSL_OP_IGNORE_UNEXPECTED_EOF)
+ /* ignore errors when peers do not send the mandatory close_notify
+ * alert on shutdown.
+ * Relevant for openssl >= 3 */
+ if((SSL_CTX_set_options(ctx, SSL_OP_IGNORE_UNEXPECTED_EOF) &
+ SSL_OP_IGNORE_UNEXPECTED_EOF) != SSL_OP_IGNORE_UNEXPECTED_EOF) {
+ log_crypto_err("could not set SSL_OP_IGNORE_UNEXPECTED_EOF");
+ return 0;
+ }
+#endif
if((SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE) &
SSL_OP_CIPHER_SERVER_PREFERENCE) !=
SSL_CTX_free(ctx);
return 0;
}
+#endif
+#if defined(SSL_OP_IGNORE_UNEXPECTED_EOF)
+ /* ignore errors when peers do not send the mandatory close_notify
+ * alert on shutdown.
+ * Relevant for openssl >= 3 */
+ if((SSL_CTX_set_options(ctx, SSL_OP_IGNORE_UNEXPECTED_EOF) &
+ SSL_OP_IGNORE_UNEXPECTED_EOF) != SSL_OP_IGNORE_UNEXPECTED_EOF) {
+ log_crypto_err("could not set SSL_OP_IGNORE_UNEXPECTED_EOF");
+ SSL_CTX_free(ctx);
+ return 0;
+ }
#endif
if(key && key[0]) {
if(!SSL_CTX_use_certificate_chain_file(ctx, pem)) {
void addr_to_str(struct sockaddr_storage* addr, socklen_t addrlen,
char* buf, size_t len);
+/**
+ * Check if the prefix network length is one of the allowed 32, 40, 48, 56, 64,
+ * or 96.
+ * @param prefixnet: prefix network length to check.
+ * @return 1 on success, 0 on failure.
+ */
+int prefixnet_is_nat64(int prefixnet);
+
+/**
+ * Create a NAT64 address from a given address (needs to be IPv4) and a given
+ * NAT64 prefix. The NAT64 prefix net needs to be one of 32, 40, 48, 56, 64, 96.
+ * @param addr: IPv4 address.
+ * @param nat64_prefix: NAT64 prefix.
+ * @param nat64_prefixlen: NAT64 prefix len.
+ * @param nat64_prefixnet: NAT64 prefix mask.
+ * @param nat64_addr: the resulting NAT64 address.
+ * @param nat64_addrlen: the resulting NAT64 address length.
+ */
+void addr_to_nat64(const struct sockaddr_storage* addr,
+ const struct sockaddr_storage* nat64_prefix,
+ socklen_t nat64_prefixlen, int nat64_prefixnet,
+ struct sockaddr_storage* nat64_addr, socklen_t* nat64_addrlen);
+
/**
* See if sockaddr is an ipv6 mapped ipv4 address, "::ffff:0.0.0.0"
* @param addr: address
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
* This software is open source.
- *
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
- *
+ *
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
- *
+ *
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
- *
+ *
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
- *
+ *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
#include "util/tcp_conn_limit.h"
#include "util/fptr_wlist.h"
#include "util/proxy_protocol.h"
+#include "util/timeval_func.h"
#include "sldns/pkthdr.h"
#include "sldns/sbuffer.h"
#include "sldns/str2wire.h"
#ifdef HAVE_OPENSSL_ERR_H
#include <openssl/err.h>
#endif
-
+#ifdef HAVE_LINUX_NET_TSTAMP_H
+#include <linux/net_tstamp.h>
+#endif
/* -------- Start of local definitions -------- */
/** if CMSG_ALIGN is not defined on this platform, a workaround */
#ifndef CMSG_ALIGN
/** timeout in millisec to wait for write to unblock, packets dropped after.*/
#define SEND_BLOCKED_WAIT_TIMEOUT 200
+/** Let's make timestamping code cleaner and redefine SO_TIMESTAMP* */
+#ifndef SO_TIMESTAMP
+#define SO_TIMESTAMP 29
+#endif
+#ifndef SO_TIMESTAMPNS
+#define SO_TIMESTAMPNS 35
+#endif
+#ifndef SO_TIMESTAMPING
+#define SO_TIMESTAMPING 37
+#endif
/**
* The internal event structure for keeping ub_event info for the event.
* Possibly other structures (list, tree) this is part of.
/* -------- End of local definitions -------- */
-struct comm_base*
+struct comm_base*
comm_base_create(int sigs)
{
struct comm_base* b = (struct comm_base*)calloc(1,
return b;
}
-void
+void
comm_base_delete(struct comm_base* b)
{
if(!b)
free(b);
}
-void
+void
comm_base_delete_no_base(struct comm_base* b)
{
if(!b)
free(b);
}
-void
+void
comm_base_timept(struct comm_base* b, time_t** tt, struct timeval** tv)
{
*tt = &b->eb->secs;
*tv = &b->eb->now;
}
-void
+void
comm_base_dispatch(struct comm_base* b)
{
int retval;
(struct sockaddr_storage*)addr, addrlen);
return 0;
} else if((size_t)sent != sldns_buffer_remaining(packet)) {
- log_err("sent %d in place of %d bytes",
+ log_err("sent %d in place of %d bytes",
(int)sent, (int)sldns_buffer_remaining(packet));
return 0;
}
if(r->srctype == 6) {
#ifdef IPV6_PKTINFO
char buf[1024];
- if(inet_ntop(AF_INET6, &r->pktinfo.v6info.ipi6_addr,
+ if(inet_ntop(AF_INET6, &r->pktinfo.v6info.ipi6_addr,
buf, (socklen_t)sizeof(buf)) == 0) {
(void)strlcpy(buf, "(inet_ntop error)", sizeof(buf));
}
} else if(r->srctype == 4) {
#ifdef IP_PKTINFO
char buf1[1024], buf2[1024];
- if(inet_ntop(AF_INET, &r->pktinfo.v4info.ipi_addr,
+ if(inet_ntop(AF_INET, &r->pktinfo.v4info.ipi_addr,
buf1, (socklen_t)sizeof(buf1)) == 0) {
(void)strlcpy(buf1, "(inet_ntop error)", sizeof(buf1));
}
buf1[sizeof(buf1)-1]=0;
#ifdef HAVE_STRUCT_IN_PKTINFO_IPI_SPEC_DST
- if(inet_ntop(AF_INET, &r->pktinfo.v4info.ipi_spec_dst,
+ if(inet_ntop(AF_INET, &r->pktinfo.v4info.ipi_spec_dst,
buf2, (socklen_t)sizeof(buf2)) == 0) {
(void)strlcpy(buf2, "(inet_ntop error)", sizeof(buf2));
}
buf1, buf2);
#elif defined(IP_RECVDSTADDR)
char buf1[1024];
- if(inet_ntop(AF_INET, &r->pktinfo.v4addr,
+ if(inet_ntop(AF_INET, &r->pktinfo.v4addr,
buf1, (socklen_t)sizeof(buf1)) == 0) {
(void)strlcpy(buf1, "(inet_ntop error)", sizeof(buf1));
}
/** send a UDP reply over specified interface*/
static int
comm_point_send_udp_msg_if(struct comm_point *c, sldns_buffer* packet,
- struct sockaddr* addr, socklen_t addrlen, struct comm_reply* r)
+ struct sockaddr* addr, socklen_t addrlen, struct comm_reply* r)
{
#if defined(AF_INET6) && defined(IPV6_PKTINFO) && defined(HAVE_SENDMSG)
ssize_t sent;
cmsg_data = CMSG_DATA(cmsg);
((struct in_pktinfo *) cmsg_data)->ipi_ifindex = 0;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
+ /* zero the padding bytes inserted by the CMSG_LEN */
+ if(sizeof(struct in_pktinfo) < cmsg->cmsg_len)
+ memset(((uint8_t*)(CMSG_DATA(cmsg))) +
+ sizeof(struct in_pktinfo), 0, cmsg->cmsg_len
+ - sizeof(struct in_pktinfo));
#elif defined(IP_SENDSRCADDR)
msg.msg_controllen = CMSG_SPACE(sizeof(struct in_addr));
log_assert(msg.msg_controllen <= sizeof(control.buf));
memmove(CMSG_DATA(cmsg), &r->pktinfo.v4addr,
sizeof(struct in_addr));
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
+ /* zero the padding bytes inserted by the CMSG_LEN */
+ if(sizeof(struct in_addr) < cmsg->cmsg_len)
+ memset(((uint8_t*)(CMSG_DATA(cmsg))) +
+ sizeof(struct in_addr), 0, cmsg->cmsg_len
+ - sizeof(struct in_addr));
#else
verbose(VERB_ALGO, "no IP_PKTINFO or IP_SENDSRCADDR");
msg.msg_control = NULL;
cmsg_data = CMSG_DATA(cmsg);
((struct in6_pktinfo *) cmsg_data)->ipi6_ifindex = 0;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+ /* zero the padding bytes inserted by the CMSG_LEN */
+ if(sizeof(struct in6_pktinfo) < cmsg->cmsg_len)
+ memset(((uint8_t*)(CMSG_DATA(cmsg))) +
+ sizeof(struct in6_pktinfo), 0, cmsg->cmsg_len
+ - sizeof(struct in6_pktinfo));
} else {
/* try to pass all 0 to use default route */
msg.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
cmsg->cmsg_type = IPV6_PKTINFO;
memset(CMSG_DATA(cmsg), 0, sizeof(struct in6_pktinfo));
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+ /* zero the padding bytes inserted by the CMSG_LEN */
+ if(sizeof(struct in6_pktinfo) < cmsg->cmsg_len)
+ memset(((uint8_t*)(CMSG_DATA(cmsg))) +
+ sizeof(struct in6_pktinfo), 0, cmsg->cmsg_len
+ - sizeof(struct in6_pktinfo));
}
#endif /* S_SPLINT_S */
- if(verbosity >= VERB_ALGO)
+ if(verbosity >= VERB_ALGO && r->srctype != 0)
p_ancil("send_udp over interface", r);
sent = sendmsg(c->fd, &msg, 0);
if(sent == -1) {
if(!udp_send_errno_needs_log(addr, addrlen))
return 0;
verbose(VERB_OPS, "sendmsg failed: %s", strerror(errno));
- log_addr(VERB_OPS, "remote address is",
+ log_addr(VERB_OPS, "remote address is",
(struct sockaddr_storage*)addr, addrlen);
#ifdef __NetBSD__
/* netbsd 7 has IP_PKTINFO for recv but not send */
#endif
return 0;
} else if((size_t)sent != sldns_buffer_remaining(packet)) {
- log_err("sent %d in place of %d bytes",
+ log_err("sent %d in place of %d bytes",
(int)sent, (int)sldns_buffer_remaining(packet));
return 0;
}
/* We are reading a whole packet;
* Move the rest of the data to overwrite the PROXYv2 header */
/* XXX can we do better to avoid memmove? */
- memmove(header, ((void*)header)+size,
+ memmove(header, ((char*)header)+size,
sldns_buffer_limit(buf)-size);
sldns_buffer_set_limit(buf, sldns_buffer_limit(buf)-size);
}
return 1;
}
-void
+void
comm_point_udp_ancil_callback(int fd, short event, void* arg)
{
#if defined(AF_INET6) && defined(IPV6_PKTINFO) && defined(HAVE_RECVMSG)
#ifndef S_SPLINT_S
struct cmsghdr* cmsg;
#endif /* S_SPLINT_S */
+#ifdef HAVE_LINUX_NET_TSTAMP_H
+ struct timespec *ts;
+#endif /* HAVE_LINUX_NET_TSTAMP_H */
rep.c = (struct comm_point*)arg;
log_assert(rep.c->type == comm_udp);
ub_comm_base_now(rep.c->ev->base);
for(i=0; i<NUM_UDP_PER_SELECT; i++) {
sldns_buffer_clear(rep.c->buffer);
+ timeval_clear(&rep.c->recv_tv);
rep.remote_addrlen = (socklen_t)sizeof(rep.remote_addr);
log_assert(fd != -1);
log_assert(sldns_buffer_remaining(rep.c->buffer) > 0);
sizeof(struct in_addr));
break;
#endif /* IP_PKTINFO or IP_RECVDSTADDR */
+#ifdef HAVE_LINUX_NET_TSTAMP_H
+ } else if( cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SO_TIMESTAMPNS) {
+ ts = (struct timespec *)CMSG_DATA(cmsg);
+ TIMESPEC_TO_TIMEVAL(&rep.c->recv_tv, ts);
+ } else if( cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SO_TIMESTAMPING) {
+ ts = (struct timespec *)CMSG_DATA(cmsg);
+ TIMESPEC_TO_TIMEVAL(&rep.c->recv_tv, ts);
+ } else if( cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SO_TIMESTAMP) {
+ memmove(&rep.c->recv_tv, CMSG_DATA(cmsg), sizeof(struct timeval));
+#endif /* HAVE_LINUX_NET_TSTAMP_H */
}
}
- if(verbosity >= VERB_ALGO)
+
+ if(verbosity >= VERB_ALGO && rep.srctype != 0)
p_ancil("receive_udp on interface", &rep);
#endif /* S_SPLINT_S */
#endif /* AF_INET6 && IPV6_PKTINFO && HAVE_RECVMSG */
}
-void
+void
comm_point_udp_callback(int fd, short event, void* arg)
{
struct comm_reply rep;
rep.remote_addrlen = (socklen_t)sizeof(rep.remote_addr);
log_assert(fd != -1);
log_assert(sldns_buffer_remaining(rep.c->buffer) > 0);
- rcv = recvfrom(fd, (void*)sldns_buffer_begin(rep.c->buffer),
+ rcv = recvfrom(fd, (void*)sldns_buffer_begin(rep.c->buffer),
sldns_buffer_remaining(rep.c->buffer), MSG_DONTWAIT,
(struct sockaddr*)&rep.remote_addr, &rep.remote_addrlen);
if(rcv == -1) {
#ifndef USE_WINSOCK
if(errno != EAGAIN && errno != EINTR
&& udp_recv_needs_log(errno))
- log_err("recvfrom %d failed: %s",
+ log_err("recvfrom %d failed: %s",
fd, strerror(errno));
#else
if(WSAGetLastError() != WSAEINPROGRESS &&
/** Use a new tcp handler for new query fd, set to read query */
static void
-setup_tcp_handler(struct comm_point* c, int fd, int cur, int max)
+setup_tcp_handler(struct comm_point* c, int fd, int cur, int max)
{
int handler_usage;
log_assert(c->type == comm_tcp || c->type == comm_http);
/* EINTR is signal interrupt. others are closed connection. */
if( errno == EINTR || errno == EAGAIN
#ifdef EWOULDBLOCK
- || errno == EWOULDBLOCK
+ || errno == EWOULDBLOCK
#endif
#ifdef ECONNABORTED
- || errno == ECONNABORTED
+ || errno == ECONNABORTED
#endif
#ifdef EPROTO
|| errno == EPROTO
#endif /* HAVE_NGHTTP2 */
-void
+void
comm_point_tcp_accept_callback(int fd, short event, void* arg)
{
struct comm_point* c = (struct comm_point*)arg, *c_hdl;
log_err("in comm_point_tcp_handle_read buffer_remaining is "
"not > 0 as expected, continuing with (harmless) 0 "
"length recv");
- r = recv(fd, (void*)sldns_buffer_current(c->buffer),
+ r = recv(fd, (void*)sldns_buffer_current(c->buffer),
sldns_buffer_remaining(c->buffer), MSG_DONTWAIT);
if(r == 0) {
if(c->tcp_req_info)
return 0;
}
-/**
- * Handle tcp writing callback.
+/**
+ * Handle tcp writing callback.
* @param fd: file descriptor of socket.
* @param c: comm point to write buffer out of.
* @return: 0 on error
/* from Stevens, unix network programming, vol1, 3rd ed, p450*/
int error = 0;
socklen_t len = (socklen_t)sizeof(error);
- if(getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&error,
+ if(getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&error,
&len) < 0){
#ifndef USE_WINSOCK
error = errno; /* on solaris errno is error */
return ssl_handle_it(c, 1);
#ifdef USE_MSG_FASTOPEN
- /* Only try this on first use of a connection that uses tfo,
+ /* Only try this on first use of a connection that uses tfo,
otherwise fall through to normal write */
/* Also, TFO support on WINDOWS not implemented at the moment */
if(c->tcp_do_fastopen == 1) {
if(WSAGetLastError() == WSAEWOULDBLOCK) {
ub_winsock_tcp_wouldblock(c->ev->ev,
UB_EV_WRITE);
- return 1;
+ return 1;
}
if(WSAGetLastError() == WSAECONNRESET && verbosity < 2)
return 0; /* silence reset by peer */
return 1;
if(WSAGetLastError() == WSAEWOULDBLOCK) {
ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_WRITE);
- return 1;
+ return 1;
}
if(WSAGetLastError() == WSAECONNRESET && verbosity < 2)
return 0; /* silence reset by peer */
if((!c->tcp_write_and_read && sldns_buffer_remaining(buffer) == 0) || (c->tcp_write_and_read && c->tcp_write_byte_count == c->tcp_write_pkt_len + 2)) {
tcp_callback_writer(c);
}
-
+
return 1;
}
-/** read again to drain buffers when there could be more to read */
-static void
+/** read again to drain buffers when there could be more to read, returns 0
+ * on failure which means the comm point is closed. */
+static int
tcp_req_info_read_again(int fd, struct comm_point* c)
{
while(c->tcp_req_info->read_again) {
if(!c->tcp_do_close) {
fptr_ok(fptr_whitelist_comm_point(
c->callback));
- (void)(*c->callback)(c, c->cb_arg,
+ (void)(*c->callback)(c, c->cb_arg,
NETEVENT_CLOSED, NULL);
}
- return;
+ return 0;
}
}
+ return 1;
}
/** read again to drain buffers when there could be more to read */
}
}
-void
+void
comm_point_tcp_handle_callback(int fd, short event, void* arg)
{
struct comm_point* c = (struct comm_point*)arg;
log_assert(c->type == comm_tcp);
ub_comm_base_now(c->ev->base);
+ if(c->fd == -1 || c->fd != fd)
+ return; /* duplicate event, but commpoint closed. */
+
#ifdef USE_DNSCRYPT
/* Initialize if this is a dnscrypt socket */
if(c->tcp_parent) {
}
return;
}
- if(has_tcpq && c->tcp_req_info && c->tcp_req_info->read_again)
- tcp_req_info_read_again(fd, c);
+ if(has_tcpq && c->tcp_req_info && c->tcp_req_info->read_again) {
+ if(!tcp_req_info_read_again(fd, c))
+ return;
+ }
if(moreread && *moreread)
tcp_more_read_again(fd, c);
return;
}
return;
}
- if(has_tcpq && c->tcp_req_info && c->tcp_req_info->read_again)
- tcp_req_info_read_again(fd, c);
+ if(has_tcpq && c->tcp_req_info && c->tcp_req_info->read_again) {
+ if(!tcp_req_info_read_again(fd, c))
+ return;
+ }
if(morewrite && *morewrite)
tcp_more_write_again(fd, c);
return;
{
ssize_t r;
log_assert(sldns_buffer_remaining(c->buffer) > 0);
- r = recv(fd, (void*)sldns_buffer_current(c->buffer),
+ r = recv(fd, (void*)sldns_buffer_current(c->buffer),
sldns_buffer_remaining(c->buffer), MSG_DONTWAIT);
if(r == 0) {
return 0;
/* return and wait to read more */
return 1;
}
-
+
/* callback of http reader for a new part of the data */
c->http_stored = 0;
sldns_buffer_set_position(c->buffer, 0);
/* from Stevens, unix network programming, vol1, 3rd ed, p450*/
int error = 0;
socklen_t len = (socklen_t)sizeof(error);
- if(getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&error,
+ if(getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&error,
&len) < 0){
#ifndef USE_WINSOCK
error = errno; /* on solaris errno is error */
{
ssize_t r;
log_assert(sldns_buffer_remaining(c->buffer) > 0);
- r = send(fd, (void*)sldns_buffer_current(c->buffer),
+ r = send(fd, (void*)sldns_buffer_current(c->buffer),
sldns_buffer_remaining(c->buffer), 0);
if(r == -1) {
#ifndef USE_WINSOCK
return 1;
if(WSAGetLastError() == WSAEWOULDBLOCK) {
ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_WRITE);
- return 1;
+ return 1;
}
#endif
log_err_addr("http send r", sock_strerror(errno),
#endif
}
-/**
- * Handle http writing callback.
+/**
+ * Handle http writing callback.
* @param fd: file descriptor of socket.
* @param c: comm point to write buffer out of.
* @return: 0 on error
return 1;
}
-void
+void
comm_point_http_handle_callback(int fd, short event, void* arg)
{
struct comm_point* c = (struct comm_point*)arg;
if(event&UB_EV_READ) {
if(!comm_point_tcp_handle_read(fd, c, 1)) {
fptr_ok(fptr_whitelist_comm_point(c->callback));
- (void)(*c->callback)(c, c->cb_arg, NETEVENT_CLOSED,
+ (void)(*c->callback)(c, c->cb_arg, NETEVENT_CLOSED,
NULL);
}
return;
log_err("Ignored event %d for localhdl.", event);
}
-void comm_point_raw_handle_callback(int ATTR_UNUSED(fd),
+void comm_point_raw_handle_callback(int ATTR_UNUSED(fd),
short event, void* arg)
{
struct comm_point* c = (struct comm_point*)arg;
int err = NETEVENT_NOERROR;
log_assert(c->type == comm_raw);
ub_comm_base_now(c->ev->base);
-
+
if(event&UB_EV_TIMEOUT)
err = NETEVENT_TIMEOUT;
fptr_ok(fptr_whitelist_comm_point_raw(c->callback));
(void)(*c->callback)(c, c->cb_arg, err, NULL);
}
-struct comm_point*
+struct comm_point*
comm_point_create_udp(struct comm_base *base, int fd, sldns_buffer* buffer,
int pp2_enabled, comm_point_callback_type* callback,
void* callback_arg, struct unbound_socket* socket)
evbits = UB_EV_READ | UB_EV_PERSIST;
/* ub_event stuff */
c->ev->ev = ub_event_new(base->eb->base, c->fd, evbits,
+#ifdef USE_WINSOCK
comm_point_udp_callback, c);
+#else
+ comm_point_udp_ancil_callback, c);
+#endif
if(c->ev->ev == NULL) {
log_err("could not baseset udp event");
comm_point_delete(c);
return c;
}
-struct comm_point*
+struct comm_point*
comm_point_create_udp_ancil(struct comm_base *base, int fd,
sldns_buffer* buffer, int pp2_enabled,
comm_point_callback_type* callback, void* callback_arg, struct unbound_socket* socket)
return c;
}
-static struct comm_point*
-comm_point_create_tcp_handler(struct comm_base *base,
+static struct comm_point*
+comm_point_create_tcp_handler(struct comm_base *base,
struct comm_point* parent, size_t bufsize,
struct sldns_buffer* spoolbuf, comm_point_callback_type* callback,
void* callback_arg, struct unbound_socket* socket)
return c;
}
-static struct comm_point*
-comm_point_create_http_handler(struct comm_base *base,
+static struct comm_point*
+comm_point_create_http_handler(struct comm_base *base,
struct comm_point* parent, size_t bufsize, int harden_large_queries,
uint32_t http_max_streams, char* http_endpoint,
comm_point_callback_type* callback, void* callback_arg,
return NULL;
}
#endif
-
+
/* add to parent free list */
c->tcp_free = parent->tcp_free;
parent->tcp_free = c;
return c;
}
-struct comm_point*
+struct comm_point*
comm_point_create_tcp(struct comm_base *base, int fd, int num,
int idle_timeout, int harden_large_queries,
uint32_t http_max_streams, char* http_endpoint,
return NULL;
}
}
-
+
return c;
}
-struct comm_point*
+struct comm_point*
comm_point_create_tcp_out(struct comm_base *base, size_t bufsize,
comm_point_callback_type* callback, void* callback_arg)
{
return c;
}
-struct comm_point*
+struct comm_point*
comm_point_create_http_out(struct comm_base *base, size_t bufsize,
comm_point_callback_type* callback, void* callback_arg,
sldns_buffer* temp)
return c;
}
-struct comm_point*
+struct comm_point*
comm_point_create_local(struct comm_base *base, int fd, size_t bufsize,
comm_point_callback_type* callback, void* callback_arg)
{
return c;
}
-struct comm_point*
-comm_point_create_raw(struct comm_base* base, int fd, int writing,
+struct comm_point*
+comm_point_create_raw(struct comm_base* base, int fd, int writing,
comm_point_callback_type* callback, void* callback_arg)
{
struct comm_point* c = (struct comm_point*)calloc(1,
return c;
}
-void
+void
comm_point_close(struct comm_point* c)
{
if(!c)
tcp_req_info_clear(c->tcp_req_info);
if(c->h2_session)
http2_session_server_delete(c->h2_session);
+ /* stop the comm point from reading or writing after it is closed. */
+ if(c->tcp_more_read_again && *c->tcp_more_read_again)
+ *c->tcp_more_read_again = 0;
+ if(c->tcp_more_write_again && *c->tcp_more_write_again)
+ *c->tcp_more_write_again = 0;
/* close fd after removing from event lists, or epoll.. is messed up */
if(c->fd != -1 && !c->do_not_close) {
c->fd = -1;
}
-void
+void
comm_point_delete(struct comm_point* c)
{
- if(!c)
+ if(!c)
return;
if((c->type == comm_tcp || c->type == comm_http) && c->ssl) {
#ifdef HAVE_SSL
free(c);
}
-void
+void
comm_point_send_reply(struct comm_reply *repinfo)
{
struct sldns_buffer* buffer;
}
}
-void
+void
comm_point_drop_reply(struct comm_reply* repinfo)
{
if(!repinfo)
reclaim_tcp_handler(repinfo->c);
}
-void
+void
comm_point_stop_listening(struct comm_point* c)
{
verbose(VERB_ALGO, "comm point stop listening %d", c->fd);
}
}
-void
+void
comm_point_start_listening(struct comm_point* c, int newfd, int msec)
{
- verbose(VERB_ALGO, "comm point start listening %d (%d msec)",
+ verbose(VERB_ALGO, "comm point start listening %d (%d msec)",
c->fd==-1?newfd:c->fd, msec);
if(c->type == comm_tcp_accept && !c->tcp_free) {
/* no use to start listening no free slots. */
size_t comm_point_get_mem(struct comm_point* c)
{
size_t s;
- if(!c)
+ if(!c)
return 0;
s = sizeof(*c) + sizeof(*c->ev);
- if(c->timeout)
+ if(c->timeout)
s += sizeof(*c->timeout);
if(c->type == comm_tcp || c->type == comm_local) {
s += sizeof(*c->buffer) + sldns_buffer_capacity(c->buffer);
return s;
}
-struct comm_timer*
+struct comm_timer*
comm_timer_create(struct comm_base* base, void (*cb)(void*), void* cb_arg)
{
struct internal_timer *tm = (struct internal_timer*)calloc(1,
tm->base = base;
tm->super.callback = cb;
tm->super.cb_arg = cb_arg;
- tm->ev = ub_event_new(base->eb->base, -1, UB_EV_TIMEOUT,
+ tm->ev = ub_event_new(base->eb->base, -1, UB_EV_TIMEOUT,
comm_timer_callback, &tm->super);
if(tm->ev == NULL) {
log_err("timer_create: event_base_set failed.");
return &tm->super;
}
-void
+void
comm_timer_disable(struct comm_timer* timer)
{
if(!timer)
timer->ev_timer->enabled = 0;
}
-void
+void
comm_timer_set(struct comm_timer* timer, struct timeval* tv)
{
log_assert(tv);
timer->ev_timer->enabled = 1;
}
-void
+void
comm_timer_delete(struct comm_timer* timer)
{
if(!timer)
free(timer->ev_timer);
}
-void
+void
comm_timer_callback(int ATTR_UNUSED(fd), short event, void* arg)
{
struct comm_timer* tm = (struct comm_timer*)arg;
(*tm->callback)(tm->cb_arg);
}
-int
+int
comm_timer_is_set(struct comm_timer* timer)
{
return (int)timer->ev_timer->enabled;
}
-size_t
+size_t
comm_timer_get_mem(struct comm_timer* ATTR_UNUSED(timer))
{
return sizeof(struct internal_timer);
}
-struct comm_signal*
+struct comm_signal*
comm_signal_create(struct comm_base* base,
void (*callback)(int, void*), void* cb_arg)
{
return com;
}
-void
+void
comm_signal_callback(int sig, short event, void* arg)
{
struct comm_signal* comsig = (struct comm_signal*)arg;
(*comsig->callback)(sig, comsig->cb_arg);
}
-int
+int
comm_signal_bind(struct comm_signal* comsig, int sig)
{
- struct internal_signal* entry = (struct internal_signal*)calloc(1,
+ struct internal_signal* entry = (struct internal_signal*)calloc(1,
sizeof(struct internal_signal));
if(!entry) {
log_err("malloc failed");
return 1;
}
-void
+void
comm_signal_delete(struct comm_signal* comsig)
{
struct internal_signal* p, *np;
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
* This software is open source.
- *
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
- *
+ *
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
- *
+ *
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
- *
+ *
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
- *
+ *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
#ifndef NET_EVENT_H
#define NET_EVENT_H
+#include <sys/time.h>
#include "dnscrypt/dnscrypt.h"
#ifdef HAVE_NGHTTP2_NGHTTP2_H
#include <nghttp2/nghttp2.h>
enum listen_type;
/** callback from communication point function type */
-typedef int comm_point_callback_type(struct comm_point*, void*, int,
+typedef int comm_point_callback_type(struct comm_point*, void*, int,
struct comm_reply*);
/** to pass no_error to callback function */
/** to pass closed connection to callback function */
#define NETEVENT_CLOSED -1
/** to pass timeout happened to callback function */
-#define NETEVENT_TIMEOUT -2
+#define NETEVENT_TIMEOUT -2
/** to pass fallback from capsforID to callback function; 0x20 failed */
#define NETEVENT_CAPSFAIL -3
/** to pass done transfer to callback function; http file is complete */
socklen_t client_addrlen;
};
-/**
- * Communication point to the network
+/**
+ * Communication point to the network
* These behaviours can be accomplished by setting the flags
* and passing return values from the callback.
* udp frontside: called after readdone. sendafter.
int max_tcp_count;
/** current number of tcp handler in-use for this accept socket */
int cur_tcp_count;
- /** malloced array of tcp handlers for a tcp-accept,
+ /** malloced array of tcp handlers for a tcp-accept,
of size max_tcp_count. */
struct comm_point** tcp_handlers;
/** linked list of free tcp_handlers to use for new queries.
/** is this a UDP, TCP-accept or TCP socket. */
enum comm_point_type {
/** UDP socket - handle datagrams. */
- comm_udp,
+ comm_udp,
/** TCP accept socket - only creates handlers if readable. */
- comm_tcp_accept,
+ comm_tcp_accept,
/** TCP handler socket - handle byteperbyte readwrite. */
comm_tcp,
/** HTTP handler socket */
comm_local,
/** raw - not DNS format - for pipe readers and writers */
comm_raw
- }
+ }
/** variable with type of socket, UDP,TCP-accept,TCP,pipe */
type;
/** if set the connection is NOT closed on delete. */
int do_not_close;
- /** if set, the connection is closed on error, on timeout,
+ /** if set, the connection is closed on error, on timeout,
and after read/write completes. No callback is done. */
int tcp_do_close;
/** number of queries outstanding on this socket, used by
* outside network for udp ports */
int inuse;
-
+ /** the timestamp when the packet was received by the kernel */
+ struct timeval recv_tv;
/** callback when done.
tcp_accept does not get called back, is NULL then.
If a timeout happens, callback with timeout=1 is called.
- If an error happens, callback is called with error set
+ If an error happens, callback is called with error set
nonzero. If not NETEVENT_NOERROR, it is an errno value.
If the connection is closed (by remote end) then the
callback is called with error set to NETEVENT_CLOSED=-1.
- If a timeout happens on the connection, the error is set to
+ If a timeout happens on the connection, the error is set to
NETEVENT_TIMEOUT=-2.
The reply_info can be copied if the reply needs to happen at a
later time. It consists of a struct with commpoint and address.
Note the reply information is temporary and must be copied.
NULL is passed for_reply info, in cases where error happened.
- declare as:
+ declare as:
int my_callback(struct comm_point* c, void* my_arg, int error,
struct comm_reply *reply_info);
/**
* Create a new comm base.
- * @param sigs: if true it attempts to create a default loop for
+ * @param sigs: if true it attempts to create a default loop for
* signal handling.
* @return: the new comm base. NULL on error.
*/
struct comm_base* comm_base_create(int sigs);
/**
- * Create comm base that uses the given ub_event_base (underlying pluggable
+ * Create comm base that uses the given ub_event_base (underlying pluggable
* event mechanism pointer).
* @param base: underlying pluggable event base.
* @return: the new comm base. NULL on error.
* @return: the commpoint or NULL on error.
*/
struct comm_point* comm_point_create_local(struct comm_base* base,
- int fd, size_t bufsize,
+ int fd, size_t bufsize,
comm_point_callback_type* callback, void* callback_arg);
/**
* @return: the commpoint or NULL on error.
*/
struct comm_point* comm_point_create_raw(struct comm_base* base,
- int fd, int writing,
+ int fd, int writing,
comm_point_callback_type* callback, void* callback_arg);
/**
* @param cb_arg: user callback argument.
* @return: the new timer or NULL on error.
*/
-struct comm_timer* comm_timer_create(struct comm_base* base,
+struct comm_timer* comm_timer_create(struct comm_base* base,
void (*cb)(void*), void* cb_arg);
/**
* if -1, error message has been printed if necessary, simply drop
* out of the reading handler.
*/
-int comm_point_perform_accept(struct comm_point* c,
+int comm_point_perform_accept(struct comm_point* c,
struct sockaddr_storage* addr, socklen_t* addrlen);
/**** internal routines ****/
* This routine is published for checks and tests, and is only used internally.
* handle libevent callback for udp comm point.
* @param fd: file descriptor.
- * @param event: event bits from libevent:
+ * @param event: event bits from libevent:
* EV_READ, EV_WRITE, EV_SIGNAL, EV_TIMEOUT.
* @param arg: the comm_point structure.
*/
* This routine is published for checks and tests, and is only used internally.
* handle libevent callback for udp ancillary data comm point.
* @param fd: file descriptor.
- * @param event: event bits from libevent:
+ * @param event: event bits from libevent:
* EV_READ, EV_WRITE, EV_SIGNAL, EV_TIMEOUT.
* @param arg: the comm_point structure.
*/
* This routine is published for checks and tests, and is only used internally.
* handle libevent callback for tcp accept comm point
* @param fd: file descriptor.
- * @param event: event bits from libevent:
+ * @param event: event bits from libevent:
* EV_READ, EV_WRITE, EV_SIGNAL, EV_TIMEOUT.
* @param arg: the comm_point structure.
*/
* This routine is published for checks and tests, and is only used internally.
* handle libevent callback for tcp data comm point
* @param fd: file descriptor.
- * @param event: event bits from libevent:
+ * @param event: event bits from libevent:
* EV_READ, EV_WRITE, EV_SIGNAL, EV_TIMEOUT.
* @param arg: the comm_point structure.
*/
* This routine is published for checks and tests, and is only used internally.
* handle libevent callback for tcp data comm point
* @param fd: file descriptor.
- * @param event: event bits from libevent:
+ * @param event: event bits from libevent:
* EV_READ, EV_WRITE, EV_SIGNAL, EV_TIMEOUT.
* @param arg: the comm_point structure.
*/
* This routine is published for checks and tests, and is only used internally.
* handle libevent callback for timer comm.
* @param fd: file descriptor (always -1).
- * @param event: event bits from libevent:
+ * @param event: event bits from libevent:
* EV_READ, EV_WRITE, EV_SIGNAL, EV_TIMEOUT.
* @param arg: the comm_timer structure.
*/
* This routine is published for checks and tests, and is only used internally.
* handle libevent callback for signal comm.
* @param fd: file descriptor (used for the signal number).
- * @param event: event bits from libevent:
+ * @param event: event bits from libevent:
* EV_READ, EV_WRITE, EV_SIGNAL, EV_TIMEOUT.
* @param arg: the internal commsignal structure.
*/
* This routine is published for checks and tests, and is only used internally.
* libevent callback for AF_UNIX fds
* @param fd: file descriptor.
- * @param event: event bits from libevent:
+ * @param event: event bits from libevent:
* EV_READ, EV_WRITE, EV_SIGNAL, EV_TIMEOUT.
* @param arg: the comm_point structure.
*/
* This routine is published for checks and tests, and is only used internally.
* libevent callback for raw fd access.
* @param fd: file descriptor.
- * @param event: event bits from libevent:
+ * @param event: event bits from libevent:
* EV_READ, EV_WRITE, EV_SIGNAL, EV_TIMEOUT.
* @param arg: the comm_point structure.
*/
* This routine is published for checks and tests, and is only used internally.
* libevent callback for timeout on slow accept.
* @param fd: file descriptor.
- * @param event: event bits from libevent:
+ * @param event: event bits from libevent:
* EV_READ, EV_WRITE, EV_SIGNAL, EV_TIMEOUT.
* @param arg: the comm_point structure.
*/
table->num = 0;
table->space_used = 0;
table->space_max = maxmem;
+ table->max_collisions = 0;
table->array = calloc(table->size, sizeof(struct lruhash_bin));
if(!table->array) {
lock_quick_destroy(&table->lock);
struct lruhash_entry*
bin_find_entry(struct lruhash* table,
- struct lruhash_bin* bin, hashvalue_type hash, void* key)
+ struct lruhash_bin* bin, hashvalue_type hash, void* key, size_t* collisions)
{
+ size_t c = 0;
struct lruhash_entry* p = bin->overflow_list;
while(p) {
if(p->hash == hash && table->compfunc(p->key, key) == 0)
- return p;
+ break;
+ c++;
p = p->overflow_next;
}
- return NULL;
+ if (collisions != NULL)
+ *collisions = c;
+ return p;
}
void
struct lruhash_bin* bin;
struct lruhash_entry* found, *reclaimlist=NULL;
size_t need_size;
+ size_t collisions;
fptr_ok(fptr_whitelist_hash_sizefunc(table->sizefunc));
fptr_ok(fptr_whitelist_hash_delkeyfunc(table->delkeyfunc));
fptr_ok(fptr_whitelist_hash_deldatafunc(table->deldatafunc));
lock_quick_lock(&bin->lock);
/* see if entry exists already */
- if(!(found=bin_find_entry(table, bin, hash, entry->key))) {
+ if(!(found=bin_find_entry(table, bin, hash, entry->key, &collisions))) {
/* if not: add to bin */
entry->overflow_next = bin->overflow_list;
bin->overflow_list = entry;
lru_front(table, entry);
table->num++;
+ if (table->max_collisions < collisions)
+ table->max_collisions = collisions;
table->space_used += need_size;
} else {
/* if so: update data - needs a writelock */
lock_quick_lock(&table->lock);
bin = &table->array[hash & table->size_mask];
lock_quick_lock(&bin->lock);
- if((entry=bin_find_entry(table, bin, hash, key)))
+ if((entry=bin_find_entry(table, bin, hash, key, NULL)))
lru_touch(table, entry);
lock_quick_unlock(&table->lock);
lock_quick_lock(&table->lock);
bin = &table->array[hash & table->size_mask];
lock_quick_lock(&bin->lock);
- if((entry=bin_find_entry(table, bin, hash, key))) {
+ if((entry=bin_find_entry(table, bin, hash, key, NULL))) {
bin_overflow_remove(bin, entry);
lru_remove(table, entry);
} else {
struct lruhash_bin* bin;
struct lruhash_entry* found, *reclaimlist = NULL;
size_t need_size;
+ size_t collisions;
fptr_ok(fptr_whitelist_hash_sizefunc(table->sizefunc));
fptr_ok(fptr_whitelist_hash_delkeyfunc(table->delkeyfunc));
fptr_ok(fptr_whitelist_hash_deldatafunc(table->deldatafunc));
lock_quick_lock(&bin->lock);
/* see if entry exists already */
- if ((found = bin_find_entry(table, bin, hash, entry->key)) != NULL) {
+ if ((found = bin_find_entry(table, bin, hash, entry->key, &collisions)) != NULL) {
/* if so: keep the existing data - acquire a writelock */
lock_rw_wrlock(&found->lock);
}
bin->overflow_list = entry;
lru_front(table, entry);
table->num++;
+ if (table->max_collisions < collisions)
+ table->max_collisions = collisions;
table->space_used += need_size;
/* return the entry that was presented, and lock it */
found = entry;
size_t space_used;
/** the amount of space the hash table is maximally allowed to use. */
size_t space_max;
+ /** the maximum collisions were detected during the lruhash_insert operations. */
+ size_t max_collisions;
};
/**
* @param bin: hash bin to look into.
* @param hash: hash value to look for.
* @param key: key to look for.
+ * @param collisions: how many collisions were found during the search.
* @return: the entry or NULL if not found.
*/
struct lruhash_entry* bin_find_entry(struct lruhash* table,
- struct lruhash_bin* bin, hashvalue_type hash, void* key);
+ struct lruhash_bin* bin, hashvalue_type hash, void* key, size_t* collisions);
/**
* Remove entry from bin overflow chain.
}
return cnt;
}
+
+void get_slabhash_stats(struct slabhash* sh, long long* num, long long* collisions)
+{
+ size_t slab, cnt = 0, max_collisions = 0;
+
+ for(slab=0; slab<sh->size; slab++) {
+ lock_quick_lock(&sh->array[slab]->lock);
+ cnt += sh->array[slab]->num;
+ if (max_collisions < sh->array[slab]->max_collisions) {
+ max_collisions = sh->array[slab]->max_collisions;
+ }
+ lock_quick_unlock(&sh->array[slab]->lock);
+ }
+ if (num != NULL)
+ *num = cnt;
+ if (collisions != NULL)
+ *collisions = max_collisions;
+}
*/
size_t count_slabhash_entries(struct slabhash* table);
+/**
+ * Retrieves number of items in slabhash and the current max collision level
+ * @param table: slabbed hash table.
+ * @param entries_count: where to save the current number of elements.
+ * @param max_collisions: where to save the current max collisions level.
+ */
+void get_slabhash_stats(struct slabhash* table,
+ long long* entries_count, long long* max_collisions);
+
/* --- test representation --- */
/** test structure contains test key */
struct slabhash_testkey {
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
* This software is open source.
- *
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
- *
+ *
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
- *
+ *
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
- *
+ *
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
- *
+ *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
#include <sys/types.h>
#include "util/timehist.h"
#include "util/log.h"
+#include "util/timeval_func.h"
/** special timestwo operation for time values in histogram setup */
static void
struct timehist* timehist_setup(void)
{
- struct timehist* hist = (struct timehist*)calloc(1,
+ struct timehist* hist = (struct timehist*)calloc(1,
sizeof(struct timehist));
if(!hist)
return NULL;
hist->num = NUM_BUCKETS_HIST;
- hist->buckets = (struct th_buck*)calloc(hist->num,
+ hist->buckets = (struct th_buck*)calloc(hist->num,
sizeof(struct th_buck));
if(!hist->buckets) {
free(hist);
hist->buckets[i].count = 0;
}
-/** histogram compare of time values */
-static int
-timeval_smaller(const struct timeval* x, const struct timeval* y)
-{
-#ifndef S_SPLINT_S
- if(x->tv_sec < y->tv_sec)
- return 1;
- else if(x->tv_sec == y->tv_sec) {
- if(x->tv_usec <= y->tv_usec)
- return 1;
- else return 0;
- }
- else return 0;
-#endif
-}
-
-
void timehist_insert(struct timehist* hist, struct timeval* tv)
{
size_t i;
return res;
}
-double
+double
timehist_quartile(struct timehist* hist, double q)
{
double lookfor, passed, res;
lookfor *= q;
passed = 0;
i = 0;
- while(i+1 < hist->num &&
+ while(i+1 < hist->num &&
passed+(double)hist->buckets[i].count < lookfor) {
passed += (double)hist->buckets[i++].count;
}
/* got the right bucket */
#ifndef S_SPLINT_S
- low = (double)hist->buckets[i].lower.tv_sec +
+ low = (double)hist->buckets[i].lower.tv_sec +
(double)hist->buckets[i].lower.tv_usec/1000000.;
- up = (double)hist->buckets[i].upper.tv_sec +
+ up = (double)hist->buckets[i].upper.tv_sec +
(double)hist->buckets[i].upper.tv_usec/1000000.;
#endif
res = (lookfor - passed)*(up-low)/((double)hist->buckets[i].count);
return low+res;
}
-void
+void
timehist_export(struct timehist* hist, long long* array, size_t sz)
{
size_t i;
array[i] = (long long)hist->buckets[i].count;
}
-void
+void
timehist_import(struct timehist* hist, long long* array, size_t sz)
{
size_t i;
#include "util/netevent.h"
#include "util/fptr_wlist.h"
#include "util/ub_event.h"
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#endif
#ifndef USE_WINSOCK
/* on unix */
return 1;
}
-/** perform a select() on the fd */
+/** perform poll() on the fd */
static int
pollit(int fd, struct timeval* t)
{
- fd_set r;
+ struct pollfd fds;
+ int pret;
+ int msec = -1;
+ memset(&fds, 0, sizeof(fds));
+ fds.fd = fd;
+ fds.events = POLLIN | POLLERR | POLLHUP;
#ifndef S_SPLINT_S
- FD_ZERO(&r);
- FD_SET(FD_SET_T fd, &r);
+ if(t)
+ msec = t->tv_sec*1000 + t->tv_usec/1000;
#endif
- if(select(fd+1, &r, NULL, NULL, t) == -1) {
+
+ pret = poll(&fds, 1, msec);
+
+ if(pret == -1)
+ return 0;
+ if(pret != 0)
+ return 1;
return 0;
- }
- errno = 0;
- return (int)(FD_ISSET(fd, &r));
}
int tube_poll(struct tube* tube)
int tube_wait_timeout(struct tube* tube, int msec)
{
- struct timeval t;
- int fd = tube->sr;
- fd_set r;
- t.tv_sec = msec/1000;
- t.tv_usec = (msec%1000)*1000;
-#ifndef S_SPLINT_S
- FD_ZERO(&r);
- FD_SET(FD_SET_T fd, &r);
-#endif
+ int ret = 0;
+
while(1) {
- if(select(fd+1, &r, NULL, NULL, &t) == -1) {
+ struct pollfd fds;
+ memset(&fds, 0, sizeof(fds));
+
+ fds.fd = tube->sr;
+ fds.events = POLLIN | POLLERR | POLLHUP;
+ ret = poll(&fds, 1, msec);
+
+ if(ret == -1) {
if(errno == EAGAIN || errno == EINTR)
continue;
return -1;
}
break;
}
- return (int)(FD_ISSET(fd, &r));
+
+ if(ret != 0)
+ return 1;
+ return 0;
}
int tube_read_fd(struct tube* tube)
if(tube->event == WSA_INVALID_EVENT) {
free(tube);
log_err("WSACreateEvent: %s", wsa_strerror(WSAGetLastError()));
+ return NULL;
}
if(!WSAResetEvent(tube->event)) {
log_err("WSAResetEvent: %s", wsa_strerror(WSAGetLastError()));
edns.opt_list_out = NULL;
edns.opt_list_inplace_cb_out = NULL;
edns.padding_block_size = 0;
+ edns.cookie_present = 0;
+ edns.cookie_valid = 0;
if(sldns_buffer_capacity(buf) < 65535)
edns.udp_size = (uint16_t)sldns_buffer_capacity(buf);
else edns.udp_size = 65535;
*/
#include "config.h"
#ifdef HAVE_OPENSSL_SSL_H
-#include "openssl/ssl.h"
+#include <openssl/ssl.h>
#define NSEC3_SHA_LEN SHA_DIGEST_LENGTH
#else
#define NSEC3_SHA_LEN 20
/* Matching NSEC, use to generate No Data answer. Not creating answers
* yet for No Data proven using wildcard. */
if(nsec && nsec_proves_nodata(nsec, qinfo, &nodata_wc) && !nodata_wc) {
+ /* do not create nodata answers for qtype ANY, it is a query
+ * type, not an rrtype to disprove. Nameerrors are useful for
+ * qtype ANY, in the else branch. */
+ if(qinfo->qtype == LDNS_RR_TYPE_ANY)
+ return NULL;
if(!(msg = dns_msg_create(qinfo->qname, qinfo->qname_len,
qinfo->qtype, qinfo->qclass, region, 2)))
return NULL;
/** check security status from cache or verify rrset, returns true if secure */
static int
-nsec_verify_rrset(struct module_env* env, struct val_env* ve,
- struct ub_packed_rrset_key* nsec, struct key_entry_key* kkey,
- char** reason, struct module_qstate* qstate)
+nsec_verify_rrset(struct module_env* env, struct val_env* ve,
+ struct ub_packed_rrset_key* nsec, struct key_entry_key* kkey,
+ char** reason, sldns_ede_code* reason_bogus,
+ struct module_qstate* qstate)
{
struct packed_rrset_data* d = (struct packed_rrset_data*)
nsec->entry.data;
if(d->security == sec_status_secure)
return 1;
d->security = val_verify_rrset_entry(env, ve, nsec, kkey, reason,
- NULL, LDNS_SECTION_AUTHORITY, qstate);
+ reason_bogus, LDNS_SECTION_AUTHORITY, qstate);
if(d->security == sec_status_secure) {
rrset_update_sec_status(env->rrset_cache, nsec, *env->now);
return 1;
val_nsec_prove_nodata_dsreply(struct module_env* env, struct val_env* ve,
struct query_info* qinfo, struct reply_info* rep,
struct key_entry_key* kkey, time_t* proof_ttl, char** reason,
- struct module_qstate* qstate)
+ sldns_ede_code* reason_bogus, struct module_qstate* qstate)
{
struct ub_packed_rrset_key* nsec = reply_find_rrset_section_ns(
rep, qinfo->qname, qinfo->qname_len, LDNS_RR_TYPE_NSEC,
* 1) this is a delegation point and there is no DS
* 2) this is not a delegation point */
if(nsec) {
- if(!nsec_verify_rrset(env, ve, nsec, kkey, reason, qstate)) {
+ if(!nsec_verify_rrset(env, ve, nsec, kkey, reason,
+ reason_bogus, qstate)) {
verbose(VERB_ALGO, "NSEC RRset for the "
"referral did not verify.");
return sec_status_bogus;
if(sec == sec_status_bogus) {
/* something was wrong. */
*reason = "NSEC does not prove absence of DS";
+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec;
} else if(sec == sec_status_insecure) {
/* this wasn't a delegation point. */
if(rep->rrsets[i]->rk.type != htons(LDNS_RR_TYPE_NSEC))
continue;
if(!nsec_verify_rrset(env, ve, rep->rrsets[i], kkey, reason,
- qstate)) {
+ reason_bogus, qstate)) {
verbose(VERB_ALGO, "NSEC for empty non-terminal "
"did not verify.");
+ *reason = "NSEC for empty non-terminal "
+ "did not verify.";
return sec_status_bogus;
}
if(nsec_proves_nodata(rep->rrsets[i], qinfo, &wc)) {
#ifndef VALIDATOR_VAL_NSEC_H
#define VALIDATOR_VAL_NSEC_H
#include "util/data/packed_rrset.h"
+#include "sldns/rrdef.h"
struct val_env;
struct module_env;
struct module_qstate;
* @param kkey: key entry to use for verification of signatures.
* @param proof_ttl: if secure, the TTL of how long this proof lasts.
* @param reason: string explaining why bogus.
+ * @param reason_bogus: relevant EDE code for validation failure.
* @param qstate: qstate with region.
* @return security status.
* SECURE: proved absence of DS.
enum sec_status val_nsec_prove_nodata_dsreply(struct module_env* env,
struct val_env* ve, struct query_info* qinfo,
struct reply_info* rep, struct key_entry_key* kkey,
- time_t* proof_ttl, char** reason, struct module_qstate* qstate);
+ time_t* proof_ttl, char** reason, sldns_ede_code* reason_bogus,
+ struct module_qstate* qstate);
/**
* nsec typemap check, takes an NSEC-type bitmap as argument, checks for type.
#include "util/data/msgparse.h"
#include "util/data/dname.h"
#include "util/rbtree.h"
+#include "util/rfc_1982.h"
#include "util/module.h"
#include "util/net_help.h"
#include "util/regional.h"
}
verbose(VERB_ALGO, "rrset failed to verify: all signatures are bogus");
if(!numchecked) {
- *reason = "signature missing";
+ *reason = "signature for expected key and algorithm missing";
if(reason_bogus)
- *reason_bogus = LDNS_EDE_RRSIGS_MISSING;
+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
} else if(numchecked == numindeterminate) {
verbose(VERB_ALGO, "rrset failed to verify due to algorithm "
"refusal by cryptolib");
(unsigned)incep, (unsigned)now);
}
-/** RFC 1982 comparison, uses unsigned integers, and tries to avoid
- * compiler optimization (eg. by avoiding a-b<0 comparisons),
- * this routine matches compare_serial(), for SOA serial number checks */
-static int
-compare_1982(uint32_t a, uint32_t b)
-{
- /* for 32 bit values */
- const uint32_t cutoff = ((uint32_t) 1 << (32 - 1));
-
- if (a == b) {
- return 0;
- } else if ((a < b && b - a < cutoff) || (a > b && a - b > cutoff)) {
- return -1;
- } else {
- return 1;
- }
-}
-
-/** if we know that b is larger than a, return the difference between them,
- * that is the distance between them. in RFC1982 arith */
-static uint32_t
-subtract_1982(uint32_t a, uint32_t b)
-{
- /* for 32 bit values */
- const uint32_t cutoff = ((uint32_t) 1 << (32 - 1));
-
- if(a == b)
- return 0;
- if(a < b && b - a < cutoff) {
- return b-a;
- }
- if(a > b && a - b > cutoff) {
- return ((uint32_t)0xffffffff) - (a-b-1);
- }
- /* wrong case, b smaller than a */
- return 0;
-}
-
/** check rrsig dates */
static int
check_dates(struct val_env* ve, uint32_t unow, uint8_t* expi_p,
return key_entry_create_rrset(region,
ds_rrset->rk.dname, ds_rrset->rk.dname_len,
ntohs(ds_rrset->rk.rrset_class), dnskey_rrset,
- downprot?sigalg:NULL, *env->now);
+ downprot?sigalg:NULL, LDNS_EDE_NONE, NULL,
+ *env->now);
} else if(sec == sec_status_insecure) {
return key_entry_create_null(region, ds_rrset->rk.dname,
- ds_rrset->rk.dname_len,
+ ds_rrset->rk.dname_len,
ntohs(ds_rrset->rk.rrset_class),
- rrset_get_ttl(ds_rrset), *env->now);
+ rrset_get_ttl(ds_rrset), *reason_bogus, *reason,
+ *env->now);
}
return key_entry_create_bad(region, ds_rrset->rk.dname,
ds_rrset->rk.dname_len, ntohs(ds_rrset->rk.rrset_class),
- BOGUS_KEY_TTL, *env->now);
+ BOGUS_KEY_TTL, *reason_bogus, *reason, *env->now);
}
enum sec_status
has_useful_ta = 1;
sec = dnskey_verify_rrset(env, ve, dnskey_rrset,
- ta_dnskey, i, reason, NULL, LDNS_SECTION_ANSWER, qstate);
+ ta_dnskey, i, reason, reason_bogus, LDNS_SECTION_ANSWER, qstate);
if(sec == sec_status_secure) {
if(!sigalg || algo_needs_set_secure(&needs,
(uint8_t)dnskey_get_algo(ta_dnskey, i))) {
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);
+ downprot?sigalg:NULL, LDNS_EDE_NONE, NULL, *env->now);
} else if(sec == sec_status_insecure) {
return key_entry_create_null(region, dnskey_rrset->rk.dname,
dnskey_rrset->rk.dname_len,
ntohs(dnskey_rrset->rk.rrset_class),
- rrset_get_ttl(dnskey_rrset), *env->now);
+ rrset_get_ttl(dnskey_rrset), *reason_bogus, *reason,
+ *env->now);
}
return key_entry_create_bad(region, dnskey_rrset->rk.dname,
dnskey_rrset->rk.dname_len, ntohs(dnskey_rrset->rk.rrset_class),
- BOGUS_KEY_TTL, *env->now);
+ BOGUS_KEY_TTL, *reason_bogus, *reason, *env->now);
}
int
struct query_info* qinfo, struct sock_list* origin);
-/* Updates the suplied EDE (RFC8914) code selectively so we don't loose
- * a more specific code
- */
+/* Updates the suplied EDE (RFC8914) code selectively so we don't lose
+ * 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;
- }
+ if(reason_bogus == LDNS_EDE_NONE) return;
+ if(reason_bogus == LDNS_EDE_DNSSEC_BOGUS
+ && rep->reason_bogus != LDNS_EDE_NONE
+ && rep->reason_bogus != LDNS_EDE_DNSSEC_BOGUS) return;
+ rep->reason_bogus = reason_bogus;
}
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);
- }
-
+ /* Bad keys should have the relevant EDE code and text */
+ sldns_ede_code 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_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));
- }
+ 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_insecure;
val_mark_insecure(vq->chase_reply, vq->key_entry->name,
qstate->env->rrset_cache, qstate->env);
- key_cache_insert(ve->kcache, vq->key_entry, qstate);
+ key_cache_insert(ve->kcache, vq->key_entry,
+ qstate->env->cfg->val_log_level >= 2);
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;
-
- update_reason_bogus(vq->chase_reply, LDNS_EDE_DNSKEY_MISSING);
+ update_reason_bogus(vq->chase_reply,
+ key_entry_get_reason_bogus(vq->key_entry));
errinf_ede(qstate, "while building chain of trust",
- LDNS_EDE_DNSKEY_MISSING);
+ key_entry_get_reason_bogus(vq->key_entry));
if(vq->restart_count >= ve->max_restart)
- key_cache_insert(ve->kcache, vq->key_entry, qstate);
+ key_cache_insert(ve->kcache, vq->key_entry,
+ qstate->env->cfg->val_log_level >= 2);
return 1;
}
log_query_info(NO_VERBOSE, "validation failure",
&qstate->qinfo);
else {
- char* err = errinf_to_str_bogus(qstate);
- if(err) log_info("%s", err);
- free(err);
+ char* err_str = errinf_to_str_bogus(qstate);
+ if(err_str) {
+ size_t err_str_len = strlen(err_str);
+ log_info("%s", err_str);
+ /* allocate space and store the error
+ * string */
+ vq->orig_msg->rep->reason_bogus_str = regional_alloc(
+ qstate->region,
+ sizeof(char) * (err_str_len+1));
+ memcpy(vq->orig_msg->rep->reason_bogus_str,
+ err_str, err_str_len+1);
+ }
+ free(err_str);
}
}
/*
}
}
}
+
+ /* Update rep->reason_bogus as it is the one being cached */
+ update_reason_bogus(vq->orig_msg->rep, errinf_to_reason_bogus(qstate));
/* store results in cache */
if(qstate->query_flags&BIT_RD) {
/* if secure, this will override cache anyway, no need
log_nametypeclass(VERB_OPS, "failed to prime trust anchor -- "
"could not fetch DNSKEY rrset",
ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass);
+ reason_bogus = LDNS_EDE_DNSKEY_MISSING;
+ reason = "no DNSKEY rrset";
if(qstate->env->cfg->harden_dnssec_stripped) {
- errinf_ede(qstate, "no DNSKEY rrset", LDNS_EDE_DNSKEY_MISSING);
+ errinf_ede(qstate, reason, reason_bogus);
kkey = key_entry_create_bad(qstate->region, ta->name,
ta->namelen, ta->dclass, BOGUS_KEY_TTL,
+ reason_bogus, reason,
*qstate->env->now);
} else kkey = key_entry_create_null(qstate->region, ta->name,
ta->namelen, ta->dclass, NULL_KEY_TTL,
+ reason_bogus, reason,
*qstate->env->now);
if(!kkey) {
log_err("out of memory: allocate fail prime key");
errinf_ede(qstate, reason, reason_bogus);
kkey = key_entry_create_bad(qstate->region, ta->name,
ta->namelen, ta->dclass, BOGUS_KEY_TTL,
+ reason_bogus, reason,
*qstate->env->now);
} else kkey = key_entry_create_null(qstate->region, ta->name,
ta->namelen, ta->dclass, NULL_KEY_TTL,
+ reason_bogus, reason,
*qstate->env->now);
if(!kkey) {
log_err("out of memory: allocate null prime key");
/* errors here pretty much break validation */
verbose(VERB_DETAIL, "DS response was error, thus bogus");
errinf(qstate, rc);
- errinf_ede(qstate, "no DS", LDNS_EDE_NETWORK_ERROR);
-
+ reason = "no DS";
+ reason_bogus = LDNS_EDE_NETWORK_ERROR;
+ errinf_ede(qstate, reason, reason_bogus);
goto return_bogus;
}
if(!ds) {
log_warn("internal error: POSITIVE DS response was "
"missing DS.");
- errinf_ede(qstate, "no DS record", LDNS_EDE_DNSSEC_BOGUS);
+ reason = "no DS record";
+ errinf_ede(qstate, reason, reason_bogus);
goto return_bogus;
}
/* Verify only returns BOGUS or SECURE. If the rrset is
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; this needs
- * EDE to be added on non SERVFAIL answers. */
-
- *ke = key_entry_create_null(qstate->region,
- qinfo->qname, qinfo->qname_len, qinfo->qclass,
- ub_packed_rrset_ttl(ds), *qstate->env->now);
+ *ke = key_entry_create_null(qstate->region,
+ qinfo->qname, qinfo->qname_len, qinfo->qclass,
+ ub_packed_rrset_ttl(ds),
+ LDNS_EDE_UNSUPPORTED_DS_DIGEST, NULL,
+ *qstate->env->now);
return (*ke) != NULL;
}
log_query_info(VERB_DETAIL, "validated DS", qinfo);
*ke = key_entry_create_rrset(qstate->region,
qinfo->qname, qinfo->qname_len, qinfo->qclass, ds,
- NULL, *qstate->env->now);
+ NULL, LDNS_EDE_NONE, NULL, *qstate->env->now);
return (*ke) != NULL;
} else if(subtype == VAL_CLASS_NODATA ||
subtype == VAL_CLASS_NAMEERROR) {
/* 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_ede(qstate, reason, LDNS_EDE_NSEC_MISSING);
+ reason_bogus = LDNS_EDE_NSEC_MISSING;
+ errinf_ede(qstate, reason, reason_bogus);
goto return_bogus;
}
/* Try to prove absence of the DS with NSEC */
sec = val_nsec_prove_nodata_dsreply(
qstate->env, ve, qinfo, msg->rep, vq->key_entry,
- &proof_ttl, &reason, qstate);
+ &proof_ttl, &reason, &reason_bogus, qstate);
switch(sec) {
case sec_status_secure:
verbose(VERB_DETAIL, "NSEC RRset for the "
*ke = key_entry_create_null(qstate->region,
qinfo->qname, qinfo->qname_len,
qinfo->qclass, proof_ttl,
+ LDNS_EDE_NONE, NULL,
*qstate->env->now);
return (*ke) != NULL;
case sec_status_insecure:
*ke = key_entry_create_null(qstate->region,
qinfo->qname, qinfo->qname_len,
qinfo->qclass, proof_ttl,
+ LDNS_EDE_NONE, NULL,
*qstate->env->now);
return (*ke) != NULL;
case sec_status_indeterminate:
* this is BOGUS. */
verbose(VERB_DETAIL, "DS %s ran out of options, so return "
"bogus", val_classification_to_string(subtype));
- errinf(qstate, "no DS but also no proof of that");
+ reason = "no DS but also no proof of that";
+ errinf_ede(qstate, reason, reason_bogus);
goto return_bogus;
} else if(subtype == VAL_CLASS_CNAME ||
subtype == VAL_CLASS_CNAMENOANSWER) {
cname = reply_find_rrset_section_an(msg->rep, qinfo->qname,
qinfo->qname_len, LDNS_RR_TYPE_CNAME, qinfo->qclass);
if(!cname) {
- errinf(qstate, "validator classified CNAME but no "
- "CNAME of the queried name for DS");
+ reason = "validator classified CNAME but no "
+ "CNAME of the queried name for DS";
+ errinf_ede(qstate, reason, reason_bogus);
goto return_bogus;
}
if(((struct packed_rrset_data*)cname->entry.data)->rrsig_count
== 0) {
if(msg->rep->an_numrrsets != 0 && ntohs(msg->rep->
rrsets[0]->rk.type)==LDNS_RR_TYPE_DNAME) {
- errinf(qstate, "DS got DNAME answer");
+ reason = "DS got DNAME answer";
} else {
- errinf(qstate, "DS got unsigned CNAME answer");
+ reason = "DS got unsigned CNAME answer";
}
+ errinf_ede(qstate, reason, reason_bogus);
goto return_bogus;
}
- sec = val_verify_rrset_entry(qstate->env, ve, cname,
- vq->key_entry, &reason, NULL, LDNS_SECTION_ANSWER, qstate);
+ sec = val_verify_rrset_entry(qstate->env, ve, cname,
+ vq->key_entry, &reason, &reason_bogus,
+ LDNS_SECTION_ANSWER, qstate);
if(sec == sec_status_secure) {
verbose(VERB_ALGO, "CNAME validated, "
"proof that DS does not exist");
return 1;
}
errinf(qstate, "CNAME in DS response was not secure.");
- errinf(qstate, reason);
+ errinf_ede(qstate, reason, reason_bogus);
goto return_bogus;
} else {
verbose(VERB_QUERY, "Encountered an unhandled type of "
"DS response, thus bogus.");
errinf(qstate, "no DS and");
+ reason = "no DS";
if(FLAGS_GET_RCODE(msg->rep->flags) != LDNS_RCODE_NOERROR) {
char rc[16];
rc[0]=0;
}
return_bogus:
*ke = key_entry_create_bad(qstate->region, qinfo->qname,
- qinfo->qname_len, qinfo->qclass,
- BOGUS_KEY_TTL, *qstate->env->now);
+ qinfo->qname_len, qinfo->qclass, BOGUS_KEY_TTL,
+ reason_bogus, reason, *qstate->env->now);
return (*ke) != NULL;
}
vq->restart_count++;
return;
}
- vq->key_entry = key_entry_create_bad(qstate->region,
+ reason = "No DNSKEY record";
+ reason_bogus = LDNS_EDE_DNSKEY_MISSING;
+ vq->key_entry = key_entry_create_bad(qstate->region,
qinfo->qname, qinfo->qname_len, qinfo->qclass,
- BOGUS_KEY_TTL, *qstate->env->now);
+ BOGUS_KEY_TTL, reason_bogus, reason,
+ *qstate->env->now);
if(!vq->key_entry) {
log_err("alloc failure in missing dnskey response");
/* key_entry is NULL for failure in Validate */
}
- errinf_ede(qstate, "No DNSKEY record", LDNS_EDE_DNSKEY_MISSING);
+ errinf_ede(qstate, reason, reason_bogus);
errinf_origin(qstate, origin);
errinf_dname(qstate, "for key", qinfo->qname);
vq->state = VAL_VALIDATE_STATE;
qstate->errinf = NULL;
/* The DNSKEY validated, so cache it as a trusted key rrset. */
- key_cache_insert(ve->kcache, vq->key_entry, qstate);
+ key_cache_insert(ve->kcache, vq->key_entry,
+ qstate->env->cfg->val_log_level >= 2);
/* If good, we stay in the FINDKEY state. */
log_query_info(VERB_DETAIL, "validated DNSKEY", qinfo);
errinf_origin(qstate, origin);
errinf_dname(qstate, "for trust anchor", ta->name);
/* store the freshly primed entry in the cache */
- key_cache_insert(ve->kcache, vq->key_entry, qstate);
+ key_cache_insert(ve->kcache, vq->key_entry,
+ qstate->env->cfg->val_log_level >= 2);
}
/* If the result of the prime is a null key, skip the FINDKEY state.*/